/*
* 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 HashGenerator = require('./hash-generator');
var codec = require('scytl-codec');
module.exports = ZeroKnowledgeProofVerifier;
/**
* @class ZeroKnowledgeProofVerifier
* @classdesc Encapsulates a zero-knowledge proof of knowledge verifier that
* uses the PHI function defined in Maurer's unified framework.
* @private
* @param {MessageDigestService}
* messageDigestService The message digest service.
* @param {MathematicalService}
* mathService The mathematical service.
*/
function ZeroKnowledgeProofVerifier(messageDigestService, mathService) {
/**
* Verifies a zero-knowledge proof of knowledge.
*
* @function verify
* @memberof ZeroKnowledgeProofVerifier
* @param {ZpSubgroup}
* group The Zp subgroup to which all exponents and Zp group
* elements required for the proof verification are associated or
* belong, respectively.
* @param {ZeroKnowledgeProof}
* proof The zero-knowledge proof of knowledge to verify.
* @param {PhiFunction}
* phiFunction The PHI function used for the verification.
* @param {ZpGroupElement[]}
* publicValues The public values used to verify the proof.
* @param {Uint8Array|string}
* data Auxiliary data.
* @param {ZpGroupElement[]|ProgressMeter}
* [elementsOrProgressMeter=Not used] The possible Zp group
* elements used to generate the ciphertext of an OR
* zero-knowledge proof OR a progress meter.
* @param {number}
* [progressMeter=Not used] A parameter that can be used as a
* progress meter, for the case in which the previous parameter
* was of type ZpGroupElements[]
.
* @returns {boolean} true
if the zero-knowledge proof of
* knowledge was verified, false
otherwise.
*/
this.verify = function(
group, proof, phiFunction, publicValues, data, elementsOrProgressMeter,
progressMeter) {
var elements;
if (!isArray(elementsOrProgressMeter)) {
progressMeter = elementsOrProgressMeter;
} else {
elements = elementsOrProgressMeter;
}
var proofHash = proof.hash;
var phiOutputs;
var generatedValues;
if (typeof elements === 'undefined') {
phiOutputs = phiFunction.calculate(proof.values, progressMeter);
generatedValues = generateValues(publicValues, phiOutputs, proofHash);
} else {
var numElements = elements.length;
var proofValues = proof.values;
var challenges = proofValues.slice(0, numElements);
var phiInputs = proofValues.slice(numElements, numElements * 2);
phiOutputs = phiFunction.calculate(phiInputs, progressMeter);
generatedValues =
generateValues(publicValues, phiOutputs, elements, challenges);
}
var calculatedHash =
generateHash(group, publicValues, generatedValues, data);
return proofHash.equals(calculatedHash);
};
function generateValues(
publicValues, phiOutputs, proofHashOrElements, challenges) {
var proofHash;
var elements;
if (!isArray(proofHashOrElements)) {
proofHash = proofHashOrElements;
} else {
elements = proofHashOrElements;
}
var generatedValues = [];
if (typeof proofHash !== 'undefined') {
for (var i = 0; i < phiOutputs.length; i++) {
generatedValues.push(phiOutputs[i].multiply(
publicValues[i].exponentiate(proofHash.negate())));
}
} else {
var gamma = publicValues[0];
var phi = publicValues[1];
for (var j = 0; j < challenges.length; j++) {
var offset = 2 * j;
var gammaFactor = gamma.invert().exponentiate(challenges[j]);
var phiFactor =
(phi.invert().multiply(elements[j])).exponentiate(challenges[j]);
generatedValues.push(phiOutputs[offset].multiply(gammaFactor));
generatedValues.push(phiOutputs[offset + 1].multiply(phiFactor));
}
}
return generatedValues;
}
function generateHash(group, publicValues, generatedValues, data) {
var hashGenerator = new HashGenerator(messageDigestService);
var hashBytes = hashGenerator.generate(publicValues, generatedValues, data);
var hashByteArray = Array.apply([], hashBytes);
hashByteArray.unshift(0);
var value = codec.bytesToBigInteger(hashBytes);
return mathService.newExponent(group.q, value);
}
function isArray(a) {
return (!!a) && (a.constructor === Array);
}
}