homomorphic.cipher.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747
  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. cryptolib.modules.homomorphic = cryptolib.modules.homomorphic || {};
  9. cryptolib.modules.homomorphic.cipher = function(box) {
  10. 'use strict';
  11. box.homomorphic = box.homomorphic || {};
  12. box.homomorphic.cipher = {};
  13. /**
  14. * A submodule that holds ElGamal cipher functionalities.
  15. *
  16. * @exports homomorphic/cipher/factory
  17. */
  18. box.homomorphic.cipher.factory = {};
  19. var converters, exceptions, randomFactory, mathematical, keyPairFactory;
  20. var f = function(box) {
  21. converters = new box.commons.utils.Converters();
  22. exceptions = box.commons.exceptions;
  23. mathematical = box.commons.mathematical;
  24. randomFactory =
  25. new box.primitives.securerandom.factory.SecureRandomFactory();
  26. keyPairFactory = new box.homomorphic.keypair.factory.KeyFactory();
  27. };
  28. f.policies = {
  29. primitives: {
  30. secureRandom:
  31. {provider: box.policies.homomorphic.cipher.secureRandom.provider}
  32. }
  33. };
  34. cryptolib('commons', 'primitives.securerandom', 'homomorphic.keypair', f);
  35. /** @class */
  36. box.homomorphic.cipher.factory.ElGamalCipherFactory = function() {
  37. };
  38. box.homomorphic.cipher.factory.ElGamalCipherFactory.prototype = {
  39. /**
  40. * @function createEncrypter
  41. * @returns ElGamalEncrypter
  42. */
  43. createEncrypter: function(elGamalPublicKey, cryptoRandomInteger) {
  44. if (typeof cryptoRandomInteger === 'undefined') {
  45. cryptoRandomInteger = randomFactory.getCryptoRandomInteger();
  46. }
  47. return new ElGamalEncrypter(elGamalPublicKey, cryptoRandomInteger);
  48. },
  49. /**
  50. * @function createDecrypter
  51. * @returns ElGamalDecrypter
  52. */
  53. createDecrypter: function(elGamalPrivateKey) {
  54. return new ElGamalDecrypter(elGamalPrivateKey);
  55. },
  56. /**
  57. * @function createRandomDecrypter
  58. * @returns ElGamalRandomDecrypter
  59. */
  60. createRandomDecrypter: function(elGamalPublicKey, cryptoRandomInteger) {
  61. return new ElGamalRandomDecrypter(elGamalPublicKey, cryptoRandomInteger);
  62. }
  63. };
  64. function validateArray(inputArray) {
  65. if (typeof inputArray === 'undefined') {
  66. throw new exceptions.CryptoLibException(
  67. 'The given array should be initialized');
  68. }
  69. if (!(inputArray instanceof Array)) {
  70. throw new exceptions.CryptoLibException(
  71. 'The given array are not from the expected type');
  72. }
  73. if (inputArray === []) {
  74. throw new exceptions.CryptoLibException(
  75. 'The given array cannot be empty');
  76. }
  77. }
  78. /**
  79. * Defines an ElGamal encrypter.
  80. * <P>
  81. * The methods specified in this object allow data to be encrypted using an
  82. * implementation of the ElGamal cryptosystem.
  83. *
  84. * @class ElGamalEncrypter
  85. * @param {ElGamalPublicKey}
  86. * elGamalPublicKey The public key.
  87. * @param {Object}
  88. * cryptoRandomInteger The source of randomness.
  89. * @returns {ElGamalEncrypter}
  90. */
  91. function ElGamalEncrypter(elGamalPublicKey, cryptoRandomInteger) {
  92. var _elGamalPublicKey = elGamalPublicKey;
  93. var _cryptoRandomInteger = cryptoRandomInteger;
  94. /**
  95. * @function getElGamalPublicKey
  96. *
  97. * @returns {ElGamalPublicKey}
  98. */
  99. this.getElGamalPublicKey = function() {
  100. return _elGamalPublicKey;
  101. };
  102. /**
  103. * @function getCryptoRandomInteger
  104. *
  105. * @returns {ElGamalPublicKey}
  106. */
  107. this.getCryptoRandomInteger = function() {
  108. return _cryptoRandomInteger;
  109. };
  110. // Private methods
  111. this._validateMessagesNotLargerThanKeySize = function(length) {
  112. if (_elGamalPublicKey.getGroupElementsArray().length < length) {
  113. throw new exceptions.CryptoLibException(
  114. 'The list of messages to encrypt was larger than the number of public key elements.');
  115. }
  116. };
  117. this._validatePrePhisNotLargerThanKeySize = function(phis) {
  118. if (_elGamalPublicKey.getGroupElementsArray().length < phis.length) {
  119. throw new exceptions.CryptoLibException(
  120. 'The list of prePhis was larger than the number of public key elements.');
  121. }
  122. };
  123. this._preCompute = function(useShortExponent) {
  124. var gamma;
  125. var prePhis = [];
  126. try {
  127. var publicKeyElements = _elGamalPublicKey.getGroupElementsArray();
  128. var group = _elGamalPublicKey.getGroup();
  129. if (useShortExponent &&
  130. !group.isQuadraticResidueGroup()) {
  131. throw new exceptions.CryptoLibException(
  132. 'Attempt to ElGamal encrypt using short exponent for Zp subgroup that is not of type quadratic residue.');
  133. }
  134. var randomExponent = mathematical.groupUtils.generateRandomExponent(
  135. group, _cryptoRandomInteger, useShortExponent);
  136. gamma = group.getGenerator().exponentiate(randomExponent);
  137. // For each element in the array of public key elements, compute the
  138. // following: element: prephi[i] = pubKey[i]^(random)
  139. var pubKeyElementRaised;
  140. for (var i = 0; i < publicKeyElements.length; i++) {
  141. pubKeyElementRaised =
  142. publicKeyElements[i].exponentiate(randomExponent);
  143. prePhis.push(pubKeyElementRaised);
  144. }
  145. return new ElGamalEncrypterValues(randomExponent, gamma, prePhis);
  146. } catch (error) {
  147. throw new exceptions.CryptoLibException(
  148. 'There was an error while precomputing values.', error);
  149. }
  150. };
  151. this._compute = function(messages, computationValues) {
  152. var compressedComputationValues = compressComputationValuesIfNecessary(
  153. messages.length, computationValues);
  154. var phis = [];
  155. // For each element in the array of messages, compute the following:
  156. // element:phi[i]=message[i]*prePhi[i]
  157. for (var i = 0; i < compressedComputationValues.getPhis().length; i++) {
  158. phis.push(
  159. messages[i].multiply(compressedComputationValues.getPhis()[i]));
  160. }
  161. return new box.homomorphic.cipher.ElGamalComputationValues(
  162. compressedComputationValues.getGamma(), phis);
  163. };
  164. this._getListAsQuadraticResidues = function(messages) {
  165. var valuesZpSubgroupElements = [];
  166. var group = _elGamalPublicKey.getGroup();
  167. var groupElement;
  168. for (var i = 0; i < messages.length; i++) {
  169. groupElement = new mathematical.ZpGroupElement(
  170. new box.forge.jsbn.BigInteger(messages[i]), group.getP(),
  171. group.getQ());
  172. valuesZpSubgroupElements.push(groupElement);
  173. }
  174. return valuesZpSubgroupElements;
  175. };
  176. function compressComputationValuesIfNecessary(
  177. numMessages, computationValues) {
  178. var phis = computationValues.getPhis();
  179. if (phis.length <= numMessages) {
  180. return computationValues;
  181. }
  182. var compressedPhiArray =
  183. mathematical.groupUtils.buildListWithCompressedFinalElement(
  184. _elGamalPublicKey.getGroup(), phis, numMessages);
  185. return new box.homomorphic.cipher.ElGamalComputationValues(
  186. computationValues.getGamma(), compressedPhiArray);
  187. }
  188. }
  189. ElGamalEncrypter.prototype = {
  190. /**
  191. * Encrypt the received list of messages (which are represented as
  192. * ZpGroupElements).
  193. * <p>
  194. * The length of the received list of messages must be equal to, or less
  195. * than, the length of the public key of this encrypter. If this
  196. * condition is not met, then an exception will be thrown.
  197. *
  198. * @function encryptGroupElements
  199. *
  200. * @param {Array}
  201. * messages an array of ZpGroupElements.
  202. * @param {Object} [encryptionOption] an optional input parameter. If this option is of type
  203. * {boolean}, then the encryption will be pre-computed, using
  204. * the value of the boolean to determine whether or not to
  205. * use a short random exponent for the pre-computation. If
  206. * the option is of type {ElGamalEncrypterValues} then it
  207. * will be used as the pre-computation of the encryption.
  208. *
  209. * @returns {ElGamalEncrypterValues}
  210. */
  211. encryptGroupElements: function(messages, encryptionOption) {
  212. validateArray(messages);
  213. this._validateMessagesNotLargerThanKeySize(messages.length);
  214. var useShortExponent;
  215. var preComputedValues;
  216. if (typeof encryptionOption !== 'undefined' &&
  217. typeof encryptionOption === 'boolean') {
  218. useShortExponent = encryptionOption;
  219. } else {
  220. preComputedValues = encryptionOption;
  221. }
  222. if (!preComputedValues) {
  223. preComputedValues = this._preCompute(useShortExponent);
  224. } else {
  225. var prePhis = preComputedValues.getPhis();
  226. validateArray(prePhis);
  227. this._validatePrePhisNotLargerThanKeySize(prePhis);
  228. }
  229. var computationValues = this._compute(
  230. messages, preComputedValues.getElGamalComputationValues());
  231. return new ElGamalEncrypterValues(
  232. preComputedValues.getR(), computationValues.getGamma(),
  233. computationValues.getPhis());
  234. },
  235. /**
  236. * Pre-compute the ElGamal encrypter values, based on the ElGamal public
  237. * key provided to the encrypter.
  238. *
  239. * @param {boolean}
  240. * [useShortExponent] true if a short exponent is to be used
  241. * for the pre-computations. Default value is false.
  242. * @returns {ElGamalEncrypterValues}
  243. */
  244. preCompute: function(useShortExponent) {
  245. return this._preCompute(useShortExponent);
  246. },
  247. /**
  248. * Encrypt the received list of messages (which are represented as
  249. * Strings).
  250. * <p>
  251. * The length of the received list of messages must be equal to, or less
  252. * than, the length of the public key of this encrypter. If this
  253. * condition is not met, then an exception will be thrown.
  254. *
  255. * @function encryptStrings
  256. *
  257. * @param {Array}
  258. * messages an array of strings.
  259. * @param {ElGamalEncrypterValues}
  260. * [preComputedValues] an optional parameter. If it is not
  261. * provided, then pre-computations are made.
  262. *
  263. * @returns {ElGamalEncrypterValues} the pre-computed values.
  264. */
  265. encryptStrings: function(messages, preComputedValues) {
  266. var messagesAsGroupElements = this._getListAsQuadraticResidues(messages);
  267. if (!preComputedValues) {
  268. return this.encryptGroupElements(messagesAsGroupElements);
  269. } else {
  270. return this.encryptGroupElements(
  271. messagesAsGroupElements, preComputedValues);
  272. }
  273. }
  274. };
  275. /**
  276. * Defines an ElGamal decrypter.
  277. *
  278. * @class ElGamalDecrypter
  279. * @param {ElGamalPrivateKey}
  280. * elGamalPrivateKey The ElGamal private key.
  281. * @returns {ElGamalDecrypter}
  282. */
  283. function ElGamalDecrypter(elGamalPrivateKey) {
  284. function validateCorrectGroup(elGamalPrivateKey) {
  285. var exponentsArray = elGamalPrivateKey.getExponentsArray();
  286. var group = elGamalPrivateKey.getGroup();
  287. for (var i = 0; i < exponentsArray.length; i++) {
  288. if (!(group.getQ().equals(exponentsArray[i].getQ()))) {
  289. throw new exceptions.CryptoLibException(
  290. 'Each Exponent must be of the specified group order.');
  291. }
  292. }
  293. }
  294. validateCorrectGroup(elGamalPrivateKey);
  295. var _elGamalPrivateKey = elGamalPrivateKey;
  296. this.getElGamalPrivateKey = function() {
  297. return _elGamalPrivateKey;
  298. };
  299. this._areGroupMembers = function(cipherText) {
  300. for (var i = 0; i < cipherText.getPhis().length; i++) {
  301. var next = cipherText.getPhis()[i];
  302. if (!(_elGamalPrivateKey.getGroup().isGroupMember(next))) {
  303. return false;
  304. }
  305. }
  306. return true;
  307. };
  308. this._validateCiphertextSize = function(cipherText) {
  309. if (_elGamalPrivateKey.getExponentsArray().length <
  310. cipherText.getPhis().length) {
  311. throw new exceptions.CryptoLibException(
  312. 'The list of ciphertext was larger than the number of private key exponents.');
  313. }
  314. };
  315. this._compressKeyIfNecessary = function(numRequired) {
  316. var exponents = _elGamalPrivateKey.getExponentsArray();
  317. var group = _elGamalPrivateKey.getGroup();
  318. if (exponents.length <= numRequired) {
  319. return _elGamalPrivateKey;
  320. }
  321. var listWithCompressedFinalExponent =
  322. mathematical.groupUtils.buildListWithCompressedFinalExponent(
  323. group, exponents, numRequired);
  324. return keyPairFactory.createPrivateKey(
  325. listWithCompressedFinalExponent, _elGamalPrivateKey.getGroup());
  326. };
  327. }
  328. ElGamalDecrypter.prototype = {
  329. /**
  330. * Decrypts a ciphertext.
  331. * <P>
  332. * The encrypted message parameter will be a list of group elements,
  333. * encapsulated within an ComputationValues object.
  334. * <P>
  335. * The length of the received ciphertext (number of group elements
  336. * contained within it) must be equal to, or less than, the length of
  337. * the private key of this decrypter. If this condition is not met, then
  338. * an exception will be thrown.
  339. *
  340. * @function decrypt
  341. *
  342. * @param {Array}
  343. * cipherText the encrypted message to be decrypted.
  344. * @param {Boolean}
  345. * [confirmGroupMembership] if true, a confirmation is made that
  346. * each element in
  347. * {@code ciphertext} is a member of the mathematical group
  348. * over which this decrypter operates. Default value is false.
  349. * @returns {Array} the decrypted ciphertext.
  350. */
  351. decrypt: function(cipherText, confirmGroupMembership) {
  352. if (confirmGroupMembership === true &&
  353. !this._areGroupMembers(cipherText)) {
  354. throw new exceptions.CryptoLibException(
  355. 'All values to decrypt must be group elements.');
  356. }
  357. this._validateCiphertextSize(cipherText);
  358. var privateKeyAfterCompression =
  359. this._compressKeyIfNecessary(cipherText.getPhis().length);
  360. var plaintext = [];
  361. var negatedExponent;
  362. var exponentsArray = privateKeyAfterCompression.getExponentsArray();
  363. for (var i = 0; i < cipherText.getPhis().length; i++) {
  364. // Compute the e = negate (-) of privKey[i]
  365. negatedExponent = exponentsArray[i].negate();
  366. // Compute dm[i]= gamma^(e) * phi[i]
  367. plaintext.push(cipherText.getGamma()
  368. .exponentiate(negatedExponent)
  369. .multiply(cipherText.getPhis()[i]));
  370. }
  371. return plaintext;
  372. }
  373. };
  374. /**
  375. * Defines an ElGamal decrypter that can be used to decrypt some ciphertext,
  376. * given the public key and the source of randomness used to generate the
  377. * ciphertext.
  378. *
  379. * @class ElGamalRandomDecrypter
  380. * @param {ElGamalPublicKey}
  381. * elGamalPublicKey The ElGamal public key.
  382. * @param {Object} cryptoRandomInteger The source of randomness.
  383. * @returns {ElGamalDecrypter} The ElGamal decrypter.
  384. */
  385. function ElGamalRandomDecrypter(elGamalPublicKey, cryptoRandomInteger) {
  386. function validateCorrectGroup(elGamalPublicKey) {
  387. var elementsArray = elGamalPublicKey.getGroupElementsArray();
  388. var group = elGamalPublicKey.getGroup();
  389. for (var i = 0; i < elementsArray.length; i++) {
  390. if (!(group.getP().equals(elementsArray[i].getP()))) {
  391. throw new exceptions.CryptoLibException(
  392. 'Each element must be of the specified group modulus.');
  393. }
  394. if (!(group.getQ().equals(elementsArray[i].getQ()))) {
  395. throw new exceptions.CryptoLibException(
  396. 'Each element must be of the specified group order.');
  397. }
  398. }
  399. }
  400. validateCorrectGroup(elGamalPublicKey);
  401. var _elGamalPublicKey = elGamalPublicKey;
  402. var _cryptoRandomInteger = cryptoRandomInteger;
  403. this.getElGamalPublicKey = function() {
  404. return _elGamalPublicKey;
  405. };
  406. this.getCryptoRandomInteger = function() {
  407. return _cryptoRandomInteger;
  408. };
  409. this._areGroupMembers = function(cipherText) {
  410. for (var i = 0; i < cipherText.getPhis().length; i++) {
  411. var next = cipherText.getPhis()[i];
  412. if (!(_elGamalPublicKey.getGroup().isGroupMember(next))) {
  413. return false;
  414. }
  415. }
  416. return true;
  417. };
  418. this._validateCiphertextSize = function(cipherText) {
  419. if (_elGamalPublicKey.getGroupElementsArray().length <
  420. cipherText.getPhis().length) {
  421. throw new exceptions.CryptoLibException(
  422. 'The list of ciphertext was larger than the number of public key elements.');
  423. }
  424. };
  425. this._compressKeyIfNecessary = function(numRequired) {
  426. var publicKeyElements = _elGamalPublicKey.getGroupElementsArray();
  427. var group = _elGamalPublicKey.getGroup();
  428. if (publicKeyElements.length <= numRequired) {
  429. return _elGamalPublicKey;
  430. }
  431. var listWithCompressedFinalElement =
  432. mathematical.groupUtils.buildListWithCompressedFinalElement(
  433. group, publicKeyElements, numRequired);
  434. return keyPairFactory.createPublicKey(
  435. listWithCompressedFinalElement, _elGamalPublicKey.getGroup());
  436. };
  437. }
  438. ElGamalRandomDecrypter.prototype = {
  439. /**
  440. * Decrypts a ciphertext, using the public key and the source of randomness
  441. * used to generate the ciphertext. <P> The encrypted message parameter will
  442. * be a list of group elements, encapsulated within an ComputationValues
  443. * object. <P> The length of the received ciphertext (number of group
  444. * elements contained within it) must be equal to, or less than, the length
  445. * of the public key of this decrypter. If this condition is not met, then
  446. * an exception will be thrown.
  447. *
  448. * @function decrypt
  449. *
  450. * @param {Array}
  451. * cipherText the encrypted message to be decrypted.
  452. * @param {Boolean}
  453. * [confirmGroupMembership] if true, a confirmation is made that
  454. * each element in
  455. * {@code ciphertext} is a member of the mathematical group
  456. * over which this decrypter operates. Default value is false.
  457. * @param {boolean}
  458. * [useShortExponent] set to true if a short exponent was used
  459. * for the encryption. Default value is false.
  460. * @returns {Array} the decrypted messages.
  461. */
  462. decrypt: function(cipherText, confirmGroupMembership, useShortExponent) {
  463. if (confirmGroupMembership === true &&
  464. !this._areGroupMembers(cipherText)) {
  465. throw new exceptions.CryptoLibException(
  466. 'All values to decrypt must be group elements.');
  467. }
  468. if (typeof useShortExponent === 'undefined') {
  469. useShortExponent = false;
  470. }
  471. this._validateCiphertextSize(cipherText);
  472. var publicKeyAfterCompression =
  473. this._compressKeyIfNecessary(cipherText.getPhis().length);
  474. var group = publicKeyAfterCompression.getGroup();
  475. var publicKeyElements = publicKeyAfterCompression.getGroupElementsArray();
  476. var plaintext = [];
  477. var negatedExponent;
  478. var randomExponent = mathematical.groupUtils.generateRandomExponent(
  479. group, this.getCryptoRandomInteger(), useShortExponent);
  480. negatedExponent = randomExponent.negate();
  481. for (var i = 0; i < cipherText.getPhis().length; i++) {
  482. // Compute dm[i]= publicKeyElement^(-e) * phi[i]
  483. plaintext.push(publicKeyElements[i]
  484. .exponentiate(negatedExponent)
  485. .multiply(cipherText.getPhis()[i]));
  486. }
  487. return plaintext;
  488. }
  489. };
  490. /**
  491. * Class which encapsulates an 'r' value (random exponent) and a set of
  492. * ElGamal encryption values (a gamma value and a list of phi values).
  493. *
  494. * @class ElGamalEncrypterValues
  495. * @param {Exponent}
  496. * exponent the Exponent to set.
  497. * @param {ZpGroupElement}
  498. * gamma the gamma (first element) of the ciphertext.
  499. * @param {Array}
  500. * phis the phi values of the ciphertext.
  501. * @returns {ElGamalEncrypterValues}
  502. */
  503. function ElGamalEncrypterValues(exponent, gamma, phis) {
  504. var _r = exponent;
  505. var _gamma = gamma;
  506. var _phis = phis;
  507. /**
  508. * @function
  509. *
  510. * @returns {Exponent}
  511. */
  512. this.getR = function() {
  513. return _r;
  514. };
  515. /**
  516. * @function
  517. *
  518. * @returns {ZpGroupElement}
  519. */
  520. this.getGamma = function() {
  521. return _gamma;
  522. };
  523. /**
  524. * @function
  525. *
  526. * @returns {Array}
  527. */
  528. this.getPhis = function() {
  529. return _phis;
  530. };
  531. this.getElGamalComputationValues = function() {
  532. return new box.homomorphic.cipher.ElGamalComputationValues(_gamma, _phis);
  533. };
  534. }
  535. box.homomorphic.cipher.ElGamalEncrypterValues = ElGamalEncrypterValues;
  536. /**
  537. * Encapsulates encryption parameters.
  538. * <p>
  539. * These parameters should have been generated as defined below:
  540. * <ul>
  541. * <li> For p and q:
  542. * http://csrc.nist.gov/publications/fips/fips186-3/fips_186-3.pdf
  543. * <li> For g:
  544. * http://csrc.nist.gov/publications/fips/fips186-3/fips_186-3.pdf
  545. * <li> For f:
  546. * http://csrc.nist.gov/publications/fips/fips186-3/fips_186-3.pdf
  547. * </ul>
  548. * <p>
  549. * Note that p, q, and g are received within the mathematical group.
  550. *
  551. *
  552. * @class EncryptionParameters
  553. * @param {ZpSubgroup}
  554. * group the group of the encryption parameters.
  555. *
  556. * @returns {EncryptionParameters}
  557. */
  558. box.homomorphic.cipher.EncryptionParameters = function(group) {
  559. var _group = group;
  560. /**
  561. * @function
  562. *
  563. * @returns {ZpSubgroup}
  564. */
  565. this.getEncParamGroup = function() {
  566. return _group;
  567. };
  568. };
  569. /**
  570. * Class which encapsulates a gamma and a set of phi values that are Zp
  571. * subgroup elements.
  572. *
  573. * @class ElGamalComputationValues
  574. * @param {ZpGroupElement}
  575. * gamma the gamma (first element) of the ciphertext.
  576. * @param {Array}
  577. * phis the phi values of the ciphertext.
  578. */
  579. box.homomorphic.cipher.ElGamalComputationValues = function(gamma, phis) {
  580. this._gamma = gamma;
  581. this._phis = phis;
  582. };
  583. box.homomorphic.cipher.ElGamalComputationValues.prototype = {
  584. getGamma: function() {
  585. return this._gamma;
  586. },
  587. getPhis: function() {
  588. return this._phis;
  589. },
  590. stringify: function() {
  591. var phis = Array();
  592. for (var i = 0; i < this._phis.length; i++) {
  593. phis[i] =
  594. converters.base64FromBigInteger(this._phis[i].getElementValue());
  595. }
  596. return JSON.stringify({
  597. ciphertext: {
  598. p: converters.base64FromBigInteger(this._gamma.getP()),
  599. q: converters.base64FromBigInteger(this._gamma.getQ()),
  600. gamma: converters.base64FromBigInteger(this._gamma.getElementValue()),
  601. phis: phis
  602. }
  603. });
  604. }
  605. };
  606. box.homomorphic.cipher.deserializeElGamalComputationValues = function(
  607. serializedObject) {
  608. var valuesJson = JSON.parse(serializedObject).ciphertext;
  609. var p = converters.base64ToBigInteger(valuesJson.p);
  610. var q = converters.base64ToBigInteger(valuesJson.q);
  611. var gamma = new mathematical.ZpGroupElement(
  612. converters.base64ToBigInteger(valuesJson.gamma), p, q);
  613. var phis = [];
  614. for (var i = 0; i < valuesJson.phis.length; i++) {
  615. phis.push(new mathematical.ZpGroupElement(
  616. converters.base64ToBigInteger(valuesJson.phis[i]), p, q));
  617. }
  618. return new box.homomorphic.cipher.ElGamalComputationValues(gamma, phis);
  619. };
  620. };