prng-decrypter.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  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 ElGamalPublicKey = require('./public-key');
  11. var ElGamalEncryptedElements = require('./encrypted-elements');
  12. var validator = require('./input-validator');
  13. module.exports = ElGamalPrngDecrypter;
  14. /**
  15. * @class ElGamalPrngDecrypter
  16. * @classdesc The ElGamal PRNG decrypter API. To instantiate this object, use
  17. * the method {@link ElGamalCryptographyService.newPrngDecrypter}.
  18. * @hideconstructor
  19. * @param {MathematicalService}
  20. * mathService The mathematical service to use.
  21. */
  22. function ElGamalPrngDecrypter(mathService) {
  23. var mathArrayCompressor_ = mathService.newArrayCompressor();
  24. var mathRandomGenerator_ = mathService.newRandomGenerator();
  25. var publicKey_;
  26. var secureRandomGenerator_;
  27. /**
  28. * Initializes the ElGamal PRNG decrypter with the provided ElGamal public
  29. * key and secure random generator.
  30. *
  31. * @function init
  32. * @memberof ElGamalPrngDecrypter
  33. * @param {ElGamalPublicKey}
  34. * publicKey The ElGamal public key with which to initialize the
  35. * ElGamal PRNG decrypter.
  36. * @param {SecureRandomGenerator}
  37. * secureRandomGenerator The secure random generator which which
  38. * to initialize the ElGamal PRNG decrypter.
  39. * @returns {ElGamalPrngDecrypter} A reference to this object, to facilitate
  40. * method chaining.
  41. * @throws {Error}
  42. * If the input data validation fails.
  43. */
  44. this.init = function(publicKey, secureRandomGenerator) {
  45. checkElGamalPublicKey(
  46. publicKey,
  47. 'ElGamal public key with which to initialize ElGamal PRNG decrypter');
  48. validator.checkIsObjectWithProperties(
  49. secureRandomGenerator,
  50. 'Secure random generator with which to initialize ElGamal PRNG decrypter');
  51. publicKey_ = publicKey;
  52. secureRandomGenerator_ = secureRandomGenerator;
  53. return this;
  54. };
  55. /**
  56. * ElGamal decrypts some ElGamal encrypted Zp group elements. Before using
  57. * this method, the decrypter must have been initialized with an ElGamal
  58. * public key and a secure random generator, via the method
  59. * {@link ElGamalPrngDecrypter.init}.
  60. * <p>
  61. * The number of phi elements that comprise the encryption must be less than
  62. * or equal to the number of elements in the ElGamal public key that was
  63. * used to initialize the ElGamal PRNG decrypter.
  64. *
  65. * @function decrypt
  66. * @memberof ElGamalPrngDecrypter
  67. * @param {ElGamalEncryptedElements}
  68. * encryptedElements The ElGamal encrypted Zp group elements.
  69. * @param {Object}
  70. * [options] An object containing optional arguments.
  71. * @param {boolean}
  72. * [options.confirmMembership=false] If <true>true</false>, then
  73. * each of the encryption elements will be checked for membership
  74. * in the Zp subgroup associated with the public key.
  75. * <b>WARNING:</b> This operation is computationally costly and increases
  76. * linearly with the number of encrypted elements.
  77. * @param {boolean}
  78. * [options.useShortExponent=false] If <code>true</code>, a
  79. * <code>short</code> secret exponent was used for the
  80. * encryption and must be regenerated for the decryption.
  81. * @returns {ZpGroupElement[]} The decrypted Zp group elements.
  82. * @throws {Error}
  83. * If the input data validation fails.
  84. */
  85. this.decrypt = function(encryptedElements, options) {
  86. if (typeof publicKey_ === 'undefined') {
  87. throw new Error(
  88. 'Could not ElGamal decrypt; PRNG decrypter has not been initialized with any ElGamal public key');
  89. }
  90. options = options || {};
  91. checkDecryptionData(publicKey_, encryptedElements, options);
  92. var phis = encryptedElements.phis;
  93. var group = publicKey_.group;
  94. var checkMembership = options.checkMembership || false;
  95. if (checkMembership) {
  96. for (var i = 0; i < phis.length; i++) {
  97. if (!(group.isGroupMember(phis[i]))) {
  98. throw new Error(
  99. 'Found phi element with value: ' + phis[i].value +
  100. ' that is not member of Zp subgroup associated with public key.');
  101. }
  102. }
  103. }
  104. var compressedPublicKey = compressPublicKey(publicKey_, phis.length);
  105. var publicKeyElements = compressedPublicKey.elements;
  106. var useShortExponent = options.useShortExponent || false;
  107. var secret = mathRandomGenerator_.nextExponent(group, {
  108. secureRandomGenerator: secureRandomGenerator_,
  109. useShortExponent: useShortExponent
  110. });
  111. var negatedSecret = secret.negate();
  112. var decryptedElements = [];
  113. for (var j = 0; j < encryptedElements.phis.length; j++) {
  114. decryptedElements.push(
  115. publicKeyElements[j].exponentiate(negatedSecret).multiply(phis[j]));
  116. }
  117. return decryptedElements;
  118. };
  119. function checkElGamalPublicKey(publicKey, label) {
  120. validator.checkElGamalPublicKey(publicKey, label);
  121. validator.checkIsInstanceOf(
  122. publicKey, ElGamalPublicKey, 'ElGamalPublicKey', label);
  123. }
  124. function checkElGamalEncryptedElements(elements, label) {
  125. validator.checkElGamalEncryptedElements(elements, label);
  126. validator.checkIsInstanceOf(
  127. elements, ElGamalEncryptedElements, 'ElGamalEncryptedElements', label);
  128. }
  129. function checkDecryptionData(publicKey, encryptedElements, options) {
  130. checkElGamalEncryptedElements(
  131. encryptedElements, 'ElGamal encrypted elements to decrypt, using PRNG');
  132. var numPublicKeyElements = publicKey.elements.length;
  133. var numPhis = encryptedElements.phis.length;
  134. if (numPublicKeyElements < numPhis) {
  135. throw new Error(
  136. 'Expected number of phi elements to decrypt, using PRNG, to be less than or equal to number of public key elements: ' +
  137. numPublicKeyElements + ' ; Found: ' + numPhis);
  138. }
  139. var confirmMembership = options.confirmMembership;
  140. if (typeof confirmMembership !== 'undefined') {
  141. validator.checkIsType(
  142. confirmMembership, 'boolean',
  143. '\"Confirm group membership\" flag for decryption, using PRNG');
  144. }
  145. var useShortExponent = options.useShortExponent;
  146. if (typeof useShortExponent !== 'undefined') {
  147. validator.checkIsType(
  148. useShortExponent, 'boolean',
  149. '\"Use shoft exponent\" flag for decryption, using PRNG');
  150. }
  151. }
  152. function compressPublicKey(publicKey, numPhis) {
  153. var elements = publicKey.elements;
  154. var group = publicKey.group;
  155. if (elements.length === numPhis) {
  156. return publicKey;
  157. } else {
  158. var compressedElements =
  159. mathArrayCompressor_.compressTrailingZpGroupElements(
  160. elements, numPhis);
  161. return new ElGamalPublicKey(group, compressedElements);
  162. }
  163. }
  164. }