plaintext-equality-proof-handler.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  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 ZeroKnowledgeProofProver = require('../prover');
  11. var ZeroKnowledgeProofVerifier = require('../verifier');
  12. var ZeroKnowledgeProof = require('../proof');
  13. var ZeroKnowledgeProofPreComputation = require('../pre-computation');
  14. var PhiFunction = require('../phi-function');
  15. var ProgressMeter = require('../progress-meter');
  16. var validator = require('../input-validator');
  17. module.exports = PlaintextEqualityProofHandler;
  18. /**
  19. * @class PlaintextEqualityProofHandler
  20. * @classdesc Encapsulates a handler for the plaintext equality zero-knowledge
  21. * proof of knowledge generation, pre-computation and verification
  22. * processes. To instantiate this object, use the method {@link
  23. * ZeroKnowledgeProofService.newPlaintextEqualityProofHandler}.
  24. * @param {ZpSubgroup}
  25. * group The Zp subgroup to which all exponents and Zp group elements
  26. * required for the proof generation are associated or belong,
  27. * respectively.
  28. * @param {MessageDigestService}
  29. * messageDigestService The message digest service.
  30. * @param {MathematicalService}
  31. * mathematicalService The mathematical service.
  32. */
  33. function PlaintextEqualityProofHandler(
  34. group, messageDigestService, mathematicalService) {
  35. var NUM_PHI_INPUTS = 2;
  36. var DEFAULT_AUXILIARY_DATA = 'PlaintextEqualityProof';
  37. var mathGroupHandler_ = mathematicalService.newGroupHandler();
  38. var prover_ =
  39. new ZeroKnowledgeProofProver(messageDigestService, mathematicalService);
  40. var verifier_ =
  41. new ZeroKnowledgeProofVerifier(messageDigestService, mathematicalService);
  42. var progressCallback_;
  43. var progressPercentMinCheckInterval_;
  44. var measureProgress_ = false;
  45. var primaryPublicKey_;
  46. var secondaryPublicKey_;
  47. /**
  48. * Initializes the handler with the provided primary and secondary ElGamal
  49. * public keys.
  50. *
  51. * @function init
  52. * @memberof PlaintextEqualityProofHandler
  53. * @param {ElGamalPublicKey}
  54. * primaryPublicKey The primary ElGamal public key.
  55. * @param {ElGamalPublicKey}
  56. * secondaryPublicKey The secondary ElGamal public key.
  57. * @returns {PlaintextEqualityProofHandler} A reference to this object, to
  58. * facilitate method chaining.
  59. * @throws {Error}
  60. * If the input data validation fails.
  61. */
  62. this.init = function(primaryPublicKey, secondaryPublicKey) {
  63. checkInitData(primaryPublicKey, secondaryPublicKey);
  64. primaryPublicKey_ = primaryPublicKey;
  65. secondaryPublicKey_ = secondaryPublicKey;
  66. return this;
  67. };
  68. /**
  69. * Generates a plaintext equality zero-knowledge proof of knowledge. Before
  70. * using this method, the handler must have been initialized with primary
  71. * secondary public keys, via the method
  72. * {@link PlaintextEqualityProofHandler.init}.
  73. *
  74. * @function generate
  75. * @memberof PlaintextEqualityProofHandler
  76. * @param {Exponent}
  77. * primarySecret The secret exponent used to generate the primary
  78. * ciphertext, the knowledge of which must be proven.
  79. * @param {Exponent}
  80. * secondarySecret The secret exponent used to generate the
  81. * secondary ciphertext, the knowledge of which must be proven.
  82. * @param {ElGamalEncryptedElements}
  83. * primaryCiphertext The primary ciphertext.
  84. * @param {ElGamalEncryptedElements}
  85. * secondaryCiphertext The secondary ciphertext.
  86. * @param {Object}
  87. * [options] An object containing optional arguments.
  88. * @param {Uint8Array|string}
  89. * [options.data='PlaintextEqualityProof'] Auxiliary data.
  90. * @param {ZeroKnowledgeProofPreComputation}
  91. * [options.preComputation=Generated internally] A
  92. * pre-computation of the plaintext equality zero-knowledge proof
  93. * of knowledge.
  94. * @returns {ZeroKnowledgeProof} The plaintext equality zero-knowledge proof
  95. * of knowledge.
  96. * @throws {Error}
  97. * If the input data validation fails.
  98. */
  99. this.generate = function(
  100. primarySecret, secondarySecret, primaryCiphertext, secondaryCiphertext,
  101. options) {
  102. if (typeof primaryPublicKey_ === 'undefined') {
  103. throw new Error(
  104. 'Cannot generate plaintext equality proof; Associated handler has not been initialized with any public keys');
  105. }
  106. options = options || {};
  107. checkGenerationData(
  108. primarySecret, secondarySecret, primaryCiphertext, secondaryCiphertext,
  109. options);
  110. var data = options.data;
  111. if (typeof data === 'undefined') {
  112. data = DEFAULT_AUXILIARY_DATA;
  113. }
  114. var preComputation = options.preComputation;
  115. var privateValues = [primarySecret, secondarySecret];
  116. var publicValues =
  117. generatePublicValues(primaryCiphertext, secondaryCiphertext);
  118. if (typeof preComputation === 'undefined') {
  119. preComputation = this.preCompute();
  120. }
  121. return prover_.prove(
  122. group, privateValues, publicValues, data, preComputation);
  123. };
  124. /**
  125. * Pre-computes a plaintext equality zero-knowledge proof of knowledge.
  126. * IMPORTANT: The same pre-computed values must not be used twice. Before
  127. * using this method, the handler must have been initialized with primary
  128. * secondary public keys, via the method {@link
  129. * PlaintextEqualityProofHandler.init}.
  130. *
  131. * @function preCompute
  132. * @memberof PlaintextEqualityProofHandler
  133. * @returns {ZeroKnowledgeProofPreComputation} The plaintext equality
  134. * zero-knowledge proof of knowledge pre-computation.
  135. */
  136. this.preCompute = function() {
  137. if (typeof primaryPublicKey_ === 'undefined') {
  138. throw new Error(
  139. 'Cannot pre-compute plaintext equality proof; Associated handler has not been initialized with any public keys');
  140. }
  141. var phiFunction =
  142. newPhiFunction(group, primaryPublicKey_, secondaryPublicKey_);
  143. if (!measureProgress_) {
  144. return prover_.preCompute(group, phiFunction);
  145. } else {
  146. var preComputation = prover_.preCompute(
  147. group, phiFunction, newProgressMeter(primaryPublicKey_));
  148. measureProgress_ = false;
  149. return preComputation;
  150. }
  151. };
  152. /**
  153. * Verifies a plaintext equality zero-knowledge proof of knowledge. Before
  154. * using this method, the handler must have been initialized with primary
  155. * secondary public keys, via the method {@link
  156. * PlaintextEqualityProofHandler.init}.
  157. *
  158. * @function verify
  159. * @memberof PlaintextEqualityProofHandler
  160. * @param {ZeroKnowledgeProof}
  161. * proof The plaintext equality zero-knowledge proof of knowledge
  162. * to verify.
  163. * @param {ElGamalEncryptedElements}
  164. * primaryCiphertext The primary ciphertext.
  165. * @param {ElGamalEncryptedElements}
  166. * secondaryCiphertext The secondary ciphertext.
  167. * @param {Object}
  168. * [options] An object containing optional arguments.
  169. * @param {Uint8Array|string}
  170. * [options.data='PlaintextEqualityProof'] Auxiliary data. It
  171. * must be the same as that used to generate the proof.
  172. * @returns {boolean} <code>true</code> if the plaintext equality
  173. * zero-knowledge proof of knowledge was verified,
  174. * <code>false</code> otherwise.
  175. * @throws {Error}
  176. * If the input data validation fails.
  177. */
  178. this.verify = function(
  179. proof, primaryCiphertext, secondaryCiphertext, options) {
  180. if (typeof primaryPublicKey_ === 'undefined') {
  181. throw new Error(
  182. 'Cannot verify plaintext equality proof; Associated handler has not been initialized with any public keys');
  183. }
  184. options = options || {};
  185. checkVerificationData(
  186. proof, primaryCiphertext, secondaryCiphertext, options);
  187. var data = options.data;
  188. if (typeof data === 'undefined') {
  189. data = DEFAULT_AUXILIARY_DATA;
  190. }
  191. var phiFunction =
  192. newPhiFunction(group, primaryPublicKey_, secondaryPublicKey_);
  193. var publicValues =
  194. generatePublicValues(primaryCiphertext, secondaryCiphertext);
  195. if (!measureProgress_) {
  196. return verifier_.verify(group, proof, phiFunction, publicValues, data);
  197. } else {
  198. var verified = verifier_.verify(
  199. group, proof, phiFunction, publicValues, data,
  200. newProgressMeter(primaryPublicKey_));
  201. measureProgress_ = false;
  202. return verified;
  203. }
  204. };
  205. /**
  206. * Indicates that a progress meter is to be used for the next plaintext
  207. * equality zero-knowledge proof of knowledge operation to be performed.
  208. *
  209. * @function measureProgress
  210. * @memberof PlaintextEqualityProofHandler
  211. * @param {function}
  212. * callback The callback function for the progress meter.
  213. * @param {Object}
  214. * [options] An object containing optional arguments.
  215. * @param {number}
  216. * [minCheckInterval=10] The minimum interval used to check
  217. * progress, as a percentage of the expected final progress
  218. * value.
  219. * @returns {PlaintextEqualityProofHandler} A reference to this object, to
  220. * facilitate method chaining.
  221. * @throws {Error}
  222. * If the input data validation fails.
  223. */
  224. this.measureProgress = function(callback, minCheckInterval) {
  225. validator.checkProgressMeterData(
  226. callback, minCheckInterval, 'Plaintext equality zero-knowledge proof');
  227. progressCallback_ = callback;
  228. progressPercentMinCheckInterval_ = minCheckInterval;
  229. measureProgress_ = true;
  230. return this;
  231. };
  232. function generatePublicValues(primaryCiphertext, secondaryCiphertext) {
  233. var quotientElements = mathGroupHandler_.divideElements(
  234. primaryCiphertext.phis, secondaryCiphertext.phis);
  235. return [primaryCiphertext.gamma].concat(
  236. [secondaryCiphertext.gamma], quotientElements);
  237. }
  238. function newPhiFunction(group, primaryPublicKey, secondaryPublicKey) {
  239. var numOutputs = primaryPublicKey.elements.length + 2;
  240. var computationRules = generateComputationRules(numOutputs);
  241. var baseElements =
  242. generateBaseElements(group, primaryPublicKey, secondaryPublicKey);
  243. return new PhiFunction(
  244. NUM_PHI_INPUTS, numOutputs, computationRules, baseElements);
  245. }
  246. function generateComputationRules(numOutputs) {
  247. var rules = [];
  248. rules[0] = [];
  249. rules[0][0] = [];
  250. rules[0][0].push(1);
  251. rules[0][0].push(1);
  252. rules[1] = [];
  253. rules[1][0] = [];
  254. rules[1][0].push(1);
  255. rules[1][0].push(2);
  256. for (var i = 2; i < numOutputs; i++) {
  257. rules[i] = [];
  258. rules[i][0] = [];
  259. rules[i][0].push(i);
  260. rules[i][0].push(1);
  261. rules[i][1] = [];
  262. rules[i][1].push(i + (numOutputs - 2));
  263. rules[i][1].push(2);
  264. }
  265. return rules;
  266. }
  267. function generateBaseElements(group, primaryPublicKey, secondaryPublicKey) {
  268. return [group.generator].concat(
  269. primaryPublicKey.elements, invertPublicKeyElements(secondaryPublicKey));
  270. }
  271. function invertPublicKeyElements(publicKey) {
  272. var elements = publicKey.elements;
  273. var invertedElements = [];
  274. for (var i = 0; i < elements.length; i++) {
  275. invertedElements.push(elements[i].invert());
  276. }
  277. return invertedElements;
  278. }
  279. function newProgressMeter(primaryPublicKey) {
  280. var progressMax = (primaryPublicKey.elements.length * 2) + 2;
  281. return new ProgressMeter(
  282. progressMax, progressCallback_, progressPercentMinCheckInterval_);
  283. }
  284. function checkInitData(primaryPublicKey, secondaryPublicKey) {
  285. validator.checkElGamalPublicKey(
  286. primaryPublicKey,
  287. 'Primary ElGamal public key for plaintext equality proof handler initialization',
  288. group);
  289. validator.checkElGamalPublicKey(
  290. secondaryPublicKey,
  291. 'Secondary ElGamal public key for plaintext equality proof handler initialization',
  292. group);
  293. validator.checkArrayLengthsEqual(
  294. primaryPublicKey.elements,
  295. 'primary ElGamal public key Zp group elements for plaintext equality proof handler initialization',
  296. secondaryPublicKey.elements,
  297. 'secondary ElGamal public key Zp group elements');
  298. }
  299. function checkGenerationData(
  300. primarySecret, secondarySecret, primaryCiphertext, secondaryCiphertext,
  301. options) {
  302. validator.checkExponent(
  303. primarySecret,
  304. 'Primary secret exponent for plaintext equality proof generation',
  305. group.q);
  306. validator.checkElGamalEncryptedElements(
  307. primaryCiphertext,
  308. 'Primary ciphertext ElGamal encrypted elements for plaintext equality proof generation',
  309. group);
  310. validator.checkExponent(
  311. secondarySecret,
  312. 'Secondary secret exponent for plaintext equality proof generation',
  313. group.q);
  314. validator.checkElGamalEncryptedElements(
  315. secondaryCiphertext,
  316. 'Secondary ciphertext ElGamal encrypted elements for plaintext equality proof generation',
  317. group);
  318. validator.checkArrayLengthsEqual(
  319. primaryCiphertext.phis,
  320. 'primary ciphertext phi Zp group elements for plaintext equality proof generation',
  321. primaryPublicKey_.elements,
  322. 'primary ElGamal public key Zp group elements');
  323. validator.checkArrayLengthsEqual(
  324. secondaryCiphertext.phis,
  325. 'secondary ciphertext ElGamal phi Zp group elements for plaintext equality proof generation',
  326. secondaryPublicKey_.elements,
  327. 'secondary ElGamal public key Zp group elements');
  328. if (typeof options.data !== 'undefined') {
  329. if (typeof options.data !== 'string') {
  330. validator.checkIsInstanceOf(
  331. options.data, Uint8Array, 'Uint8Array',
  332. 'Non-string auxiliary data for plaintext equality proof generation');
  333. }
  334. }
  335. if (options.preComputation) {
  336. validator.checkIsInstanceOf(
  337. options.preComputation, ZeroKnowledgeProofPreComputation,
  338. 'ZeroKnowledgeProofPreComputation',
  339. 'Pre-computation for plaintext equality proof generation');
  340. }
  341. }
  342. function checkVerificationData(
  343. proof, primaryCiphertext, secondaryCiphertext, options) {
  344. validator.checkIsInstanceOf(
  345. proof, ZeroKnowledgeProof, 'ZeroKnowledgeProof',
  346. 'Plaintext equality zero-knowledge proof');
  347. validator.checkElGamalEncryptedElements(
  348. primaryCiphertext,
  349. 'Primary ciphertext ElGamal encrypted elements for plaintext equality proof verification',
  350. group);
  351. validator.checkElGamalEncryptedElements(
  352. secondaryCiphertext,
  353. 'Secondary ciphertext ElGamal encrypted elements for plaintext equality proof verification',
  354. group);
  355. validator.checkArrayLengthsEqual(
  356. primaryCiphertext.phis,
  357. 'primary ciphertext phi Zp group elements for plaintext equality proof verification',
  358. primaryPublicKey_.elements,
  359. 'primary ElGamal public key Zp group elements');
  360. validator.checkArrayLengthsEqual(
  361. secondaryCiphertext.phis,
  362. 'secondary ciphertext ElGamal phi Zp group elements for plaintext equality proof verification',
  363. secondaryPublicKey_.elements,
  364. 'secondary ElGamal public key Zp group elements');
  365. if (typeof options.data !== 'undefined') {
  366. if (typeof options.data !== 'string') {
  367. validator.checkIsInstanceOf(
  368. options.data, Uint8Array, 'Uint8Array',
  369. 'Non-string auxiliary data for plaintext equality proof verification');
  370. }
  371. }
  372. }
  373. }