/* * 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 = ExponentiationProofHandler; /** * @class ExponentiationProofHandler * @classdesc Encapsulates a handler for the exponentiation zero-knowledge proof * of knowledge generation, pre-computation and verification * processes. To instantiate this object, use the method {@link * ZeroKnowledgeProofService.newExponentiationProofHandler}. * @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 ExponentiationProofHandler( group, messageDigestService, mathematicalService) { var NUM_PHI_INPUTS = 1; var DEFAULT_AUXILIARY_DATA = 'ExponentiationProof'; 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 ExponentiationProofHandler * @param {ZpGroupElement[]} * baseElements The base elements. * @returns {ExponentiationProofHandler} 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 exponentiation proof handler initialization', group); baseElements_ = baseElements; return this; }; /** * Generates an exponentiation zero-knowledge proof of knowledge. Before * using this method, the handler must have been initialized with base * elements, via the method {@link ExponentiationProofHandler.init}. * * @function generate * @memberof ExponentiationProofHandler * @param {Exponent} * secret The secret exponent, the knowledge of which must be * proven. * @param {ZpGroupElement[]} * exponentiatedElements The base elements after exponentiation * with the secret exponent. * @param {Object} * [options] An object containing optional arguments. * @param {Uint8Array|string} * [options.data='ExponentiationProof'] Auxiliary data. * @param {ZeroKnowledgeProofPreComputation} * [options.preComputation=Generated internally] A * pre-computation of the exponentiation zero-knowledge proof of * knowledge. * @returns {ZeroKnowledgeProof} The exponentiation zero-knowledge proof of * knowledge. * @throws {Error} * If the input data validation fails. */ this.generate = function(secret, exponentiatedElements, options) { if (typeof baseElements_ === 'undefined') { throw new Error( 'Cannot generate exponentiation proof; Associated handler has not been initialized with any base elements'); } options = options || {}; checkGenerationData(secret, exponentiatedElements, options); var data = options.data; if (typeof data === 'undefined') { data = DEFAULT_AUXILIARY_DATA; } var preComputation = options.preComputation; var privateValues = [secret]; var publicValues = exponentiatedElements; if (typeof preComputation === 'undefined') { preComputation = this.preCompute(); } return prover_.prove( group, privateValues, publicValues, data, preComputation); }; /** * Pre-computes an exponentiation 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 ExponentiationProofHandler.init}. * * @function preCompute * @memberof ExponentiationProofHandler * @returns {ZeroKnowledgeProofPreComputation} The exponentiation * zero-knowledge proof of knowledge pre-computation. */ this.preCompute = function() { if (typeof baseElements_ === 'undefined') { throw new Error( 'Cannot pre-compute exponentiation 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 an exponentiation zero-knowledge proof of knowledge. Before * using this method, the handler must have been initialized with base * elements, via the method {@link ExponentiationProofHandler.init}. * * @function verify * @memberof ExponentiationProofHandler * @param {ZeroKnowledgeProof} * proof The exponentiation zero-knowledge proof of knowledge to * verify. * @param {ZpGroupElement[]} * exponentiatedElements The base elements after exponentiation * with the secret exponent. * @param {Object} * [options] An object containing optional arguments. * @param {Uint8Array|string} * [options.data='ExponentiationProof'] Auxiliary data. It must * be the same as that used to generate the proof. * @returns {boolean} true if the exponentiation * zero-knowledge proof of knowledge was verified, * false otherwise. * @throws {Error} * If the input data validation fails. */ this.verify = function(proof, exponentiatedElements, options) { if (typeof baseElements_ === 'undefined') { throw new Error( 'Cannot verify exponentiation proof; Associated handler has not been initialized with any base elements'); } options = options || {}; checkVerificationData(proof, exponentiatedElements, options); var data = options.data; if (typeof data === 'undefined') { data = DEFAULT_AUXILIARY_DATA; } var phiFunction = newPhiFunction(group, baseElements_); var publicValues = exponentiatedElements; 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 exponentiation * zero-knowledge proof of knowledge operation to be performed. * * @function measureProgress * @memberof ExponentiationProofHandler * @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 {ExponentiationProofHandler} 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, 'Exponentiation zero-knowledge proof'); progressCallback_ = callback; progressPercentMinCheckInterval_ = minCheckInterval; measureProgress_ = true; return this; }; function newPhiFunction(group, baseElements) { var numOutputs = baseElements.length; var computationRules = generateComputationRules(numOutputs); return new PhiFunction( NUM_PHI_INPUTS, numOutputs, computationRules, baseElements); } function generateComputationRules(numOutputs) { var rules = []; for (var i = 0; i < numOutputs; i++) { rules[i] = []; rules[i][0] = []; rules[i][0].push(i + 1); rules[i][0].push(1); } return rules; } function newProgressMeter(baseElements) { var progressMax = baseElements.length; return new ProgressMeter( progressMax, progressCallback_, progressPercentMinCheckInterval_); } function checkGenerationData(secret, exponentiatedElements, options) { validator.checkExponent( secret, 'Secret exponent for exponentiation proof generation', group.q); validator.checkZpGroupElements( exponentiatedElements, 'Exponentiated base elements for exponentiation proof generation', group); validator.checkArrayLengthsEqual( baseElements_, 'base elements for exponentiation proof generation', exponentiatedElements, 'exponentiated base elements'); if (typeof options.data !== 'undefined' && typeof options.data !== 'string') { validator.checkIsInstanceOf( options.data, Uint8Array, 'Uint8Array', 'Non-string auxiliary data for exponentiation proof generation'); } if (options.preComputation) { validator.checkIsInstanceOf( options.preComputation, ZeroKnowledgeProofPreComputation, 'ZeroKnowledgeProofPreComputation', 'Pre-computation for exponentiation proof generation'); } } function checkVerificationData(proof, exponentiatedElements, options) { validator.checkIsInstanceOf( proof, ZeroKnowledgeProof, 'ZeroKnowledgeProof', 'Exponentiation zero-knowledge proof'); validator.checkZpGroupElements( exponentiatedElements, 'Exponentiated base elements for exponentiation proof verification', group); validator.checkArrayLengthsEqual( baseElements_, 'base elements for exponentiation proof verification', exponentiatedElements, 'exponentiated base elements'); if (typeof options.data !== 'undefined' && typeof options.data !== 'string') { validator.checkIsInstanceOf( options.data, Uint8Array, 'Uint8Array', 'Non-string auxiliary data for exponentiation proof verification'); } } }