/*
 * 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
 */
/* global forge */
/* global CL */
/* global OV */
/* global TD */

describe('Proofs api', function() {

    'use strict';

    var TD = require('./mocks/testdata.json');

    it('should generate schnorr proof', function() {

        var encParms = OV.parseEncryptionParams(TD.authResponse);

        // encrypt vote

        var serializedEncrypterValues = OV.precomputeEncrypterValues(encParms);
        var encrypterValues = OV.deserializeEncrypterValues(serializedEncrypterValues);

        var keyValue = forge.jsbn.BigInteger.ONE;
        var element = new CL.commons.mathematical.ZpGroupElement(keyValue, encParms.p, encParms.q);
        var messages = [element];
        var encryptedOptions = OV.encryptOptions(messages, encParms, encrypterValues);

        // generate the proof

        var schnorr = OV.generateSchnorrProof('votingCardId', 'electionId', encParms, encryptedOptions);

        // prove it

        var verifies = (new CL.proofs.ProofsFactory()).createSchnorrProofVerifier(
            encryptedOptions.getElGamalComputationValues().getGamma(),
            'votingCardId',
            'electionId',
            schnorr,
            encParms.group).verify();
        expect(verifies).toBe(true);

    });

    // --------------------------------------------------------------

    it('should generate exponentiation proof', function() {

        var startVotingKey = TD.startVotingKey;
        var eventId = TD.eventId;
        var encParms = OV.parseEncryptionParams(TD.authResponse);
        var verificationCard = TD.authResponse.verificationCard;

        var startVotingKeyUnpacked = OV.parseStartVotingKey(startVotingKey, eventId);

        // encrypt vote
        var serializedEncrypterValues = OV.precomputeEncrypterValues(encParms);
        var encrypterValues = OV.deserializeEncrypterValues(serializedEncrypterValues);
        var keyValue = forge.jsbn.BigInteger.ONE;
        var element = new CL.commons.mathematical.ZpGroupElement(keyValue, encParms.p, encParms.q);
        var messages = [element];
        var encryptedOptions = OV.encryptOptions(messages, encParms, encrypterValues);

        var verificationCardPublicKey = JSON.parse(
            forge.util.decode64(verificationCard.signedVerificationPublicKey));
        var verificationCardSecret = OV.parseVerificationCard(
            verificationCard.verificationCardKeystore, startVotingKeyUnpacked.pin);

        // generate the proof

        var cipherTextExponentiations = OV.generateCipherTextExponentiations(
            encParms, encryptedOptions, verificationCardSecret);

        var exponentiationProof = OV.generateExponentiationProof(
            encParms, cipherTextExponentiations, verificationCardPublicKey.publicKey);

        // prove it

        var keyfactory = new CL.homomorphic.keypair.factory.KeyFactory();
        var generator = new CL.commons.mathematical.ZpGroupElement(encParms.g, encParms.p, encParms.q);
        var publicKey = keyfactory.createPublicKey(verificationCardPublicKey.publicKey).getGroupElementsArray()[0];

        var verifies = (new CL.proofs.ProofsFactory()).createExponentiationProofVerifier(
            [publicKey].concat(cipherTextExponentiations.cipherTextExponentiation), //
            [generator].concat(cipherTextExponentiations.cipherTextBases),
            exponentiationProof,
            encParms.group).verify();
        expect(verifies).toBe(true);

    });


    // --------------------------------------------------------------

    it('should generate plaintext equality proof', function() {

        var startVotingKey = TD.startVotingKey;
        var eventId = TD.eventId;
        var encParms = OV.parseEncryptionParams(TD.authResponse);
        var verificationCard = TD.authResponse.verificationCard;

        var startVotingKeyUnpacked = OV.parseStartVotingKey(startVotingKey, eventId);
        var verificationCardSecret = OV.parseVerificationCard(
            verificationCard.verificationCardKeystore, startVotingKeyUnpacked.pin);

        // encrypt vote

        var serializedEncrypterValues = OV.precomputeEncrypterValues(encParms);
        var encrypterValues = OV.deserializeEncrypterValues(serializedEncrypterValues);
        var keyValue = forge.jsbn.BigInteger.ONE;
        var element = new CL.commons.mathematical.ZpGroupElement(keyValue, encParms.p, encParms.q);
        var messages = [element];
        var encryptedOptions = OV.encryptOptions(messages, encParms, encrypterValues);

        // generate and ecrypt partial choice codes

        var partialChoiceCodes = OV.generatePartialChoiceCodes(messages, encParms, verificationCardSecret);
        var encryptedPCC = OV.encryptPartialChoiceCodes(partialChoiceCodes, encParms);

        // generate the proof

        var  cipherTextExponentiations = OV.generateCipherTextExponentiations(
            encParms, encryptedOptions, verificationCardSecret);



        var plaintextEqualityProof = OV.generatePlaintextEqualityProof(
            encParms,
            encryptedOptions,
            encryptedPCC,
            verificationCardSecret, cipherTextExponentiations);

        // prove it

        var primaryCiphertext = (function() {

            var exponentiatedVoteCiphertext = CL.commons.mathematical.groupUtils.exponentiateArrays(
                [encryptedOptions.getGamma()].concat(encryptedOptions.getPhis()), // base elements
                new CL.commons.mathematical.Exponent(encParms.q, verificationCardSecret), // exponent
                encParms.group);

            return {
                getGamma: function() {
                    return exponentiatedVoteCiphertext[0];
                },
                getPhis: function() {
                    return exponentiatedVoteCiphertext.slice(1);
                },
                getComputationalValues: function() {
                    return {
                        gamma: exponentiatedVoteCiphertext[0],
                        phis: exponentiatedVoteCiphertext.slice(1)
                    };
                }
            };
        })();

        var secondaryCipherText = (function() {

            var compressedPRCphis = CL.commons.mathematical.groupUtils.compressGroupElements(
                encParms.group,
                encryptedPCC.getPhis());

            var gamma = encryptedPCC.getGamma();

            return {
                getGamma: function() {
                    return gamma;
                },
                getPhis: function() {
                    return [compressedPRCphis];
                },
                getComputationalValues: function() {
                    return {
                        gamma: gamma,
                        phis: [compressedPRCphis]
                    };
                }
            };
        })();

        var ebPubKey = encParms.optionsEncryptionKey;
        var prcPubKey = encParms.choiceCodesEncryptionKey;

        var primaryPublicKey = ebPubKey;
        var secondaryPublicKey = (function() {

            var compressedSecondaryPublicKey = CL.commons.mathematical.groupUtils.compressGroupElements(
                encParms.group,
                prcPubKey.getGroupElementsArray());

            return {
                getGroup: function() {
                    return encParms.group;
                },
                getGroupElementsArray: function() {
                    return [compressedSecondaryPublicKey];
                }
            };
        })();

        var verifies = (new CL.proofs.ProofsFactory()).createPlaintextEqualityProofVerifier(
            primaryCiphertext,
            primaryPublicKey,
            secondaryCipherText,
            secondaryPublicKey,
            plaintextEqualityProof,
            encParms.group).verify();
        expect(verifies).toBe(true);

    });




});
