index.spec.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  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 CommonTestData = require('./data/common-data');
  11. var elGamal = require('../lib/index');
  12. var mathematical = require('scytl-mathematical');
  13. var secureRandom = require('scytl-securerandom');
  14. describe('The ElGamal cryptography module should be able to ...', function() {
  15. var elGamalService_;
  16. var secureRandomService_;
  17. var mathService_;
  18. var mathRandomGenerator_;
  19. var group_;
  20. var publicKey_;
  21. var privateKey_;
  22. var publicKeyElements_;
  23. var privateKeyExponents_;
  24. var keyPair_;
  25. var elements_;
  26. var elementValueStrings_;
  27. var largePublicKey_;
  28. var largePrivateKey_;
  29. var elementsFromLargeGroup_;
  30. var elementsFromLargeNonQrGroup_;
  31. var identityElements_;
  32. var shortExponentBitLength_;
  33. var singleElements_;
  34. var elGamalEncrypter_;
  35. var elGamalDecrypter_;
  36. var encryptedElements_;
  37. var elGamalEncrypterLargeGroup_;
  38. var elGamalDecrypterLargeGroup_;
  39. var prngSeed_;
  40. var encrypterRandomGenerator_;
  41. var decrypterRandomGenerator_;
  42. beforeAll(function() {
  43. elGamalService_ = elGamal.newService();
  44. secureRandomService_ = secureRandom.newService();
  45. mathService_ =
  46. mathematical.newService({secureRandomService: secureRandomService_});
  47. mathRandomGenerator_ = mathService_.newRandomGenerator();
  48. var testData = new CommonTestData();
  49. group_ = testData.getGroup();
  50. publicKey_ = testData.getPublicKey();
  51. privateKey_ = testData.getPrivateKey();
  52. publicKeyElements_ = testData.getPublicKeyElements();
  53. privateKeyExponents_ = testData.getPrivateKeyExponents();
  54. keyPair_ = testData.getKeyPair();
  55. elements_ = testData.getZpGroupElements();
  56. elementValueStrings_ = testData.getZpGroupElementValueStrings();
  57. largePublicKey_ = testData.getLargePublicKey();
  58. largePrivateKey_ = testData.getLargePrivateKey();
  59. elementsFromLargeGroup_ = testData.getElementsFromLargeZpSubgroup();
  60. elementsFromLargeNonQrGroup_ =
  61. testData.getElementsFromLargeNonQrZpSubgroup();
  62. identityElements_ = testData.getIdentityElements();
  63. singleElements_ = testData.getSingleElements();
  64. shortExponentBitLength_ = testData.getShortExponentBitLength();
  65. elGamalEncrypter_ = elGamalService_.newEncrypter().init(publicKey_);
  66. elGamalDecrypter_ = elGamalService_.newDecrypter().init(privateKey_);
  67. encryptedElements_ = elGamalEncrypter_.encrypt(elements_);
  68. elGamalEncrypterLargeGroup_ =
  69. elGamalService_.newEncrypter().init(largePublicKey_);
  70. elGamalDecrypterLargeGroup_ =
  71. elGamalService_.newDecrypter().init(largePrivateKey_);
  72. prngSeed_ = secureRandomService_.nextSeed();
  73. });
  74. beforeEach(function() {
  75. encrypterRandomGenerator_ =
  76. secureRandom.newService({prngSeed: prngSeed_}).newRandomGenerator();
  77. decrypterRandomGenerator_ =
  78. secureRandom.newService({prngSeed: prngSeed_}).newRandomGenerator();
  79. });
  80. describe('create an ElGamal cryptography service that should be able to ..', function() {
  81. it('create a new ElGamal key pair', function() {
  82. validatePublicKey(keyPair_.publicKey, group_);
  83. validatePrivateKey(keyPair_.privateKey, group_);
  84. });
  85. it('create a new ElGamalEncryptedElements object', function() {
  86. var encryptedElements = elGamalEncrypter_.encrypt(elements_);
  87. var gamma = encryptedElements.gamma;
  88. var phis = encryptedElements.phis;
  89. var newEncryptedElements =
  90. elGamalService_.newEncryptedElements(gamma, phis);
  91. var decryptedElements = elGamalDecrypter_.decrypt(newEncryptedElements);
  92. checkDecryptedElements(decryptedElements, elements_);
  93. });
  94. it('create a new ElGamalEncryptedElements object, with a saved secret',
  95. function() {
  96. var encryptedElementsAndSecret =
  97. elGamalEncrypter_.encrypt(elements_, {saveSecret: true});
  98. var gamma = encryptedElementsAndSecret.gamma;
  99. var phis = encryptedElementsAndSecret.phis;
  100. var secret = encryptedElementsAndSecret.secret;
  101. var newEncryptedElementsAndSecret =
  102. elGamalService_.newEncryptedElements(gamma, phis, secret);
  103. expect((newEncryptedElementsAndSecret.secret.equals(secret)))
  104. .toBeTruthy();
  105. var encryptedElements = elGamalService_.newEncryptedElements(
  106. newEncryptedElementsAndSecret.gamma,
  107. newEncryptedElementsAndSecret.phis);
  108. var decryptedElements = elGamalDecrypter_.decrypt(encryptedElements);
  109. checkDecryptedElements(decryptedElements, elements_);
  110. });
  111. it('serialize and deserialize an ElGamal public key', function() {
  112. var publicKeyJson = publicKey_.toJson();
  113. var publicKeyFromJson = elGamalService_.newPublicKey(publicKeyJson);
  114. validatePublicKey(publicKeyFromJson, group_);
  115. });
  116. it('serialize and deserialize an ElGamal private key', function() {
  117. var privateKeyJson = privateKey_.toJson();
  118. var privateKeyFromJson = elGamalService_.newPrivateKey(privateKeyJson);
  119. validatePrivateKey(privateKeyFromJson, group_);
  120. });
  121. it('serialize and deserialize ElGamal encrypted elements', function() {
  122. var encryptedElements = elGamalEncrypter_.encrypt(elements_);
  123. var encryptedElementsJson = encryptedElements.toJson();
  124. var encryptedElementsFromJson =
  125. elGamalService_.newEncryptedElements(encryptedElementsJson);
  126. var decryptedElements =
  127. elGamalDecrypter_.decrypt(encryptedElementsFromJson);
  128. checkDecryptedElements(decryptedElements, elements_);
  129. });
  130. it('serialize and deserialize an encryption pre-computation', function() {
  131. var preComputation = elGamalEncrypter_.preCompute();
  132. var preComputationJson = preComputation.toJson();
  133. var preComputationFromJson =
  134. elGamalService_.newEncryptedElements(preComputationJson);
  135. var encryptedElements = elGamalEncrypter_.encrypt(
  136. elements_, {preComputation: preComputationFromJson});
  137. var decryptedElements = elGamalDecrypter_.decrypt(encryptedElements);
  138. checkDecryptedElements(decryptedElements, elements_);
  139. });
  140. describe('create an encrypter/decrypter pair that should be able to ..', function() {
  141. it('encrypt and decrypt some group elements', function() {
  142. var decryptedElements = elGamalDecrypter_.decrypt(encryptedElements_);
  143. checkDecryptedElements(decryptedElements, elements_);
  144. });
  145. it('encrypt and decrypt some group elements, using a specified secure random service object',
  146. function() {
  147. var elGamalService =
  148. elGamal.newService({secureRandomService: secureRandomService_});
  149. var elGamalEncrypter =
  150. elGamalService.newEncrypter().init(publicKey_);
  151. var elGamalDecrypter =
  152. elGamalService.newDecrypter().init(privateKey_);
  153. var encryptedElements = elGamalEncrypter.encrypt(elements_);
  154. var decryptedElements = elGamalDecrypter.decrypt(encryptedElements);
  155. checkDecryptedElements(decryptedElements, elements_);
  156. });
  157. it('encrypt and decrypt some group elements, using a specified mathematical service object',
  158. function() {
  159. var elGamalService =
  160. elGamal.newService({mathematicalService: mathService_});
  161. var elGamalEncrypter =
  162. elGamalService.newEncrypter().init(publicKey_);
  163. var elGamalDecrypter =
  164. elGamalService.newDecrypter().init(privateKey_);
  165. var encryptedElements = elGamalEncrypter.encrypt(elements_);
  166. var decryptedElements = elGamalDecrypter.decrypt(encryptedElements);
  167. checkDecryptedElements(decryptedElements, elements_);
  168. });
  169. it('encrypt and decrypt some group element value strings', function() {
  170. var encryptedElements = elGamalEncrypter_.encrypt(elementValueStrings_);
  171. var decryptedElements = elGamalDecrypter_.decrypt(encryptedElements);
  172. checkDecryptedElements(decryptedElements, elements_);
  173. });
  174. it('decrypt some group elements, with the check group membership flag set to true',
  175. function() {
  176. var decryptedElements = elGamalDecrypter_.decrypt(
  177. encryptedElements_, {checkMembership: true});
  178. checkDecryptedElements(decryptedElements, elements_);
  179. });
  180. it('decrypt some group elements, with check group membership flag set to false',
  181. function() {
  182. var decryptedElements = elGamalDecrypter_.decrypt(
  183. encryptedElements_, {checkMembership: false});
  184. checkDecryptedElements(decryptedElements, elements_);
  185. });
  186. it('throw an error when decrypting some group elements, the check group membership flag is set to true and the check fails',
  187. function() {
  188. expect(function() {
  189. elGamalDecrypterLargeGroup_.decrypt(
  190. encryptedElements_, {checkMembership: true});
  191. }).toThrow();
  192. });
  193. it('encrypt and decrypt some group elements, using an encryption pre-computation',
  194. function() {
  195. var preComputation = elGamalEncrypter_.preCompute();
  196. var encryptedElements = elGamalEncrypter_.encrypt(
  197. elements_, {preComputation: preComputation});
  198. var decryptedElements = elGamalDecrypter_.decrypt(encryptedElements);
  199. checkDecryptedElements(decryptedElements, elements_);
  200. });
  201. it('encrypt and decrypt some group elements and retrieve the secret exponent',
  202. function() {
  203. var encryptedElementsAndSecret =
  204. elGamalEncrypter_.encrypt(elements_, {saveSecret: true});
  205. checkSecretExponent(
  206. publicKey_, encryptedElementsAndSecret, elements_);
  207. var encryptedElements = elGamalService_.newEncryptedElements(
  208. encryptedElementsAndSecret.gamma,
  209. encryptedElementsAndSecret.phis);
  210. var decryptedElements = elGamalDecrypter_.decrypt(encryptedElements);
  211. checkDecryptedElements(decryptedElements, elements_);
  212. });
  213. it('encrypt and decrypt some group elements, using an encryption pre-computation, and retrieve the secret exponent after pre-computing',
  214. function() {
  215. var preComputationAndSecret =
  216. elGamalEncrypter_.preCompute({saveSecret: true});
  217. checkSecretExponent(
  218. publicKey_, preComputationAndSecret, identityElements_);
  219. var preComputation = elGamalService_.newEncryptedElements(
  220. preComputationAndSecret.gamma, preComputationAndSecret.phis);
  221. var encryptedElements = elGamalEncrypter_.encrypt(
  222. elements_, {preComputation: preComputation});
  223. var decryptedElements = elGamalDecrypter_.decrypt(encryptedElements);
  224. checkDecryptedElements(decryptedElements, elements_);
  225. });
  226. it('encrypt and decrypt some group elements, using encrypted elements generated with a short exponent',
  227. function() {
  228. var encryptedElementsAndSecret = elGamalEncrypterLargeGroup_.encrypt(
  229. elementsFromLargeGroup_,
  230. {useShortExponent: true, saveSecret: true});
  231. expect(encryptedElementsAndSecret.secret.value.bitLength())
  232. .not.toBeGreaterThan(shortExponentBitLength_);
  233. var encryptedElements = elGamalService_.newEncryptedElements(
  234. encryptedElementsAndSecret.gamma,
  235. encryptedElementsAndSecret.phis);
  236. var decryptedElements =
  237. elGamalDecrypterLargeGroup_.decrypt(encryptedElements);
  238. checkDecryptedElements(decryptedElements, elementsFromLargeGroup_);
  239. });
  240. it('encrypt and decrypt some group elements, using an encryption pre-computation generated with a short exponent',
  241. function() {
  242. var preComputationAndSecret = elGamalEncrypterLargeGroup_.preCompute(
  243. {useShortExponent: true, saveSecret: true});
  244. expect(preComputationAndSecret.secret.value.bitLength())
  245. .not.toBeGreaterThan(shortExponentBitLength_);
  246. var preComputation = elGamalService_.newEncryptedElements(
  247. preComputationAndSecret.gamma, preComputationAndSecret.phis);
  248. var encryptedElements = elGamalEncrypterLargeGroup_.encrypt(
  249. elementsFromLargeGroup_, {preComputation: preComputation});
  250. var decryptedElements =
  251. elGamalDecrypterLargeGroup_.decrypt(encryptedElements);
  252. checkDecryptedElements(decryptedElements, elementsFromLargeGroup_);
  253. });
  254. it('encrypt and decrypt some group elements, when the public key and private key undergo compression',
  255. function() {
  256. var encryptedElements = elGamalEncrypter_.encrypt([elements_[0]]);
  257. var decryptedElements = elGamalDecrypter_.decrypt(encryptedElements);
  258. checkDecryptedElements(decryptedElements, [elements_[0]]);
  259. });
  260. it('throw an error when encrypting before the encrypter has been initialized with a public key',
  261. function() {
  262. var encrypter = elGamalService_.newEncrypter();
  263. expect(function() {
  264. encrypter.encrypt(elements_);
  265. }).toThrow();
  266. });
  267. it('throw an error when decrypting before the decrypter has been initialized with a private key',
  268. function() {
  269. var decrypter = elGamalService_.newDecrypter();
  270. expect(function() {
  271. decrypter.decrypt(encryptedElements_);
  272. }).toThrow();
  273. });
  274. it('throw an error when decrypting before the PRNG decrypter has been initialized with a public key',
  275. function() {
  276. var prngDecrypter = elGamalService_.newPrngDecrypter();
  277. expect(function() {
  278. prngDecrypter.decrypt(encryptedElements_);
  279. }).toThrow();
  280. });
  281. it('throw an error when attempting to decrypt non-group members and group membership is being checked',
  282. function() {
  283. expect(function() {
  284. var encryptedElements =
  285. elGamalEncrypterLargeGroup_.encrypt(elementsFromLargeGroup_);
  286. elGamalDecrypter_.decrypt(
  287. encryptedElements, {checkMembership: true});
  288. }).toThrow();
  289. });
  290. it('throw an error when attempting to ElGamal encrypt, using a short exponent, for a group that is not of type quadratic residue',
  291. function() {
  292. expect(function() {
  293. elGamalEncrypterLargeGroup_.encrypt(
  294. elementsFromLargeNonQrGroup_, {useShortExponent: true});
  295. }).toThrow();
  296. });
  297. it('throw an error when attempting to ElGamal encrypt, using a short exponent, for a group whose order is smaller than this exponent',
  298. function() {
  299. expect(function() {
  300. elGamalEncrypter_.encrypt(elements_, {useShortExponent: true});
  301. }).toThrow();
  302. });
  303. });
  304. describe(
  305. 'create an encrypter/PRNG decrypter pair that should be able to ..',
  306. function() {
  307. it('encrypt and decrypt some group elements', function() {
  308. var encrypterFromSeed = elGamalService_.newEncrypter().init(
  309. publicKey_, {secureRandomGenerator: encrypterRandomGenerator_});
  310. var encryptedElements = encrypterFromSeed.encrypt(elements_);
  311. var prngDecrypter = elGamalService_.newPrngDecrypter().init(
  312. publicKey_, decrypterRandomGenerator_);
  313. var decryptedElements = prngDecrypter.decrypt(encryptedElements);
  314. checkDecryptedElements(decryptedElements, elements_);
  315. });
  316. it('encrypt and decrypt some group elements, using an encryption pre-computation',
  317. function() {
  318. var encrypterFromSeed = elGamalService_.newEncrypter().init(
  319. publicKey_,
  320. {secureRandomGenerator: encrypterRandomGenerator_});
  321. var preComputation = encrypterFromSeed.preCompute();
  322. var encryptedElements = encrypterFromSeed.encrypt(
  323. elements_, {preComputation: preComputation});
  324. var prngDecrypter = elGamalService_.newPrngDecrypter().init(
  325. publicKey_, decrypterRandomGenerator_);
  326. var decryptedElements = prngDecrypter.decrypt(encryptedElements);
  327. checkDecryptedElements(decryptedElements, elements_);
  328. });
  329. it('encrypt some group elements and retrieve the secret exponent that will be used for decryption',
  330. function() {
  331. var encrypterFromSeed = elGamalService_.newEncrypter().init(
  332. publicKey_,
  333. {secureRandomGenerator: encrypterRandomGenerator_});
  334. var encryptedElementsAndSecret =
  335. encrypterFromSeed.encrypt(elements_, {saveSecret: true});
  336. var secret = encryptedElementsAndSecret.secret;
  337. var secretFromDecrypter = mathRandomGenerator_.nextExponent(
  338. group_, {secureRandomGenerator: decrypterRandomGenerator_});
  339. expect(secretFromDecrypter.equals(secret)).toBeTruthy();
  340. });
  341. it('encrypt some group elements, using pre-computation, and retrieve the secret exponent that will be used for decryption',
  342. function() {
  343. var encrypterFromSeed = elGamalService_.newEncrypter().init(
  344. publicKey_,
  345. {secureRandomGenerator: encrypterRandomGenerator_});
  346. var preComputationAndSecret =
  347. encrypterFromSeed.preCompute({saveSecret: true});
  348. var secret = preComputationAndSecret.secret;
  349. var secretFromDecrypter = mathRandomGenerator_.nextExponent(
  350. group_, {secureRandomGenerator: decrypterRandomGenerator_});
  351. expect(secretFromDecrypter.equals(secret)).toBeTruthy();
  352. });
  353. it('encrypt and decrypt some group elements, using encrypted elements generated with a short exponent',
  354. function() {
  355. var encrypterFromSeed = elGamalService_.newEncrypter().init(
  356. largePublicKey_,
  357. {secureRandomGenerator: encrypterRandomGenerator_});
  358. var encryptedElementsAndSecret = encrypterFromSeed.encrypt(
  359. elementsFromLargeGroup_,
  360. {useShortExponent: true, saveSecret: true});
  361. expect(encryptedElementsAndSecret.secret.value.bitLength())
  362. .not.toBeGreaterThan(shortExponentBitLength_);
  363. var encryptedElements = elGamalService_.newEncryptedElements(
  364. encryptedElementsAndSecret.gamma,
  365. encryptedElementsAndSecret.phis);
  366. var prngDecrypter = elGamalService_.newPrngDecrypter().init(
  367. largePublicKey_, decrypterRandomGenerator_);
  368. var decryptedElements = prngDecrypter.decrypt(
  369. encryptedElements, {useShortExponent: true});
  370. checkDecryptedElements(
  371. decryptedElements, elementsFromLargeGroup_);
  372. });
  373. it('encrypt and decrypt some group elements, using an encryption pre-computation generated with a short exponent',
  374. function() {
  375. var encrypterFromSeed = elGamalService_.newEncrypter().init(
  376. largePublicKey_,
  377. {secureRandomGenerator: encrypterRandomGenerator_});
  378. var preComputationAndSecret = encrypterFromSeed.preCompute(
  379. {useShortExponent: true, saveSecret: true});
  380. expect(preComputationAndSecret.secret.value.bitLength())
  381. .not.toBeGreaterThan(shortExponentBitLength_);
  382. var preComputation = elGamalService_.newEncryptedElements(
  383. preComputationAndSecret.gamma, preComputationAndSecret.phis);
  384. var encryptedElements = encrypterFromSeed.encrypt(
  385. elementsFromLargeGroup_, {preComputation: preComputation});
  386. var prngDecrypter = elGamalService_.newPrngDecrypter().init(
  387. largePublicKey_, decrypterRandomGenerator_);
  388. var decryptedElements = prngDecrypter.decrypt(
  389. encryptedElements, {useShortExponent: true});
  390. checkDecryptedElements(
  391. decryptedElements, elementsFromLargeGroup_);
  392. });
  393. it('encrypt and decrypt an array of single group elements',
  394. function() {
  395. var encrypterFromSeed = elGamalService_.newEncrypter().init(
  396. largePublicKey_,
  397. {secureRandomGenerator: encrypterRandomGenerator_});
  398. var encryptedElementArray = [];
  399. for (var i = 0; i < singleElements_.length; i++) {
  400. encryptedElementArray.push(
  401. encrypterFromSeed.encrypt([singleElements_[i]]));
  402. }
  403. var prngDecrypter = elGamalService_.newPrngDecrypter().init(
  404. largePublicKey_, decrypterRandomGenerator_);
  405. var decryptedElementArray = [];
  406. for (var j = 0; j < encryptedElementArray.length; j++) {
  407. decryptedElementArray.push(
  408. prngDecrypter.decrypt(encryptedElementArray[j])[0]);
  409. }
  410. checkDecryptedElements(decryptedElementArray, singleElements_);
  411. });
  412. });
  413. });
  414. function validatePublicKey(publicKey, group) {
  415. var elements = publicKey.elements;
  416. var numElements = elements.length;
  417. expect(numElements).toBe(publicKeyElements_.length);
  418. for (var i = 0; i < numElements; i++) {
  419. expect(elements[i].value.toString())
  420. .toEqual(publicKeyElements_[i].value.toString());
  421. }
  422. expect(publicKey.group.equals(group)).toBeTruthy();
  423. }
  424. function validatePrivateKey(privateKey, group) {
  425. var exponents = privateKey.exponents;
  426. var numExponents = exponents.length;
  427. expect(numExponents).toBe(privateKeyExponents_.length);
  428. for (var i = 0; i < numExponents; i++) {
  429. expect(exponents[i].value.toString())
  430. .toEqual(privateKeyExponents_[i].value.toString());
  431. }
  432. expect(privateKey.group.equals(group)).toBeTruthy();
  433. }
  434. function checkDecryptedElements(decryptedElements, elements) {
  435. expect(decryptedElements.length).toBe(elements.length);
  436. for (var i = 0; i < decryptedElements.length; i++) {
  437. expect(decryptedElements[i].equals(elements[i])).toBeTruthy();
  438. }
  439. }
  440. function checkSecretExponent(
  441. publicKey, encryptedElementsAndSecret, elements) {
  442. var publicKeyElements = publicKey.elements;
  443. var gamma = encryptedElementsAndSecret.gamma;
  444. var phis = encryptedElementsAndSecret.phis;
  445. var secret = encryptedElementsAndSecret.secret;
  446. expect((group_.generator.exponentiate(secret)).equals(gamma)).toBeTruthy();
  447. for (var i = 0; i < phis.length; i++) {
  448. expect((publicKeyElements[i].exponentiate(secret).multiply(elements[i]))
  449. .equals(phis[i]))
  450. .toBeTruthy();
  451. }
  452. }
  453. });