/* * Copyright 2018 Scytl Secure Electronic Voting SA * * All rights reserved * * See our extended copyright notice in *file 'Copyright.txt' which is part of this source code package */ /* jshint node:true */ 'use strict'; var ZeroKnowledgeProofProver = require('../prover'); var ZeroKnowledgeProofVerifier = require('../verifier'); var ZeroKnowledgeProof = require('../proof'); var ZeroKnowledgeProofPreComputation = require('../pre-computation'); var PhiFunction = require('../phi-function'); var ProgressMeter = require('../progress-meter'); var validator = require('../input-validator'); module.exports = PlaintextExponentEqualityProofHandler; /** * @class PlaintextExponentEqualityProofHandler * @classdesc Encapsulates a handler for the plaintext exponent equality * zero-knowledge proof of knowledge generation, pre-computation and * verification processes. To instantiate this object, use the method * {@link * ZeroKnowledgeProofService.newPlaintextExponentEqualityProofHandler}. * @param {ZpSubgroup} * group The Zp subgroup to which all exponents and Zp group elements * required for the proof generation are associated or belong, * respectively. * @param {MessageDigestService} * messageDigestService The message digest service. * @param {MathematicalService} * mathematicalService The mathematical service. */ function PlaintextExponentEqualityProofHandler( group, messageDigestService, mathematicalService) { var NUM_PHI_INPUTS = 2; var DEFAULT_AUXILIARY_DATA = 'PlaintextExponentEqualityProof'; var prover_ = new ZeroKnowledgeProofProver(messageDigestService, mathematicalService); var verifier_ = new ZeroKnowledgeProofVerifier(messageDigestService, mathematicalService); var progressCallback_; var progressPercentMinCheckInterval_; var measureProgress_ = false; var baseElements_; /** * Initializes the handler with the provided base elements. * * @function init * @memberof PlaintextExponentEqualityProofHandler * @param {ZpGroupElement[]} * baseElements The base elements. * @returns {PlaintextExponentEqualityProofHandler} A reference to this * object, to facilitate method chaining. * @throws {Error} * If the input data validation fails. */ this.init = function(baseElements) { validator.checkZpGroupElements( baseElements, 'Base elements for plaintext exponent eqaulity proof handler initialization', group); baseElements_ = baseElements; return this; }; /** * Generates a plaintext exponent equality zero-knowledge proof of * knowledge. Before using this method, the handler must have been * initialized with base elements, via the method * {@link PlaintextExponentEqualityProofHandler.init}. * * @function generate * @memberof PlaintextExponentEqualityProofHandler * @param {Exponent} * firstSecret The "ephemeral key" used to generate the gamma * element of the ciphertext, the knowledge of which must be * proven. * @param {Exponent} * secondSecret The "message key" used to generate the phi * elements of the ciphertext, the knowledge of which must be * proven. * @param {ElGamalEncryptedElements} * ciphertext The ciphertext. * @param {Object} * [options] An object containing optional arguments. * @param {Uint8Array|string} * [options.data='PlaintextExponentEqualityProof'] Auxiliary * data. * @param {ZeroKnowledgeProofPreComputation} * [options.preComputation=Generated internally] A * pre-computation of the plaintext exponent equality * zero-knowledge proof of knowledge. * @returns {ZeroKnowledgeProof} The plaintext exponent equality * zero-knowledge proof of knowledge. * @throws {Error} * If the input data validation fails. */ this.generate = function(firstSecret, secondSecret, ciphertext, options) { if (typeof baseElements_ === 'undefined') { throw new Error( 'Cannot generate plaintext exponent equality proof; Associated handler has not been initialized with any base elements'); } options = options || {}; checkGenerationData(firstSecret, secondSecret, ciphertext, options); var data = options.data; if (typeof data === 'undefined') { data = DEFAULT_AUXILIARY_DATA; } var preComputation = options.preComputation; var privateValues = [firstSecret, secondSecret]; var publicValues = generatePublicValues(ciphertext); if (typeof preComputation === 'undefined') { preComputation = this.preCompute(); } return prover_.prove( group, privateValues, publicValues, data, preComputation); }; /** * Pre-computes a plaintext exponent equality zero-knowledge proof of * knowledge. IMPORTANT: The same pre-computed values must not be used * twice. Before using this method, the handler must have been initialized * with base elements, via the method {@link * PlaintextExponentEqualityProofHandler.init}. * * @function preCompute * @memberof PlaintextExponentEqualityProofHandler * @returns {ZeroKnowledgeProofPreComputation} The plaintext exponent * equality zero-knowledge proof of knowledge pre-computation. */ this.preCompute = function() { if (typeof baseElements_ === 'undefined') { throw new Error( 'Cannot pre-compute plaintext exponent equality proof; Associated handler has not been initialized with any base elements'); } var phiFunction = newPhiFunction(group, baseElements_); if (!measureProgress_) { return prover_.preCompute(group, phiFunction); } else { var preComputation = prover_.preCompute( group, phiFunction, newProgressMeter(baseElements_)); measureProgress_ = false; return preComputation; } }; /** * Verifies a plaintext exponent equality zero-knowledge proof of knowledge. * Before using this method, the handler must have been initialized with * base elements, via the method {@link * PlaintextExponentEqualityProofHandler.init}. * * @function verify * @memberof PlaintextExponentEqualityProofHandler * @param {Proof} * proof The plaintext exponent equality zero-knowledge proof of * knowledge to verify. * @param {ElGamalEncryptedElements} * ciphertext The ciphertext. * @param {Object} * [options] An object containing optional arguments. * @param {Uint8Array|string} * [options.data='PlaintextExponentEqualityProof'] Auxiliary * data. It must be the same as that used to generate the proof. * @returns {boolean} true if the plaintext exponent equality * zero-knowledge proof of knowledge was verified, * false otherwise. * @throws {Error} * If the input data validation fails. */ this.verify = function(proof, ciphertext, options) { if (typeof baseElements_ === 'undefined') { throw new Error( 'Cannot verify plaintext exponent equality proof; Associated handler has not been initialized with any base elements'); } options = options || {}; checkVerificationData(proof, ciphertext, options); var data = options.data; if (typeof data === 'undefined') { data = DEFAULT_AUXILIARY_DATA; } var phiFunction = newPhiFunction(group, baseElements_); var publicValues = generatePublicValues(ciphertext); if (!measureProgress_) { return verifier_.verify(group, proof, phiFunction, publicValues, data); } else { var verified = verifier_.verify( group, proof, phiFunction, publicValues, data, newProgressMeter(baseElements_)); measureProgress_ = false; return verified; } }; /** * Indicates that a progress meter is to be used for the next plaintext * exponent equality zero-knowledge proof of knowledge operation to be * performed. * * @function measureProgress * @memberof PlaintextExponentEqualityProofHandler * @param {function} * callback The callback function for the progress meter. * @param {Object} * [options] An object containing optional arguments. * @param {number} * [minCheckInterval=10] The minimum interval used to check * progress, as a percentage of the expected final progress * value. * @returns {PlaintextExponentEqualityProofHandler} A reference to this * object, to facilitate method chaining. * @throws {Error} * If the input data validation fails. */ this.measureProgress = function(callback, minCheckInterval) { validator.checkProgressMeterData( callback, minCheckInterval, 'Plaintext exponent equality zero-knowledge proof'); progressCallback_ = callback; progressPercentMinCheckInterval_ = minCheckInterval; measureProgress_ = true; return this; }; function generatePublicValues(ciphertext) { return [ciphertext.gamma].concat(ciphertext.phis); } function newPhiFunction(group, baseElements) { var numOutputs = ((baseElements.length - 1) / 2) + 1; var computationRules = generateComputationRules(numOutputs); return new PhiFunction( NUM_PHI_INPUTS, numOutputs, computationRules, baseElements); } function generateComputationRules(numOutputs) { var rules = []; rules[0] = []; rules[0][0] = []; rules[0][0].push(1); rules[0][0].push(1); for (var i = 1; i < numOutputs; i++) { rules[i] = []; rules[i][0] = []; rules[i][1] = []; rules[i][0].push(i + 1); rules[i][0].push(1); rules[i][1].push(i + numOutputs); rules[i][1].push(2); } return rules; } function newProgressMeter(baseElements) { var progressMax = baseElements.length; return new ProgressMeter( progressMax, progressCallback_, progressPercentMinCheckInterval_); } function checkGenerationData(firstSecret, secondSecret, ciphertext, options) { validator.checkExponent( firstSecret, 'First secret exponent for plaintext exponent equality proof generation', group.q); validator.checkExponent( secondSecret, 'Second secret exponent for plaintext exponent equality proof generation', group.q); validator.checkElGamalEncryptedElements( ciphertext, 'Ciphertext ElGamal encrypted elements for plaintext exponent equality proof generation', group); checkCiphertextLength(ciphertext, baseElements_); if (typeof options.data !== 'undefined' && typeof options.data !== 'string') { validator.checkIsInstanceOf( options.data, Uint8Array, 'Uint8Array', 'Non-string auxiliary data for plaintext exponent equality proof generation'); } if (typeof options.preComputation !== 'undefined') { validator.checkIsInstanceOf( options.preComputation, ZeroKnowledgeProofPreComputation, 'ZeroKnowledgeProofPreComputation', 'Pre-computation for plaintext exponent equality proof generation'); } } function checkVerificationData(proof, ciphertext, options) { validator.checkIsInstanceOf( proof, ZeroKnowledgeProof, 'ZeroKnowledgeProof', 'Plaintext exponent equality zero-knowledge proof'); validator.checkElGamalEncryptedElements( ciphertext, 'Ciphertext ElGamal encrypted elements for plaintext exponent equality proof verification', group); checkCiphertextLength(ciphertext, baseElements_); if (typeof options.data !== 'undefined' && typeof options.data !== 'string') { validator.checkIsInstanceOf( options.data, Uint8Array, 'Uint8Array', 'Non-string auxiliary data for plaintext exponent equality proof verification'); } } function checkCiphertextLength(ciphertext, baseElements) { var numPhisFound = ciphertext.phis.length; var numPhisExpected = (baseElements.length - 1) / 2; if (numPhisFound !== numPhisExpected) { throw new Error( 'Expected number of ElGamal phi elements in ciphertext used for plaintext exponent equality proof opertion to be: ' + numPhisExpected + ' ; Found: ' + numPhisFound); } } }