/*
 * 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
 */
package com.scytl.cryptolib.elgamal.factory;

import java.util.ArrayList;
import java.util.List;

import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.cryptolib.commons.utils.validations.Validate;
import com.scytl.cryptolib.elgamal.bean.ElGamalEncryptionParameters;
import com.scytl.cryptolib.elgamal.bean.ElGamalKeyPair;
import com.scytl.cryptolib.elgamal.bean.ElGamalPrivateKey;
import com.scytl.cryptolib.elgamal.bean.ElGamalPublicKey;
import com.scytl.cryptolib.elgamal.cryptoapi.CryptoAPIElGamalKeyPairGenerator;
import com.scytl.cryptolib.mathematical.groups.impl.Exponent;
import com.scytl.cryptolib.mathematical.groups.impl.ZpGroupElement;
import com.scytl.cryptolib.mathematical.groups.impl.ZpSubgroup;
import com.scytl.cryptolib.mathematical.groups.utils.GroupUtils;
import com.scytl.cryptolib.primitives.securerandom.factory.CryptoRandomInteger;

/**
 * Class for generating ElGamal key pairs (which are instances of
 * {@link ElGamalKeyPair}).
 */
public final class CryptoElGamalKeyPairGenerator implements
        CryptoAPIElGamalKeyPairGenerator {

    private final CryptoRandomInteger _cryptoRandomInteger;

    /**
     * Creates a CryptoElGamalKeyGenerator, setting the specified
     * {@link CryptoRandomInteger} as a source of random integers.
     * 
     * @param cryptoRandomInteger
     *            a generator of random integers.
     */
    CryptoElGamalKeyPairGenerator(
            final CryptoRandomInteger cryptoRandomInteger) {

        _cryptoRandomInteger = cryptoRandomInteger;
    }

    @Override
    public ElGamalKeyPair generateKeys(
            final ElGamalEncryptionParameters encryptionParameters,
            final int length) throws GeneralCryptoLibException {

        Validate.notNull(encryptionParameters,
            "Mathematical group parameters object");
        Validate.isPositive(length,
            "Length of ElGamal key pair to generate");

        ZpSubgroup group = (ZpSubgroup) encryptionParameters.getGroup();
        ZpGroupElement generator = group.getGenerator();

        // Lists to store private and public keys
        List<Exponent> privateKeys = new ArrayList<Exponent>();
        List<ZpGroupElement> publicKeys = new ArrayList<ZpGroupElement>();

        // Generate the public and the private keys (generate one of
        // each of them during each iteration of the loop
        Exponent randomExponent;
        GroupUtils utils = new GroupUtils();
        for (int i = 0; i < length; i++) {

            // Construct a new random exponent, this value
            // will be used as a private key
            randomExponent =
                utils.getRandomExponent(group, _cryptoRandomInteger);

            // Add the newly generated private key to
            // the list of private keys
            privateKeys.add(randomExponent);

            // Generate the corresponding public key by
            // raising the generator to the power of the
            // private key
            ZpGroupElement groupElement =
                generator.exponentiate(randomExponent);

            // Add the newly generated public key to
            // the list of public keys
            publicKeys.add(groupElement);
        }

        return new ElGamalKeyPair(
            new ElGamalPrivateKey(privateKeys, group),
            new ElGamalPublicKey(publicKeys, group));
    }
}
