decrypter.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  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 ElGamalPrivateKey = require('./private-key');
  11. var ElGamalEncryptedElements = require('./encrypted-elements');
  12. var validator = require('./input-validator');
  13. module.exports = ElGamalDecrypter;
  14. /**
  15. * @class ElGamalDecrypter
  16. * @classdesc The ElGamal decrypter API. To instantiate this object, use the
  17. * method {@link ElGamalCryptographyService.newDecrypter}.
  18. * @hideconstructor
  19. * @param {MathematicalService}
  20. * mathService The mathematical service to use.
  21. */
  22. function ElGamalDecrypter(mathService) {
  23. var mathArrayCompressor_ = mathService.newArrayCompressor();
  24. var privateKey_;
  25. /**
  26. * Initializes the ElGamal decrypter with the provided ElGamal private key.
  27. *
  28. * @function init
  29. * @memberof ElGamalDecrypter
  30. * @param {ElGamalPrivateKey}
  31. * privateKey The ElGamal private key with which to initialize
  32. * the ElGamal decrypter.
  33. * @returns {ElGamalDecrypter} A reference to this object, to facilitate
  34. * method chaining.
  35. * @throws {Error}
  36. * If the input data validation fails.
  37. */
  38. this.init = function(privateKey) {
  39. checkElGamalPrivateKey(
  40. privateKey,
  41. 'ElGamal private key with which to initialize ElGamal decrypter');
  42. privateKey_ = privateKey;
  43. return this;
  44. };
  45. /**
  46. * ElGamal decrypts some ElGamal encrypted Zp group elements. Before using
  47. * this method, the decrypter must have been initialized with an ElGamal
  48. * private key, via the method {@link ElGamalDecrypter.init}.
  49. * <p>
  50. * The number of phi elements that comprise the encryption must be less than
  51. * or equal to the number of exponents in the ElGamal private key that was
  52. * used to initialize the ElGamal decrypter.
  53. *
  54. * @function decrypt
  55. * @memberof ElGamalDecrypter
  56. * @param {ElGamalEncryptedElements}
  57. * encryptedElements The ElGamal encrypted Zp group elements.
  58. * @param {Object}
  59. * [options] An object containing optional arguments.
  60. * @param {boolean}
  61. * [options.confirmMembership=false] If <true>true</false>, then
  62. * each of the encryption elements will be checked for membership
  63. * in the Zp subgroup associated with the private key.
  64. * <b>WARNING:</b> This operation is computationally costly and
  65. * increases linearly with the number of encrypted elements.
  66. * @returns {ZpGroupElement[]} The decrypted Zp group elements.
  67. * @throws {Error}
  68. * If the input data validation fails.
  69. */
  70. this.decrypt = function(encryptedElements, options) {
  71. if (typeof privateKey_ === 'undefined') {
  72. throw new Error(
  73. 'Could not ElGamal decrypt; Decrypter has not been initialized with any ElGamal private key');
  74. }
  75. options = options || {};
  76. checkDecryptionData(privateKey_, encryptedElements, options);
  77. var gamma = encryptedElements.gamma;
  78. var phis = encryptedElements.phis;
  79. var checkMembership = options.checkMembership || false;
  80. if (checkMembership) {
  81. var group = privateKey_.group;
  82. for (var i = 0; i < phis.length; i++) {
  83. if (!(group.isGroupMember(phis[i]))) {
  84. throw new Error(
  85. 'Found phi element with value: ' + phis[i].value +
  86. ' that is not member of Zp subgroup associated with private key.');
  87. }
  88. }
  89. }
  90. var compressedPrivateKey = compressPrivateKey(privateKey_, phis.length);
  91. var privateKeyExponents = compressedPrivateKey.exponents;
  92. var decryptedElements = [];
  93. for (var j = 0; j < phis.length; j++) {
  94. decryptedElements.push(gamma.exponentiate(privateKeyExponents[j].negate())
  95. .multiply(phis[j]));
  96. }
  97. return decryptedElements;
  98. };
  99. function checkElGamalPrivateKey(privateKey, label) {
  100. validator.checkElGamalPrivateKey(privateKey, label);
  101. validator.checkIsInstanceOf(
  102. privateKey, ElGamalPrivateKey, 'ElGamalPrivateKey', label);
  103. }
  104. function checkElGamalEncryptedElements(elements, label) {
  105. validator.checkElGamalEncryptedElements(elements, label);
  106. validator.checkIsInstanceOf(
  107. elements, ElGamalEncryptedElements, 'ElGamalEncryptedElements', label);
  108. }
  109. function checkDecryptionData(privateKey, encryptedElements, options) {
  110. checkElGamalEncryptedElements(
  111. encryptedElements, 'ElGamal encrypted elements to decrypt');
  112. var numPrivateKeyExponents = privateKey.exponents.length;
  113. var numPhis = encryptedElements.phis.length;
  114. if (numPrivateKeyExponents < numPhis) {
  115. throw new Error(
  116. 'Expected number of phi elements to decrypt to be less than or equal to number of private key exponents: ' +
  117. numPrivateKeyExponents + ' ; Found: ' + numPhis);
  118. }
  119. var confirmMembership = options.confirmMembership;
  120. if (typeof confirmMembership !== 'undefined') {
  121. validator.checkIsType(
  122. confirmMembership, 'boolean',
  123. '\"Confirm group membership\" flag for decryption');
  124. }
  125. }
  126. function compressPrivateKey(privateKey, numPhis) {
  127. var group = privateKey.group;
  128. var exponents = privateKey.exponents;
  129. if (exponents.length === numPhis) {
  130. return privateKey;
  131. } else {
  132. var compressedExponents =
  133. mathArrayCompressor_.compressTrailingExponents(exponents, numPhis);
  134. return new ElGamalPrivateKey(group, compressedExponents);
  135. }
  136. }
  137. }