/*
 * 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 SchnorrProofTestData = require('./data/schnorr-proof-data');
var cryptoPolicy = require('scytl-cryptopolicy');
var zkProof = require('../lib/index');
var messageDigest = require('scytl-messagedigest');
var mathematical = require('scytl-mathematical');
var secureRandom = require('scytl-securerandom');
var codec = require('scytl-codec');

describe('The zero-knowledge proof module should be able to ...', function() {
  var proofService_;
  var group_;
  var secret_;
  var anotherSecret_;
  var gamma_;
  var gammaForAnotherSecret_;
  var data_;
  var otherData_;
  var voterId_;
  var electionId_;
  var otherVoterId_;
  var otherElectionId_;
  var proofHandler_;
  var proof_;
  var preComputation_;

  beforeAll(function() {
    proofService_ = zkProof.newService();

    var testData = new SchnorrProofTestData();
    group_ = testData.getGroup();
    secret_ = testData.getSecret();
    anotherSecret_ = testData.getAnotherSecret();
    gamma_ = testData.getGamma();
    gammaForAnotherSecret_ = testData.getGammaForAnotherSecret();
    data_ = testData.getStringData();
    otherData_ = testData.getOtherStringData();
    voterId_ = testData.getVoterId();
    electionId_ = testData.getElectionId();
    otherVoterId_ = voterId_ + 'a';
    otherElectionId_ = electionId_ + 'b';
    proofHandler_ = proofService_.newSchnorrProofHandler(group_);
    proof_ = proofHandler_.generate(secret_, gamma_);
    preComputation_ = proofHandler_.preCompute();
  });

  describe('create a zero-knowledge proof service that should be able to ..', function() {
    describe('create a Schnorr proof handler that should be able to', function() {
      it('generate and verify a Schnorr proof', function() {
        var verified = proofHandler_.verify(proof_, gamma_);
        expect(verified).toBeTruthy();
      });

      it('generate and verify a Schnorr proof, using a specified cryptographic policy',
         function() {
           var policy = cryptoPolicy.newInstance();
           policy.proofs.messageDigest.algorithm =
               cryptoPolicy.options.proofs.messageDigest.algorithm.SHA512_224;

           var proofService = zkProof.newService({policy: policy});
           var proofHandler = proofService.newSchnorrProofHandler(group_);

           proof_ = proofHandler.generate(secret_, gamma_);

           var verified = proofHandler.verify(proof_, gamma_);
           expect(verified).toBeTruthy();
         });

      it('generate and verify a Schnorr proof, using a specified message digest service',
         function() {
           var policy = cryptoPolicy.newInstance();
           policy.proofs.messageDigest.algorithm =
               cryptoPolicy.options.proofs.messageDigest.algorithm.SHA512_224;
           var messageDigestService =
               messageDigest.newService({policy: policy});

           var proofService =
               zkProof.newService({messageDigestService: messageDigestService});
           var proofHandler = proofService.newSchnorrProofHandler(group_);

           proof_ = proofHandler.generate(secret_, gamma_);

           var verified = proofHandler.verify(proof_, gamma_);
           expect(verified).toBeTruthy();
         });

      it('generate and verify a Schnorr proof, using a specified secure random service object',
         function() {
           var proofService = zkProof.newService(
               {secureRandomService: secureRandom.newService()});
           var proofHandler = proofService.newSchnorrProofHandler(group_);

           proof_ = proofHandler.generate(secret_, gamma_);

           var verified = proofHandler.verify(proof_, gamma_);
           expect(verified).toBeTruthy();
         });

      it('generate and verify a Schnorr proof, using a specified mathematical service object',
         function() {
           var proofService = zkProof.newService(
               {mathematicalService: mathematical.newService()});
           var proofHandler = proofService.newSchnorrProofHandler(group_);

           proof_ = proofHandler.generate(secret_, gamma_);

           var verified = proofHandler.verify(proof_, gamma_);
           expect(verified).toBeTruthy();
         });

      it('generate and verify a Schnorr proof, using provided auxiliary data',
         function() {
           // Data as string.
           var proof = proofHandler_.generate(secret_, gamma_, {data: data_});
           var verified = proofHandler_.verify(proof, gamma_, {data: data_});
           expect(verified).toBeTruthy();

           // Data as bytes.
           proof = proofHandler_.generate(
               secret_, gamma_, {data: codec.utf8Encode(data_)});
           verified = proofHandler_.verify(
               proof, gamma_, {data: codec.utf8Encode(data_)});
           expect(verified).toBeTruthy();
         });

      it('generate and verify a Schnorr proof, using a provided voter and election ID',
         function() {
           var proof = proofHandler_.generate(
               secret_, gamma_, {voterId: voterId_, electionId: electionId_});

           var verified = proofHandler_.verify(
               proof, gamma_, {voterId: voterId_, electionId: electionId_});
           expect(verified).toBeTruthy();
         });

      it('generate and verify a Schnorr proof, using a pre-computation',
         function() {
           expect(preComputation_).toBeDefined();

           var proof = proofHandler_.generate(
               secret_, gamma_, {preComputation: preComputation_});

           var verified = proofHandler_.verify(proof, gamma_);
           expect(verified).toBeTruthy();
         });

      it('generate and verify a Schnorr proof, using empty string auxiliary data',
         function() {
           var proof = proofHandler_.generate(secret_, gamma_, {data: ''});

           var verified = proofHandler_.verify(proof, gamma_, {data: ''});
           expect(verified).toBeTruthy();
         });

      it('pre-compute, generate and verify a Schnorr proof, using method chaining',
         function() {
           var preComputation =
               proofService_.newSchnorrProofHandler(group_).preCompute();

           var proof = proofService_.newSchnorrProofHandler(group_).generate(
               secret_, gamma_, {data: data_, preComputation: preComputation});

           var verified = proofService_.newSchnorrProofHandler(group_).verify(
               proof, gamma_, {data: data_});
           expect(verified).toBeTruthy();
         });

      it('create a new Schnorr ZeroKnowledgeProof object', function() {
        var hash = proof_.hash;
        var values = proof_.values;
        var proof = proofService_.newProof(hash, values);

        var verified = proofHandler_.verify(proof, gamma_);
        expect(verified).toBeTruthy();
      });

      it('create a new Schnorr ZeroKnowledgeProofPreComputation object',
         function() {
           var exponents = preComputation_.exponents;
           var phiOutputs = preComputation_.phiOutputs;
           var preComputation =
               proofService_.newPreComputation(exponents, phiOutputs);

           var proof = proofHandler_.generate(
               secret_, gamma_, {preComputation: preComputation});

           var verified = proofHandler_.verify(proof, gamma_);
           expect(verified).toBeTruthy();
         });

      it('serialize and deserialize a Schnorr proof', function() {
        var proofJson = proof_.toJson();

        var proof = proofService_.newProof(proofJson);

        var verified = proofHandler_.verify(proof, gamma_);
        expect(verified).toBeTruthy();
      });

      it('serialize and deserialize a Schnorr proof pre-computation',
         function() {
           var preComputationJson = preComputation_.toJson();

           var preComputation =
               proofService_.newPreComputation(preComputationJson);

           var proof = proofHandler_.generate(
               secret_, gamma_, {preComputation: preComputation});

           var verified = proofHandler_.verify(proof, gamma_);
           expect(verified).toBeTruthy();
         });

      it('fail to verify a Schnorr proof that was generated with a secret not used to generate the gamma element',
         function() {
           var proof = proofHandler_.generate(anotherSecret_, gamma_);

           var verified = proofHandler_.verify(proof, gamma_);
           expect(verified).toBeFalsy();
         });

      it('fail to verify a Schnorr proof when using a gamma element generated from a secret not used to generate the proof',
         function() {
           var verified = proofHandler_.verify(proof_, gammaForAnotherSecret_);
           expect(verified).toBeFalsy();
         });

      it('fail to verify a Schnorr proof when using auxiliary data not used to generate the proof',
         function() {
           var proof = proofHandler_.generate(secret_, gamma_, {data: data_});

           var verified =
               proofHandler_.verify(proof, gamma_, {data: otherData_});
           expect(verified).toBeFalsy();
         });

      it('fail to verify a Schnorr proof when using a voter ID not used to generate the proof',
         function() {
           var proof = proofHandler_.generate(
               secret_, gamma_, {voterId: voterId_, electionId: electionId_});

           var verified = proofHandler_.verify(
               proof, gamma_,
               {voterId: otherVoterId_, electionId: electionId_});
           expect(verified).toBeFalsy();
         });

      it('fail to verify a Schnorr proof when using an election ID not used to generate the proof',
         function() {
           var proof = proofHandler_.generate(
               secret_, gamma_, {voterId: voterId_, electionId: electionId_});

           var verified = proofHandler_.verify(
               proof, gamma_,
               {voterId: voterId_, electionId: otherElectionId_});
           expect(verified).toBeFalsy();
         });
    });
  });
});
