index.spec.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  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, jasmine:true */
  9. 'use strict';
  10. var secureRandom = require('../lib/index');
  11. var constants = require('../lib/constants');
  12. var codec = require('scytl-codec');
  13. var forge = require('node-forge');
  14. var BigInteger = forge.jsbn.BigInteger;
  15. describe('The secure random module should be able to ...', function() {
  16. var TEST_SEED = new Uint8Array([18, 58, 188]);
  17. var PRIVATE_KEY_PEM =
  18. '-----BEGIN RSA PRIVATE KEY-----MIIEowIBAAKCAQEAglypZU45bnf2opnRI+Y51VqouBpvDT33xIB/OtbwKzwVpi+JrjBFtfk33tE9t/dSRs79CK94HRhCWcOiLa2qWPrjeZ9SBiEyScrhIvRZVBF41zBgwQNuRvJCsKmAqlZaFNJDZxEP4repmlBn1CfVFmfrXmOKqwP5F7l9ZtucveRzsfmF1yVPFkW8TMuB3YqMiyymyqHlS8ujCsu5I8tpgPbwuxdMOY94fNhSXrYkY8IuX1g1zdq/Z1jluOaR/UqK4UpnbuJaH/F0VgDNiWh6cTD0DFGEk0b70i5wU4Q3L/S6XZQRvSuADoCbhwBKuFL5pW5n865oLVb5S3wuVdWaGwIDAQABAoIBAC/tn34Wf3kE9BGeGc1oFLVDaqqdVVz5/oEpeR2J7q0GnzMFYUpAhzC7WvY52cYsUPyll1Q9Jx0TUTmteo/uvKWQQFfz4nVMeS+2PoXabolBDzuWlsv/1eiRo0FOYHa/3siu8YcQN9X0DpAkpbfTmT1uoZOHZ3EuucMmOFu7vGn38Grw8bSxpR0uvTtnb8ygC+aB51y38RMyhzQQanrM8FMeAfDAy6IB0Yo7b0c50Cxa6Ax4nqn9LXyGakr5WeAMkgTIOA/GId9SZD4e5eRpq+628pOeR4O9datFltgl6r1+A4ii2VrJsDqeatGtODlX6KRKqwFHoGIa2TjgSZLuorECgYEAxeSZDOOgFsI5mB7RkRzZaQ9znJ15sgdyZiAFZAOUah4hSGdAXNAnZTlrdacduXEu3EfkpuPToX7xZSv5FRYwfBwMwCLeytlGLPjQzWejZGbo4+KqgzWb9fECDYVtDPlJ/+yLih9nt67BHweJKxYydl18rVigdVyy22X86NijSykCgYEAqKPUrXZAo+TJvmTw4tgsibJgvXBYBhmsej8mGNQw+Nyp2gV28sgm61ifIeXKS8teq+MFwGA6cHQedbsCqhMHokdhESZmlbWxhSFLihQcewBxwvrBwbaxI23yXRzwMewznZFL032PpcbqrmwFmcSSEZ3nmbvTH6ShqLW+pzDNp6MCgYBQLzdgxJ7qedqSa/JohTMG4e7rh9d2rpPJE7J7ewPZF8pOpx+qO+Gqn2COdJ+Ts2vUcAETKn9nEaPIZc/wnmQY9dioxbhWo0FPGaaphBPtq9Ez/XUv4zoFppk5V1X/isdUPsmvttf00oeIBiqrXbwmv+yz5JRn2Z7TTXjz9Ev+OQKBgQCUuoCMRzl1EgcXIqEL/0kwW6BUEqufHa9u1Ri9Vw6lvL8T6DPipMEmWK9nzuid9gtVns/ovTVtDgv7GuabplLaPQePf4WDzY11c0rSyS/hDyBFrK+LL5uEOqhAlJAGB2HyOj1clWVF+GvrTpuV5LZKUS/79pmZU7G7QCaX/0Ow7wKBgC/kDH7cmWQnWvvJ5izrx/7PogQVPOLELeUIGLu/hjsSdDKiFCxCUZ948+9NuG+DnpXDWzw//r8mPBRRGGsqFws5Aipp7yjQ3kRDCCzGelPCVhHyfmKqA+8ewXPulKS3/wIyHIvaXmsuAtTfurHtpRyzjKmCBK1Y6WQ3trIXvo7s-----END RSA PRIVATE KEY-----';
  19. var PIN_ENTROPY = 100;
  20. var service_;
  21. var randomGenerator_;
  22. beforeEach(function() {
  23. service_ = secureRandom.newService();
  24. randomGenerator_ = service_.newRandomGenerator();
  25. });
  26. describe('create a secure random service that should be able to ..', function() {
  27. describe(
  28. 'create a secure random generator that should be able to', function() {
  29. it('generate 10 random bytes', function() {
  30. var numBytes = 10;
  31. var bytes = randomGenerator_.nextBytes(numBytes);
  32. expect(bytes).toBeDefined();
  33. expect(bytes.length).not.toBeGreaterThan(numBytes);
  34. });
  35. it('generate a specified number of random bytes', function() {
  36. var numBytes = 16;
  37. var bytes = randomGenerator_.nextBytes(numBytes);
  38. expect(bytes).toBeDefined();
  39. expect(bytes.numBytes).not.toBeGreaterThan(numBytes);
  40. });
  41. it('generate a random BigInteger of a specified maximum bit length',
  42. function() {
  43. for (var maxNumBits = 1; maxNumBits <= 20; maxNumBits++) {
  44. var randomBigInteger =
  45. randomGenerator_.nextBigInteger(maxNumBits);
  46. expect(randomBigInteger).not.toBeLessThan(0);
  47. expect(randomBigInteger.bitLength())
  48. .not.toBeGreaterThan(maxNumBits);
  49. }
  50. });
  51. it('generate a random BigInteger of a specified number of digits',
  52. function() {
  53. var maxValue;
  54. for (var numDigits = 1; numDigits <= 10; numDigits++) {
  55. maxValue = Math.pow(10, numDigits);
  56. var randomBigInteger =
  57. randomGenerator_.nextBigIntegerByDigits(numDigits);
  58. expect(randomBigInteger.compareTo(BigInteger.ZERO))
  59. .toBeGreaterThan(0);
  60. expect(randomBigInteger.compareTo(
  61. new BigInteger(maxValue.toString())))
  62. .toBeLessThan(0);
  63. }
  64. });
  65. it('be created by specifying a maximum allowed number of bytes',
  66. function() {
  67. var maxNumBytes = constants.SECURE_RANDOM_MAX_NUM_BYTES + 1;
  68. var randomGenerator_ =
  69. service_.newRandomGenerator({maxNumBytes: maxNumBytes});
  70. var randomBytes = randomGenerator_.nextBytes(maxNumBytes);
  71. expect(randomBytes).toBeDefined();
  72. expect(randomBytes.length).not.toBeGreaterThan(maxNumBytes);
  73. expect(function() {
  74. randomGenerator_.nextBytes(maxNumBytes + 1);
  75. }).toThrow();
  76. });
  77. it('be created by specifying maximum allowed number of digits',
  78. function() {
  79. var maxNumDigits = constants.SECURE_RANDOM_MAX_NUM_BYTES + 1;
  80. var randomGenerator_ =
  81. service_.newRandomGenerator({maxNumDigits: maxNumDigits});
  82. var randomBigInteger =
  83. randomGenerator_.nextBigIntegerByDigits(maxNumDigits);
  84. expect(randomBigInteger).toBeDefined();
  85. expect(randomBigInteger.compareTo(BigInteger.ZERO))
  86. .toBeGreaterThan(0);
  87. expect(randomBigInteger.compareTo(
  88. new BigInteger('10').pow(maxNumDigits)))
  89. .toBeLessThan(0);
  90. expect(function() {
  91. randomGenerator_.nextBigIntegerByDigits(maxNumDigits + 1);
  92. }).toThrow();
  93. });
  94. it('throw an exception when a negative input argument is passed',
  95. function() {
  96. expect(function() {
  97. randomGenerator_.nextBytes(-1);
  98. }).toThrow();
  99. expect(function() {
  100. randomGenerator_.nextBigInteger(-1);
  101. }).toThrow();
  102. expect(function() {
  103. randomGenerator_.nextBigIntegerByDigits(-1);
  104. }).toThrow();
  105. });
  106. it('throw an exception when a non-numeric input argument is passed',
  107. function() {
  108. expect(function() {
  109. randomGenerator_.nextBytes('abc');
  110. }).toThrow();
  111. expect(function() {
  112. randomGenerator_.nextBigInteger('abc');
  113. }).toThrow();
  114. expect(function() {
  115. randomGenerator_.nextBigIntegerByDigits('abc');
  116. }).toThrow();
  117. });
  118. it('throw an exception when a too-large input argument is passed',
  119. function() {
  120. expect(function() {
  121. randomGenerator_.nextBytes(
  122. constants.SECURE_RANDOM_MAX_NUM_BYTES + 1);
  123. }).toThrow();
  124. expect(function() {
  125. randomGenerator_.nextBigIntegerByDigits(
  126. constants.SECURE_RANDOM_MAX_NUM_DIGITS + 1);
  127. }).toThrow();
  128. });
  129. });
  130. it('initialize its PRNG from a provided seed', function() {
  131. var service = secureRandom.newService(
  132. {prngSeed: TEST_SEED, noDefaultCollectors: true});
  133. var randomGenerator = service.newRandomGenerator();
  134. expect(randomGenerator.nextBigIntegerByDigits(2).toString())
  135. .toEqual('21');
  136. expect(randomGenerator.nextBigIntegerByDigits(4).toString())
  137. .toEqual('2950');
  138. expect(randomGenerator.nextBigIntegerByDigits(16).toString())
  139. .toEqual('5574593524320719');
  140. });
  141. it('generate a random seed to instantiate other secure random services',
  142. function() {
  143. var seed = service_.nextSeed();
  144. expect(seed.length)
  145. .toBe(constants.SECURE_RANDOM_DEFAULT_NUM_SEED_BYTES);
  146. seed = service_.nextSeed(16);
  147. expect(seed.length).toBe(16);
  148. var service1 = secureRandom.newService(
  149. {prngSeed: seed, noDefaultCollectors: true});
  150. var randomGenerator1 = service1.newRandomGenerator();
  151. var randomInt1 = randomGenerator1.nextBigIntegerByDigits(2);
  152. var service2 = secureRandom.newService(
  153. {prngSeed: seed, noDefaultCollectors: true});
  154. var randomGenerator2 = service2.newRandomGenerator();
  155. var randomInt2 = randomGenerator2.nextBigIntegerByDigits(2);
  156. expect(randomInt1).toEqual(randomInt2);
  157. });
  158. it('create two different secure random generators that use the same PRNG',
  159. function() {
  160. var service = secureRandom.newService(
  161. {prngSeed: TEST_SEED, noDefaultCollectors: true});
  162. var randomGenerator1 = service.newRandomGenerator();
  163. var randomGenerator2 = service.newRandomGenerator();
  164. expect(randomGenerator1.nextBigIntegerByDigits(2).toString())
  165. .toEqual('21');
  166. expect(randomGenerator1.nextBigIntegerByDigits(4).toString())
  167. .toEqual('2950');
  168. expect(randomGenerator1.nextBigIntegerByDigits(16).toString())
  169. .toEqual('5574593524320719');
  170. expect(randomGenerator2.nextBigIntegerByDigits(2).toString())
  171. .toEqual('12');
  172. expect(randomGenerator2.nextBigIntegerByDigits(4).toString())
  173. .toEqual('6576');
  174. expect(randomGenerator2.nextBigIntegerByDigits(16).toString())
  175. .toEqual('2672997009079990');
  176. });
  177. it('start and stop the entropy collection manually', function() {
  178. var callbackCalled = false;
  179. var callback = function() {
  180. callbackCalled = true;
  181. };
  182. service_.startEntropyCollection(callback);
  183. var maxEntropyCollected = service_.stopEntropyCollection();
  184. expect(callbackCalled).toBeTruthy();
  185. expect(typeof maxEntropyCollected).toBe('boolean');
  186. });
  187. it('measure the entropy collected as a percentage of the maximum entropy required',
  188. function() {
  189. var service = secureRandom.newService({noDefaultCollectors: true});
  190. service.startEntropyCollection();
  191. service.stopEntropyCollection();
  192. expect(service.getEntropyPercentage()).toBe(0);
  193. service = secureRandom.newService();
  194. service.startEntropyCollection();
  195. service.stopEntropyCollection();
  196. expect(service.getEntropyPercentage()).toBeGreaterThan(0);
  197. expect(service.getEntropyPercentage()).toBeLessThan(101);
  198. });
  199. it('collect some entropy with the maximum entropy counter option set',
  200. function() {
  201. var service = secureRandom.newService({maxEntropyCounter: 150});
  202. expect(service.getEntropyPercentage()).toBeGreaterThan(0);
  203. expect(service.getEntropyPercentage()).toBeLessThan(101);
  204. });
  205. it('collect some entropy with the hash updater interval option set',
  206. function() {
  207. var service = secureRandom.newService({hashUpdaterInterval: 500});
  208. expect(service.getEntropyPercentage()).toBeGreaterThan(0);
  209. expect(service.getEntropyPercentage()).toBeLessThan(101);
  210. });
  211. it('collect some entropy using private key data', function() {
  212. var privateKeyData = {
  213. privateKey: PRIVATE_KEY_PEM,
  214. pinEntropy: PIN_ENTROPY
  215. };
  216. var service = secureRandom.newService(
  217. {noDefaultCollectors: true, privateKeyData: privateKeyData});
  218. expect(service.getEntropyPercentage()).toBeGreaterThan(0);
  219. expect(service.getEntropyPercentage()).toBeLessThan(101);
  220. });
  221. it('generate bytes with the Forge-compliant bytes generation method',
  222. function() {
  223. var service = secureRandom.newService(
  224. {prngSeed: TEST_SEED, noDefaultCollectors: true});
  225. var randomGenerator = service.newRandomGenerator();
  226. expect(codec.base64Encode(
  227. codec.binaryDecode(randomGenerator.getBytesSync(2))))
  228. .toEqual('k5U=');
  229. expect(codec.base64Encode(
  230. codec.binaryDecode(randomGenerator.getBytesSync(4))))
  231. .toEqual('FQJ+/Q==');
  232. expect(codec.base64Encode(
  233. codec.binaryDecode(randomGenerator.getBytesSync(6))))
  234. .toEqual('o9f6kc4U');
  235. });
  236. });
  237. });