simple-plaintext-equality-proof-handler.js 15 KB

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