/* * 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 ZeroKnowledgeProof = require('./proof.js'); var ZeroKnowledgeProofPreComputation = require('./pre-computation.js'); var HashGenerator = require('./hash-generator'); var codec = require('scytl-codec'); module.exports = ZeroKnowledgeProofProver; /** * @class ZeroKnowledgeProofProver * @classdesc Encapsulates a zero-knowledge proof of knowledge prover that uses * the PHI function defined in Maurer's unified framework. * @private * @param {MessageDigestService} * messageDigestService The message digest service to use. * @param {MathematicalService} * mathService The mathematical service to use. */ function ZeroKnowledgeProofProver(messageDigestService, mathService) { var mathRandomGenerator_ = mathService.newRandomGenerator(); /** * Generates a zero-knowledge proof of knowledge. * * @function prove * @memberof ZeroKnowledgeProofProver * @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 {Exponent[]} * privateValues The private values used to generate the proof. * @param {ZpGroupElement[]} * publicValues The public values used to generate the proof. * @param {Uint8Array|string} * data Auxiliary data. * @param {ZeroKnowledgeProofPreComputation} * preComputation The zero-knowledge proof of knowledge * pre-computation. * @param {ZpGroupElement[]} * [elements] A parameter to be used only for the generation of * OR zero-knowledge proofs of knowledge. It consists of the * possible Zp group elements used to generate the ciphertext. * @param {number} * [index] A parameter that is only to be used if the previous * parameter was used. It is the secret index of the Zp group * element that was used to generate the ciphertext of an OR * zero-knowledge proof of knowledge. * @returns {ZeroKnowledgeProof} The generated zero-knowledge proof of * knowledge. */ this.prove = function( group, privateValues, publicValues, data, preComputation, elements, index) { if (typeof elements !== 'undefined') { preComputation = updatePreComputation( group, preComputation, publicValues, elements, index); } var hash = generateHash(group, publicValues, preComputation.phiOutputs, data); var proofValues = generateProofValues( group, privateValues, hash, preComputation.exponents, index); return new ZeroKnowledgeProof(hash, proofValues); }; /** * Pre-computes a Schnorr zero-knowledge proof of knowledge. IMPORTANT: The * same pre-computed values must not be used twice. * * @function preComputeSchnorrProof * @memberof ZeroKnowledgeProofProver * @param {ZpSubgroup} * group The Zp subgroup to which all exponents and Zp group * elements required for the pre-computation are associated or * belong, respectively. * @param {PhiFunction} * phiFunction The PHI function used for the pre-computation. * @param {ProgressMeter} * [progressMeter=Not used] A progress meter. * @returns {ZeroKnowledgeProofPreComputation} The Schnorr zero-knowledge * proof of knowledge pre-computation. */ this.preCompute = function(group, phiFunction, progressMeter) { var exponents = generateExponents(group, phiFunction.numInputs); var phiOutputs = phiFunction.calculate(exponents, progressMeter); return new ZeroKnowledgeProofPreComputation(exponents, phiOutputs); }; function generateHash(group, publicValues, phiOutputs, data) { var hashGenerator = new HashGenerator(messageDigestService); var hashBytes = hashGenerator.generate(publicValues, phiOutputs, data); var hashByteArray = Array.apply([], hashBytes); hashByteArray.unshift(0); var value = codec.bytesToBigInteger(hashBytes); return mathService.newExponent(group.q, value); } function generateProofValues(group, privateValues, hash, exponents, index) { var proofValues = []; var proofValue; if (typeof index === 'undefined') { for (var i = 0; i < privateValues.length; i++) { proofValue = exponents[i].value.add(privateValues[i].value.multiply(hash.value)); proofValues.push(mathService.newExponent(group.q, proofValue)); } } else { var numElements = exponents.length / 2; var updatedHash = hash; for (var j = 0; j < numElements; j++) { if (j === index) { for (var k = 0; k < numElements; k++) { if (k !== index) { updatedHash = updatedHash.subtract(exponents[k]); } } proofValues.push(updatedHash); } else { proofValues.push(exponents[j]); } } for (var m = numElements; m < (numElements * 2); m++) { if (m === (numElements + index)) { proofValue = exponents[m].value.add( privateValues[0].value.multiply(updatedHash.value)); proofValues.push(mathService.newExponent(group.q, proofValue)); } else { proofValues.push(exponents[m]); } } } return proofValues; } function updatePreComputation( group, preComputation, publicValues, elements, index) { var gamma = publicValues[0]; var phi = publicValues[1]; var numElements = elements.length; var challenges = generateExponents(group, numElements); var updatedExponents = challenges.concat(preComputation.exponents); var updatedPhiOutputs = []; var phiOutputs = preComputation.phiOutputs; for (var j = 0; j < numElements; j++) { var offset = 2 * j; if (j === index) { updatedPhiOutputs.push(phiOutputs[offset]); updatedPhiOutputs.push(phiOutputs[offset + 1]); } else { var gammaFactor = gamma.invert().exponentiate(challenges[j]); var phiFactor = (phi.invert().multiply(elements[j])).exponentiate(challenges[j]); updatedPhiOutputs.push(phiOutputs[offset].multiply(gammaFactor)); updatedPhiOutputs.push(phiOutputs[offset + 1].multiply(phiFactor)); } } return new ZeroKnowledgeProofPreComputation( updatedExponents, updatedPhiOutputs); } function generateExponents(group, numExponents) { var exponents = []; for (var i = 0; i < numExponents; i++) { exponents.push(mathRandomGenerator_.nextExponent(group)); } return exponents; } }