or-proof-handler.js 13 KB

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