plaintext-proof-handler.js 12 KB

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