/*

Cryptography functions written by Bingsheng Zhang

Uses the milagro-crypto-js library at: 
https://github.com/milagro-crypto/milagro-crypto-js

*/

//Group parameter generator: returns rng object and generators g1,g2 for G1,G2 as well as order
gpGen = function(){
        //init, and base generators
        var ctx = new CTX("BN254CX");

    	var n=new ctx.BIG(0); n.rcopy(ctx.ROM_CURVE.CURVE_Order);

		//get generator P for G1
            P = new ctx.ECP(0);
            gx = new ctx.BIG(0);
            gx.rcopy(ctx.ROM_CURVE.CURVE_Gx);
            if (ctx.ECP.CURVETYPE != ctx.ECP.MONTGOMERY) {
                gy = new ctx.BIG(0);
                gy.rcopy(ctx.ROM_CURVE.CURVE_Gy);
                P.setxy(gx, gy);
            } else P.setx(gx);
		
		//get generator Q for G2
		var A=new ctx.BIG(0); 
		var B=new ctx.BIG(0); 
        A.rcopy(ctx.ROM_CURVE.CURVE_Pxa);
        B.rcopy(ctx.ROM_CURVE.CURVE_Pxb);
		var Qx=new ctx.FP2(0); Qx.bset(A,B);
		A.rcopy(ctx.ROM_CURVE.CURVE_Pya); 
        B.rcopy(ctx.ROM_CURVE.CURVE_Pyb);
		var Qy=new ctx.FP2(0); Qy.bset(A,B);
		var Q=new ctx.ECP2();
		Q.setxy(Qy,Qy);

		return{
		    n:n,
		    g1:P,
		    g2:Q
		}    
}


//creates ElGamal public and secret key
keyGen=function(params){        
        var ctx = new CTX("BN254CX");  
        //set rng
        var RAW = [];
        var d = new Date();//time for seed, not secure
        var rng = new ctx.RAND();
        rng.clean();
        RAW[0] = d.getSeconds();
        RAW[1] = d.getMinutes();
        RAW[2] = d.getMilliseconds();
        rng.seed(3, RAW);

        //ElGamal
        var sk = new ctx.BIG(0); 
        sk = ctx.BIG.randomnum(params.n,rng);
		var pk = new ctx.ECP(0);
        pk = ctx.PAIR.G1mul(params.g1,sk);
		
		
		return{
    		PK:pk,
	    	SK:sk
		}
}


//combine multiple public key together
//the input is an array of PKs
combine=function(PKs){        
        var ctx = new CTX("BN254CX");  
        var pk=new ctx.ECP();      
        //copy the first pk
        pk.copy(PKs[0]);
        //multiple the rest PKs
        for(i=1;i<PKs.length;i++){
        	pk.add(PKs[i]);
        }		
		
		return{
    		PK:pk
		}
}

		
//ElGamal encryption
encrypt=function(params,PK, m){
        var ctx = new CTX("BN254CX");  
        //set rand
        var RAW = [];
        var d = new Date();//time for seed, not secure
        var rng = new ctx.RAND();
        rng.clean();
        RAW[0] = d.getSeconds();
        RAW[1] = d.getMinutes();
        RAW[2] = d.getMilliseconds();
        rng.seed(3, RAW);

        var r=new ctx.BIG.randomnum(params.n,rng);
        var M=new ctx.BIG(m);

		var C1=new ctx.ECP();
		C1 = ctx.PAIR.G1mul(params.g1,r);
        
        var gM=new ctx.ECP();
        gM = ctx.PAIR.G1mul(params.g1,M);

		var C2=new ctx.ECP();
        C2 = ctx.PAIR.G1mul(PK,r);
		C2.mul(r);
		C2.add(gM);
		
		return{
		    C1:C1,
		    C2:C2
		}
}


//add ciphertexts
add=function(Ciphers){        
        var ctx = new CTX("BN254CX");  
        var s1=new ctx.ECP();
        var s2=new ctx.ECP();
        //copy the first cipher
        s1.copy(Ciphers[0].C1);
        s2.copy(Ciphers[0].C2);
        //multiple the rest ciphertexts
        for(i=1;i<Ciphers.length;i++){
        	s1.add(Ciphers[i].C1);
        }		
		//no idea why I need two loops
        for(j=1;j<Ciphers.length;j++){
        	s2.add(Ciphers[j].C2);
        }

		return{
    		C1:s1,
    		C2:s2
		}
}


//ElGamal decryption
decrypt=function(params,SK, C){
        var ctx = new CTX("BN254CX");  
        var D=new ctx.ECP();
        D = ctx.PAIR.G1mul(C.C1,SK);

        var gM=new ctx.ECP();
        gM.copy(C.C2);        
        gM.sub(D);

//search for message by brute force
        var B;       
        for (j = 0; j < 1000; j++) {
            //use D as temp var
            B = new ctx.BIG(j);
            D = ctx.PAIR.G1mul(params.g1,B);
            if (D.equals(gM))
                return{
                    M:j
                }

        };
 
        
        return{
            M: "Error"
        }
}




//ElGamal partial decryption
partDec=function(SK, C){
        var ctx = new CTX("BN254CX");  
        var D=new ctx.ECP();
        D = ctx.PAIR.G1mul(C.C1,SK);
        
        return{
            D: D
        }
}




//Tally, combine partial decryption 
//Ds is the array of partial decryptions; C is the ciphertext.
tally=function(params,Ds, C){
        var ctx = new CTX("BN254CX");  
        var D=new ctx.ECP();
        D.copy(Ds[0].D);

        //combine D
        for(i=1;i<Ds.length;i++){
        	D.add(Ds[i].D);
        }        


        var gM=new ctx.ECP();
        gM.copy(C.C2);        
        gM.sub(D);

//search for message by brute force
        var B;       
        for (j = 0; j < 1000; j++) {
            //use D as temp var
            B = new ctx.BIG(j);
            D = ctx.PAIR.G1mul(params.g1,B);
            if (D.equals(gM))
                return{
                    M:j
                }

        };
 
        
        return{
            M: "Error"
        }
}