package meerkat.mixer;

import com.google.protobuf.InvalidProtocolBufferException;
import meerkat.crypto.concrete.ECElGamalEncryption;
import meerkat.crypto.concrete.Util;
import meerkat.protobuf.ConcreteCrypto;
import meerkat.protobuf.Crypto;
import meerkat.protobuf.Voting;
import org.spongycastle.math.ec.ECPoint;
import org.factcenter.qilin.primitives.RandomOracle;
import org.factcenter.qilin.primitives.concrete.DigestOracle;
import org.factcenter.qilin.primitives.concrete.ECElGamal;
import org.factcenter.qilin.primitives.concrete.ECGroup;
import org.junit.Before;
import org.junit.Test;

import java.math.BigInteger;
import java.util.Random;

/**
 * Created by Tzlil on 1/17/2016.
 */
public class RerandomizeTest {

    Random rand;
    ECElGamal.SK key;
    ECGroup group;
    ECElGamalEncryption enc;
    ConcreteCrypto.ElGamalPublicKey serializedPk;
    ECPoint g ;
    ECPoint h ;
    @Before
    public void setup() throws Exception {
        rand = new Random();
        group = new ECGroup("secp256k1");
        BigInteger sk = ECElGamal.generateSecretKey(group, rand);
        key = new ECElGamal.SK(group, sk);
        serializedPk = Util.encodePK(group, key);
        enc = new ECElGamalEncryption();
        enc.init(serializedPk);
        RandomOracle randomOracle = new DigestOracle();
        g = group.getGenerator();
        h = enc.getElGamalPK().getPK();
    }

    private ECPoint convert2ECPoint(ConcreteCrypto.GroupElement bs){
        return enc.decodeElement(bs);
    }

    public void oneRerandomizeTest() throws InvalidProtocolBufferException {
        Voting.PlaintextBallot msg = Utils.genRandomBallot(2,3,16); // 2 questions with 3 answers each, in range 0-15.

        Crypto.EncryptionRandomness r = enc.generateRandomness(rand);


        Crypto.RerandomizableEncryptedMessage e = enc.encrypt(msg, enc.generateRandomness(rand));
        Crypto.RerandomizableEncryptedMessage eNew = enc.rerandomize(e, r);

        assert (Util.decrypt(Voting.PlaintextBallot.class, key, group, e).equals(msg));
        assert (Util.decrypt(Voting.PlaintextBallot.class, key, group, eNew).equals(msg));

        ConcreteCrypto.ElGamalCiphertext eElGamal = ECElGamalEncryption.RerandomizableEncryptedMessage2ElGamalCiphertext(e);
        ConcreteCrypto.ElGamalCiphertext eNewElGamal = ECElGamalEncryption.RerandomizableEncryptedMessage2ElGamalCiphertext(eNew);

        ECPoint expected1 = g.multiply(enc.extractRandomness(r));
        ECPoint result1 = group.add(convert2ECPoint(eNewElGamal.getC1()),group.negate(convert2ECPoint(eElGamal.getC1())));
        expected1.normalize();
        result1.normalize();
        assert (expected1.equals(result1));

        ECPoint expected2 = h.multiply(enc.extractRandomness(r));
        ECPoint result2 = group.add(convert2ECPoint(eNewElGamal.getC2()), group.negate(convert2ECPoint(eElGamal.getC2())));
        expected2.normalize();
        result2.normalize();

        assert (expected2.equals(result2));
    }


    @Test
    public void rerandomizeTest() throws InvalidProtocolBufferException {

        int tests = 1000;

        for (int i = 0; i < tests; i ++){
            System.out.println("re-randomize test #" + i);
            oneRerandomizeTest();
        }
    }
}
