/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package anonym_signature_2017;

import java.math.BigInteger;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import javax.smartcardio.Card;
import javax.smartcardio.CardChannel;
import javax.smartcardio.CardException;
import javax.smartcardio.CardTerminal;
import javax.smartcardio.CommandAPDU;
import javax.smartcardio.ResponseAPDU;
import javax.smartcardio.TerminalFactory;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECPoint;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

public class AnonymSignature {
    
    public static final int CURVE_SIZE                  = 24;
    public static final int SHA1_SIZE                   = 20;
    public static final int CURVE_POINT_SIZE            = 1+2*CURVE_SIZE;
    public static final int MAX_ATTRIBUTES_PER_APDU     = 5;
    
    /************************************************************************/
    /*                          VARIABLES                                   */
    /************************************************************************/
    public int NUMBER_OF_ISSUED_ATTRIBUTES  = 5;       // min  1, max 10
    public int NUMBER_OF_UNDISCLOSED_ATTRIBUTES = 2;   // min  1, max 10: first attribute is always undisclosed m_R
    public int NUMBER_OF_ALPHAS = 2; 
    public int NUMBER_OF_LS = 10;
    public int CURRENT_EPOCH = 31145; // sent to card hashed with SHA1
    private int repetition = 1;
    /************************************************************************/
    /*                          VARIABLES   END                             */
    /************************************************************************/
        
    protected List<CardTerminal> terminals;
    protected TerminalFactory factory;
    private CardTerminal terminal;
    private Card card;
    private CardChannel channel;
    private ResponseAPDU rAPDU;
    private byte[] baCommandAPDU;
    
    private long timeStart;
   
    private long timeIssueAtt;
    private long timeIssueRevocationAtt;
    private long timeProveAtt;
    
    
    private HashMap<Integer, BigInteger> issKeys = new HashMap<Integer, BigInteger>();
    private HashMap<Integer, BigInteger> ls = new HashMap<>();
    private HashMap<Integer, BigInteger> alphas = new HashMap<>();
    private HashMap<ECPoint, Integer> everyC_card_epoch = new HashMap<ECPoint, Integer>();
    BigInteger revAttr;
    
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws NoSuchAlgorithmException {
        // TODO code application logic here
        
        System.out.println("Anonym Signature: TERMINAL SIDE");
        AnonymSignature Reader = new AnonymSignature();
        Reader.run();
    }
    
     public void run() throws NoSuchAlgorithmException {
        
        try 
    	{
            factory = TerminalFactory.getDefault();
            terminals = factory.terminals().list();
            terminal = terminals.get(0);

            System.out.println("Terminals: " + terminals);
            System.out.println("Selected Terminal: " + terminal);

        }catch(Exception e){
                e.printStackTrace();
        }
        
    checkEpochChangeForRevocation(); // test
    
    timeStart = System.currentTimeMillis();   
    for(int i =0; i<repetition; i++)
        issueAttributes(NUMBER_OF_ISSUED_ATTRIBUTES); //issuing attributes plus revocation attribute m_R
    timeIssueAtt = (System.currentTimeMillis() - timeStart)/repetition;
    
    timeStart = System.currentTimeMillis();   
    for(int i =0; i<repetition; i++)
        issueRevocationAttributes(NUMBER_OF_ALPHAS, NUMBER_OF_LS);
    timeIssueRevocationAtt = (System.currentTimeMillis() - timeStart)/repetition;
        
    timeStart = System.currentTimeMillis();
    for(int i =0; i<repetition; i++)
        proveAttributes(NUMBER_OF_ISSUED_ATTRIBUTES); //issuing attributes plus revocation attribute m_R
    timeProveAtt = (System.currentTimeMillis() - timeStart)/repetition;
    
    System.out.println("issueAttributes time [ms] = "+timeIssueAtt);
    System.out.println("issueRevocationAttributes time [ms] = "+timeIssueRevocationAtt); 
    System.out.println("proveAttributes time [ms] = "+timeProveAtt);   
    
    proveAttributesCARD_ONLY(NUMBER_OF_ISSUED_ATTRIBUTES);
    }
    
     
    /**
     * 
     * @param numAttributes number of attributes to be issued (maximum is 9 attributes)
     */
    public void issueAttributes(int numAttributes){
        
        System.out.println("//*********************************//");
        System.out.println("//**  IssueAttributes protocol  ***//");
        System.out.println("//*********************************//");
        
        ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("prime192v1");
        ECCurve curve = ecSpec.getCurve();    
        ECPoint G = ecSpec.getG();
        
        generateIssuerKeys(11, curve.getOrder());
        
        cardConnection();
        
        System.out.println("Select AID");
        byte[] AID = new byte[]{(byte) 0x00, (byte) 0xA4, (byte) 0x04, (byte) 0x00, 
                                (byte) 0x04, 
                                (byte) 0xF0, (byte) 0x00, (byte) 0xE00, (byte) 0x01,
                                (byte) 0x00};
          
        byte[] rData = sendAPDU(AID);
        
        
        System.out.println("INIT -> CARD");   
        byte[] comm = createAPDUComm((byte) 0x80, (byte) 0x01, (byte) numAttributes, (byte) 0x00, 0, null, (byte) (numAttributes * CURVE_SIZE));
        rData  = sendAPDU(comm);
        
        HashMap<Integer, BigInteger> attributes = new HashMap<Integer, BigInteger>();
        byte[] attribute = new byte[CURVE_SIZE];
        
        for(int i=0; i<numAttributes;i++){
            System.arraycopy(rData, 0+i*CURVE_SIZE, attribute, 0, CURVE_SIZE); // 
            attributes.put(i, Utils.byteArrayToBigInt(attribute));
            if (i == 0){
                System.out.println("Revocation attribute m_R = "+attributes.get(i));
                revAttr = attributes.get(i);
            }
            else {
                System.out.println("Attribute "+i+" = "+attributes.get(i));                
            }
        }
        
        //compute signature Sigma
        BigInteger exp = BigInteger.ZERO;

        for(int i=0; i<numAttributes; i++){
            
            exp = exp.add( attributes.get(i).multiply(this.issKeys.get(i+1)) );    // exp = m1*x1 + m2*x2 + ... mi*x1
        }
        
        exp = exp.add( this.issKeys.get(0) );       //exp = exp + x0 = m1*x1 + m2*x2 + ... mi*x1 + x0
        BigInteger expInv = exp.modInverse(curve.getOrder());
        
        ECPoint sigma = G.multiply(expInv);
        
        HashMap<Integer, ECPoint> signaturesXi = new HashMap<Integer, ECPoint>();
        for(int i=0; i<numAttributes;i++){
            signaturesXi.put(i, sigma.multiply(this.issKeys.get(i+1)));
            
        }
        
        System.out.println("SET_Sigma -> CARD");   
        comm = createAPDUComm((byte) 0x80, (byte) 0x02, (byte) 0x00, (byte) 0x00, 1+2*CURVE_SIZE, sigma.getEncoded(false), (byte) 0x00);
        rData  = sendAPDU(comm);
        /*
        comm = createAPDUComm((byte) 0x80, (byte) 0x32, (byte) 0x00, (byte) 0x00, 0, null, (byte)(1+2*CURVE_SIZE));
        rData  = sendAPDU(comm);
        */
        
        byte[] bSignaturesXiPart1;
        byte[] bSignaturesXiPart2;
        byte[] bSignatureXi = new byte[1+2*CURVE_SIZE];
        
        if(numAttributes > MAX_ATTRIBUTES_PER_APDU){
            
            bSignaturesXiPart1 = new byte[MAX_ATTRIBUTES_PER_APDU*(1+2*CURVE_SIZE)];
            
            for(int i=0; i<MAX_ATTRIBUTES_PER_APDU; i++){
                bSignatureXi = signaturesXi.get(i).getEncoded(false);
                System.arraycopy(bSignatureXi, 0, bSignaturesXiPart1, 0+i*(1+2*CURVE_SIZE), bSignatureXi.length);
            }
            
            System.out.println("SET_SigmaXi -> CARD");   
            comm = createAPDUComm((byte) 0x80, (byte) 0x03, (byte) MAX_ATTRIBUTES_PER_APDU, (byte) 0x01, MAX_ATTRIBUTES_PER_APDU*(1+2*CURVE_SIZE), bSignaturesXiPart1, (byte) 0x00);
            rData  = sendAPDU(comm);
            
            bSignaturesXiPart2 = new byte[(numAttributes-MAX_ATTRIBUTES_PER_APDU)*(1+2*CURVE_SIZE)];
            
            for(int i=0; i<numAttributes-MAX_ATTRIBUTES_PER_APDU; i++){
                bSignatureXi = signaturesXi.get(i+MAX_ATTRIBUTES_PER_APDU).getEncoded(false);
                System.arraycopy(bSignatureXi, 0, bSignaturesXiPart2, 0+i*(1+2*CURVE_SIZE), bSignatureXi.length);
            }
            
            System.out.println("SET_SigmaXi -> CARD");   
            comm = createAPDUComm((byte) 0x80, (byte) 0x03, (byte) (numAttributes-MAX_ATTRIBUTES_PER_APDU), (byte) 0x02, (numAttributes-MAX_ATTRIBUTES_PER_APDU)*(1+2*CURVE_SIZE), bSignaturesXiPart2, (byte) 0x00);
            rData  = sendAPDU(comm);
        }else{
            
            bSignaturesXiPart1 = new byte[numAttributes*(1+2*CURVE_SIZE)];
            
            for(int i=0; i<numAttributes; i++){
                bSignatureXi = signaturesXi.get(i).getEncoded(false);
                System.arraycopy(bSignatureXi, 0, bSignaturesXiPart1, 0+i*(1+2*CURVE_SIZE), bSignatureXi.length);
            }
            
            System.out.println("SET_SigmaXi -> CARD");   
            comm = createAPDUComm((byte) 0x80, (byte) 0x03, (byte) numAttributes, (byte) 0x01, numAttributes*(1+2*CURVE_SIZE), bSignaturesXiPart1, (byte) 0x00);
            rData  = sendAPDU(comm);
            
        }
        
        /*
        comm = createAPDUComm((byte) 0x80, (byte) 0x33, (byte) numAttributes, (byte) 0x01, 0, null, (byte) (MAX_ATTRIBUTES_PER_APDU*(1+2*CURVE_SIZE)));
        rData  = sendAPDU(comm);
        
        comm = createAPDUComm((byte) 0x80, (byte) 0x33, (byte) numAttributes, (byte) 0x02, 0, null, (byte) (MAX_ATTRIBUTES_PER_APDU*(1+2*CURVE_SIZE)));
        rData  = sendAPDU(comm);
        */
        
        
    }
    
    public void issueRevocationAttributes(int numOfAlphas, int numOfLs){

        System.out.println("//*******************************************//");
        System.out.println("//**  IssueRevocationVariables protocol  ***//");
        System.out.println("//*******************************************//");

        //it will issue just alphas and ls, as m_R is already encorporated to the hashmap of m_1-9

        ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("prime192v1");
        ECCurve curve = ecSpec.getCurve();
        ECPoint G = ecSpec.getG();

        cardConnection();
        System.out.println("Select AID ");
        byte[] AID = new byte[]{(byte) 0x00, (byte) 0xA4, (byte) 0x04, (byte) 0x00, 
                                (byte) 0x04, 
                                (byte) 0xF0, (byte) 0x00, (byte) 0xE00, (byte) 0x01,
                                (byte) 0x00};

        byte[] rData = sendAPDU(AID);

        System.out.println("SET LS -> CARD");   
        byte[] comm = createAPDUComm((byte) 0x80, (byte) 0x08, (byte) numOfLs, (byte) 0x00, 0, null, (byte) (numOfLs * CURVE_SIZE));
        rData  = sendAPDU(comm);

        //HashMap<Integer, BigInteger> ls = new HashMap<>();
        byte[] l = new byte[CURVE_SIZE];

        for(int i=0; i<numOfLs;i++){
            System.arraycopy(rData, 0+i*CURVE_SIZE, l, 0, CURVE_SIZE);
            ls.put(i, Utils.byteArrayToBigInt(l));
                System.out.println("l"+(i+1)+" = "+ls.get(i));                
        }

        System.out.println("SET ALPHAS -> CARD");   
        comm = createAPDUComm((byte) 0x80, (byte) 0x09, (byte) numOfAlphas, (byte) 0x00, 0, null, (byte) (numOfAlphas * CURVE_SIZE));
        rData  = sendAPDU(comm);

        //HashMap<Integer, BigInteger> alphas = new HashMap<>();
        byte[] alpha = new byte[CURVE_SIZE];

        for(int i=0; i<numOfAlphas;i++){
            System.arraycopy(rData, 0+i*CURVE_SIZE, alpha, 0, CURVE_SIZE); // 
            alphas.put(i, Utils.byteArrayToBigInt(alpha));
            System.out.println("alpha"+(i+1)+" = "+alphas.get(i));  
            //System.out.println("SIZE OF alpha: " + alpha.length);
        }
     
        /*
        for (int i = 0; i < 0; i++){
            checkR(); // test
        }*/
       //checkC(); // test
       //checkTRevoke(); // test
       //checkSRevoke(); // test
    }
    
    public void proveAttributes(int attributes) throws NoSuchAlgorithmException{
        
        System.out.println("//*********************************//");
        System.out.println("//**  ProveAttributes protocol  ***//");
        System.out.println("//*********************************//");
        
        ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("prime192v1");
        ECCurve curve = ecSpec.getCurve();
        ECPoint G = ecSpec.getG();
        
        cardConnection();
        System.out.println("Select AID ");
        byte[] AID = new byte[]{(byte) 0x00, (byte) 0xA4, (byte) 0x04, (byte) 0x00, 
                                (byte) 0x04, 
                                (byte) 0xF0, (byte) 0x00, (byte) 0xE00, (byte) 0x01,
                                (byte) 0x00};
            
        byte[] rData = sendAPDU(AID);
        
        System.out.println("getRevocationProof1 -> CARD"); ;
            
        // SET EPOCH HASH AND NONCE
        byte[] epochHash = createEpochHash();
        
        byte[] nonce = new byte[CURVE_SIZE];
        SecureRandom.getInstanceStrong().nextBytes(nonce);
        System.out.println("VALUE OF nonce: " + Utils.byteArrayToBigInt(nonce) + " with length " + nonce.length);
        
        byte[] nonceAndEpochHash = new byte[SHA1_SIZE+CURVE_SIZE];
        System.arraycopy(epochHash, 0, nonceAndEpochHash, 0, epochHash.length);
        System.arraycopy(nonce, 0, nonceAndEpochHash, SHA1_SIZE, nonce.length);      
        
        byte[] comm = createAPDUComm((byte) 0x80, (byte) 0x10, (byte) 0x00, (byte) 0x00, SHA1_SIZE+CURVE_SIZE, nonceAndEpochHash, (byte) 0x00);
        rData  = sendAPDU(comm);
        
        //for (int i = 0; i < 1; i++)
        //{
            //checkR(); // test
        //}
        //ECPoint computed_c = checkC(); // test
        //checkTRevoke(); // test
        //checkSRevoke(); // test
        
        //checkR(); // test
        //ECPoint computed_c = checkC(); // test
        //checkTRevoke(); // test
        //checkSRevoke(); // test
        /*
        comm = createAPDUComm((byte) 0x80, (byte) 0x04, (byte) 0x00, (byte) 0x00, 0, null, (byte) (2*CURVE_POINT_SIZE+2*CURVE_SIZE));
        rData  = sendAPDU(comm);

        byte[] bc = new byte[1+2*CURVE_SIZE];
        System.arraycopy(rData, 0, bc, 0, bc.length);
        ECPoint c = curve.decodePoint(bc);
        //System.out.println("c x:"+c.getXCoord()); 
        //System.out.println("c y:"+c.getYCoord()); 
        
        byte[] bt_revoke = new byte[1+2*CURVE_SIZE];
        System.arraycopy(rData, bc.length, bt_revoke, 0, bt_revoke.length);
        ECPoint t_revoke = curve.decodePoint(bt_revoke);
        
        byte[] brhor1 = new byte[CURVE_SIZE];
        System.arraycopy(rData, bc.length+bt_revoke.length, brhor1, 0, brhor1.length);
        BigInteger rho_r1 = Utils.byteArrayToBigInt(brhor1);
        System.out.println("VALUE OF rho_r1: " + rho_r1);
        
        byte[] brho_mR1 = new byte[CURVE_SIZE];
        System.arraycopy(rData, bc.length+bt_revoke.length+brhor1.length, brho_mR1, 0, brho_mR1.length);
        BigInteger rho_mR1 = Utils.byteArrayToBigInt(brho_mR1);
        System.out.println("VALUE OF rho_mR1: " + rho_mR1);
       */
        // currently with computations of t_revoke and c
        System.out.println("getVerificationProof1 -> CARD");      
       
        comm = createAPDUComm((byte) 0x80, (byte) 0x05, (byte) NUMBER_OF_UNDISCLOSED_ATTRIBUTES, (byte) 0x00, 0, null, (byte) (4*CURVE_POINT_SIZE+2*CURVE_SIZE));
        rData  = sendAPDU(comm);
             
        byte[] bt_verify = new byte[1+2*CURVE_SIZE];
        System.arraycopy(rData, 0, bt_verify, 0, bt_verify.length);
        ECPoint t_verify = curve.decodePoint(bt_verify);
        
        byte[] bSigmaCard =  new byte[1+2*CURVE_SIZE];
        System.arraycopy(rData, bt_verify.length, bSigmaCard, 0, bSigmaCard.length);
        ECPoint sigmaCard = curve.decodePoint(bSigmaCard);
        
        byte[] bc = new byte[1+2*CURVE_SIZE];
        System.arraycopy(rData, bt_verify.length+bSigmaCard.length, bc, 0, bc.length);
        ECPoint c = curve.decodePoint(bc);
        
        byte[] bt_revoke = new byte[1+2*CURVE_SIZE];
        System.arraycopy(rData, bt_verify.length+bSigmaCard.length+bc.length, bt_revoke, 0, bt_revoke.length);
        ECPoint t_revoke = curve.decodePoint(bt_revoke);
         
        byte[] be =  new byte[CURVE_SIZE];
        System.arraycopy(rData, bt_verify.length+bSigmaCard.length+bc.length+bt_revoke.length, be, 0, be.length);
        BigInteger e = Utils.byteArrayToBigInt(be);
        
        
        byte[] bsv = new byte[CURVE_SIZE];
        System.arraycopy(rData, bt_verify.length+bSigmaCard.length+bc.length+bt_revoke.length+be.length, bsv, 0, bsv.length);
        BigInteger sv = Utils.byteArrayToBigInt(bsv);
        
        /* 
        comm = createAPDUComm((byte) 0x80, (byte) 0x34, (byte) 0x00, (byte) 0x01, 0, null, (byte) (10*CURVE_SIZE)); 
        rData  = sendAPDU(comm);
        comm = createAPDUComm((byte) 0x80, (byte) 0x34, (byte) 0x00, (byte) 0x02, 0, null, (byte) (10*CURVE_SIZE)); 
        rData  = sendAPDU(comm);
        */
        
        System.out.println("getProof4 -> CARD");   
        comm = createAPDUComm((byte) 0x80, (byte) 0x07, (byte) 0x00, (byte) 0x00, 0, null, (byte) (NUMBER_OF_ISSUED_ATTRIBUTES*CURVE_SIZE));
        rData  = sendAPDU(comm);
        
        HashMap<Integer, BigInteger> signaturesXi = new HashMap<Integer, BigInteger>();
        byte[] signatureXi = new byte[CURVE_SIZE];
        
        for(int i=0; i<NUMBER_OF_UNDISCLOSED_ATTRIBUTES;i++){
            System.arraycopy(rData, 0+i*CURVE_SIZE, signatureXi, 0, CURVE_SIZE);
            signaturesXi.put(i, Utils.byteArrayToBigInt(signatureXi));
        }        
        
        HashMap<Integer, BigInteger> attributesDisclosed = new HashMap<Integer, BigInteger>();
        byte[] attributeDisclosed = new byte[CURVE_SIZE];
        
        for(int i=0; i<NUMBER_OF_ISSUED_ATTRIBUTES-NUMBER_OF_UNDISCLOSED_ATTRIBUTES;i++){
            System.arraycopy(rData, NUMBER_OF_UNDISCLOSED_ATTRIBUTES*CURVE_SIZE+i*CURVE_SIZE, attributeDisclosed, 0, CURVE_SIZE);
            attributesDisclosed.put(i, Utils.byteArrayToBigInt(attributeDisclosed));
        }
        
        // Verification
        
        HashMap<Integer, ECPoint> sigmasXi = new HashMap<Integer, ECPoint>();
        byte[] sigmaXi = new byte[1+2*CURVE_SIZE];
        
        for(int i=0; i<NUMBER_OF_UNDISCLOSED_ATTRIBUTES;i++){
            sigmasXi.put(i, sigmaCard.multiply(issKeys.get(i+1)));
        }
                
        ECPoint sigmaCom2 = sigmaCard.multiply(issKeys.get(0));        
        BigInteger cNeg = e.negate();
        
        ECPoint tVerify = curve.getInfinity();
        for(int i=0; i<NUMBER_OF_ISSUED_ATTRIBUTES-NUMBER_OF_UNDISCLOSED_ATTRIBUTES;i++){
            tVerify = tVerify.add((sigmaCard.multiply(issKeys.get(i+NUMBER_OF_UNDISCLOSED_ATTRIBUTES+1))
                         .multiply(attributesDisclosed.get(i))));
        }
        tVerify = tVerify.add(sigmaCom2).multiply(cNeg);
        
        for(int i=0; i<NUMBER_OF_UNDISCLOSED_ATTRIBUTES;i++){
                tVerify = tVerify.add(sigmasXi.get(i).multiply(signaturesXi.get(i)));
        }
        tVerify = tVerify.add(G.multiply(sv));
     
        System.out.println("");
       if(tVerify.equals(t_verify)){
           System.out.println("//**********************************/ //"); 
           System.out.println("//***** Successful verification ****//");
           System.out.println("//**********************************//");
            }else{
           System.err.println("//**********************************//");
            System.err.println("//***** Verification failed *******//");
           System.err.println("//**********************************//");
        }
       
        System.out.println("getProof2 -> CARD");   
        comm = createAPDUComm((byte) 0x80, (byte) 0x12, (byte) 0x00, (byte) 0x00, 0, null, (byte) (2*CURVE_SIZE));
        rData  = sendAPDU(comm);
        
        byte[] bsr = new byte[CURVE_SIZE];
        System.arraycopy(rData, 0, bsr, 0, bsr.length);
        BigInteger sr = Utils.byteArrayToBigInt(bsv);

        byte[] bsmr = new byte[CURVE_SIZE];
        System.arraycopy(rData, bsr.length, bsmr, 0, bsmr.length);
        BigInteger smr = Utils.byteArrayToBigInt(bsv);
       
       /*
        System.out.println("getRevocationProof2 -> CARD");   
        comm = createAPDUComm((byte) 0x80, (byte) 0x11, (byte) 0x00, (byte) 0x00, 0, null, (byte) (5*CURVE_SIZE));
        rData  = sendAPDU(comm);
        
        byte[] bsr = new byte[CURVE_SIZE];
        System.arraycopy(rData, 0, bsr, 0, bsr.length);
        BigInteger sr = Utils.byteArrayToBigInt(bsr);
        System.out.println("VALUE OF s_r: " + sr);
        
        byte[] bsmr = new byte[CURVE_SIZE];
        System.arraycopy(rData, bsr.length, bsmr, 0, bsmr.length);
        BigInteger smr = Utils.byteArrayToBigInt(bsmr);
        System.out.println("VALUE OF s_mr: " + smr);
        
        byte[] brhor = new byte[CURVE_SIZE];
        System.arraycopy(rData, bsr.length+bsmr.length, brhor, 0, brhor.length);
        BigInteger rho_r = Utils.byteArrayToBigInt(brhor);
        System.out.println("VALUE OF attRev on card : " + rho_r); // currently attrRev
        System.out.println("VALUE OF attRev terminal: " + revAttr);
        
        byte[] be_card = new byte[CURVE_SIZE];
        System.arraycopy(rData, bsr.length+bsmr.length+brhor.length, be_card, 0, be_card.length);
        BigInteger e_card = Utils.byteArrayToBigInt(be_card); //currently r
        System.out.println("VALUE OF e_card: " + e_card);
        
        byte[] be_r = new byte[CURVE_SIZE];
        System.arraycopy(rData, bsr.length+bsmr.length+brhor.length+be_card.length, be_r, 0, be_r.length);
        BigInteger r = Utils.byteArrayToBigInt(be_r); //currently r
        System.out.println("VALUE OF r: " + r);
        
        //Revoke();
       e = Utils.byteArrayToBigInt(epochHash);
       System.out.println("VALUE OF e card: " + e_card);
       System.out.println("VALUE OF e term: " + e);
       
       System.out.println("COMPUTATION:");
       
        BigInteger exponent_of_c = r; // c = r
        exponent_of_c = exponent_of_c.add(Utils.byteArrayToBigInt(epochHash)); // c = r + currentEpoch
        exponent_of_c = exponent_of_c.subtract(revAttr); // c = r + currentEpoch - revAttr
        exponent_of_c = exponent_of_c.modInverse(curve.getOrder()); // c = 1/(r+currentEpoch-revAttr)
        //there
        ECPoint computed_c = G.multiply(exponent_of_c); // c = g^(1/(r+currentEpoch-revAttr))

        if (c.equals(computed_c))
        {
            System.out.println("Successfully computed c.");
        }
        else
        {
            System.out.println("Not quite yet with computed c.");
        }
        
        //BigInteger exponent_of_c = Utils.byteArrayToBigInt(epochHashByte); // c = r
       
       
        ECPoint computed_tRevoke = c.multiply(rho_r1.add(rho_mR1));
        //computed_tRevoke = computed_tRevoke.add(c.multiply(rho_mR1));
        
        if (t_revoke.equals(computed_tRevoke))
        {
            System.out.println("Successfully computed tRevoke.");
        }
        else
        {
            System.out.println("Not quite yet with computed tRevoke.");
        }
        
        BigInteger computed_sr;
        computed_sr = rho_r1.add(e_card.multiply(r));
        computed_sr = computed_sr.mod(curve.getOrder());
        System.out.println("VALUE OF s_r computed on terminal: " + computed_sr);
        
        if (sr.equals(computed_sr))
        {
            System.out.println("Successfully computed sr.");
        }
        else
        {
            System.out.println("Not quite yet with computed sr.");
        }
        /*
        BigInteger computed_smr;
        computed_smr = rho_mR1.subtract(e_card.multiply(revAttr));
        computed_smr = computed_smr.mod(curve.getOrder());
        System.out.println("VALUE OF s_mR computed on terminal: " + computed_smr);
        
        if (smr.equals(computed_smr))
        {
            System.out.println("Successfully computed smr.");
        }
        else
        {
            System.out.println("Not quite yet with computed smr.");
        }
        
              //tmp begin
        */
       /*
       //TRETI RIADOK REVOCATION PROTOCOL PROOF Z BAKALARKY
       BigInteger eInv = e_card.modInverse(curve.getOrder()); // eInv = -e
       
       BigInteger He = e.multiply(e_card); // He = H(epoch) * e
       He = He.mod(curve.getOrder());
       //He = He.modInverse(curve.getOrder()); // He = -(H(epoch) * e)
       
       ECPoint tRevoke = G.multiply(eInv);
       tRevoke = tRevoke.add(c.multiply(He)); // t = g^(-e) * c^(H(epoch)*e)
       BigInteger other_c_exp = rho_mR1.subtract(e_card.multiply(revAttr));   
       other_c_exp = other_c_exp.add(rho_r);
       other_c_exp = other_c_exp.add(e_card.multiply(r));
       other_c_exp = other_c_exp.mod(curve.getOrder());
       tRevoke = tRevoke.add(c.multiply(other_c_exp)); 
       */
       /*
       BigInteger other_c_exp = rho_mR1.add(rho_r);
       ECPoint tRevoke = G.multiply(other_c_exp);
       
        other_c_exp = r; // c = r
        other_c_exp = other_c_exp.add(Utils.byteArrayToBigInt(epochHash)); // c = r + currentEpoch
        other_c_exp = other_c_exp.subtract(revAttr); // c = r + currentEpoch - revAttr
        other_c_exp = other_c_exp.modInverse(curve.getOrder()); // c = 1/(r+currentEpoch-revAttr)
        //there
        ECPoint tRevoke2 = G.multiply(other_c_exp); // c = g^(1/(r+currentEpoch-revAttr))
        tRevoke = tRevoke.add(tRevoke2);
        */
       //tmp end
       
       
       BigInteger eInv = e.modInverse(curve.getOrder()); // eInv = -e
       
       BigInteger He = e.multiply(Utils.byteArrayToBigInt(epochHash)); // He = H(epoch) * e
       He = He.mod(curve.getOrder());
       //He = He.modInverse(curve.getOrder()); // He = -(H(epoch) * e)

       ECPoint tRevoke = G.multiply(eInv); // t = g^(-e)
       tRevoke = tRevoke.add(c.multiply(He)); // t = g^(-e) * c^(H(epoch)*e)

       tRevoke = tRevoke.add(c.multiply(sr)); // t = g^(-e) * c^(-H(epoch)*e) *c^(sr)
       tRevoke = tRevoke.add(c.multiply(smr)); // t = g^(-e) * c^(-H(epoch)*e) *c^(sr)*c^(smr)
       
       //ECPoint computed_tRevoke = c.multiply(rho_r1.add(rho_mR1));
       
       //ECPoint tRevoke = G.multiply(exponent_of_c);
       //BigInteger exp = rho_r1.add(rho_mR1);
       //exp = exp.add(exponent_of_c);
       //exp = exp.mod(curve.getOrder());
       //tRevoke = tRevoke.multiply(exp);
       //System.out.println("VALUE OF rho_r1: " + rho_r1);
       //System.out.println("VALUE OF rho_mR1: " + rho_mR1);
       
       System.out.println("");
       if(tRevoke.equals(t_revoke)){
           System.out.println("//**********************************//");
           System.out.println("//*** Successful revocation proof **//");
           System.out.println("//**********************************//");
      }else{
           System.err.println("//**********************************//");
            System.err.println("//**** Revocation proof failed ****//");
           System.err.println("//**********************************//");
        }
        
       /*
        BigInteger exponent_of_c = rho_r; // c = r
        exponent_of_c = exponent_of_c.add(Utils.byteArrayToBigInt(epochHashByte)); // c = r + currentEpoch
        exponent_of_c = exponent_of_c.subtract(revAttr); // c = r + currentEpoch - revAttr
        
        //BigInteger exponent_of_c = Utils.byteArrayToBigInt(epochHashByte); // c = r
      
        System.out.println("VALUE OF x computed on the card    " + revCard);
        System.out.println("VALUE OF x computed on the teminal " + exponent_of_c);
 
        //exponent_of_c = revAttr; // todo:change
        exponent_of_c = exponent_of_c.modInverse(curve.getOrder()); // c = 1/(r+currentEpoch-revAttr)

        System.out.println("VALUE OF c exponent computed on the card    " + c_exp);
        System.out.println("VALUE OF c exponent computed on the teminal " + exponent_of_c);
        
        int comparevalue;
        BigInteger computed_r;
        System.out.println("VALUE OF r: " + rho_r);

        for(int j=0; j<NUMBER_OF_LS;j++){
            for (int k=0; k<NUMBER_OF_LS;k++)
            {
                computed_r = alphas.get(0).multiply(ls.get(k)).add(alphas.get(1).multiply(ls.get(j)));
                comparevalue = rho_r.compareTo(computed_r);

                if (comparevalue == 0)
                {
                    System.out.println("r = (alpha1*l"+(k+1)+")+(alpha2*l"+(j+1)+")");
                    System.out.println("Correctly computed r.");
                }
                //System.out.println("check if = " + computed_r);
                //System.out.println("Comparevalue: " + comparevalue);
            }
        }        

        comparevalue = c_exp.compareTo(exponent_of_c);
        if (comparevalue == 0)
        {
            System.out.println("Successfully computed exponent of c.");
        }
                else
        {
            System.out.println("Not quite yet with computed exponent of c. ");
        }

        //compute c ECPoint
        ECPoint computed_c = G.multiply(exponent_of_c);

        if (c2.equals(computed_c))
        {
            System.out.println("Successfully computed c.");
        }
        else
        {
            System.out.println("Not quite yet with computed c.");
        }
        
        */
       System.out.println("WITHIN THE MAIN PROOF");
       blacklistCardForEpoch(curve, G);
       if (everyC_card_epoch.containsKey(c))
       {
            System.out.println("Successfully added c to the blacklist.");
        }
        else
        {
            System.out.println("C is not on the blacklist.");
        }
       
       checkC(c); // test
       checkTRevoke(); // test
       checkSRevoke(); // test
    }
    
     public void proveAttributesCARD_ONLY(int attributes){
        
        System.out.println("//*********************************//");
        System.out.println("//**  ProveAttributes protocol  ***//");
        System.out.println("//*********************************//");
        
        ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("prime192v1");
        ECCurve curve = ecSpec.getCurve();
        ECPoint G = ecSpec.getG();
        
        cardConnection();
       
        byte[] AID = new byte[]{(byte) 0x00, (byte) 0xA4, (byte) 0x04, (byte) 0x00, 
                                (byte) 0x04, 
                                (byte) 0xF0, (byte) 0x00, (byte) 0xE00, (byte) 0x01,
                                (byte) 0x00};
             
        
        byte[] rData = sendAPDU(AID);
                
        long t1;
        t1 = System.currentTimeMillis();
        byte[] comm = createAPDUComm((byte) 0x80, (byte) 0x05, (byte) NUMBER_OF_UNDISCLOSED_ATTRIBUTES, (byte) 0x00, 0, null, (byte) 0x00);
        for(int i =0; i<repetition; i++)
            rData  = sendAPDU(comm);
        t1 = (System.currentTimeMillis() - t1)/repetition;
         
        comm = createAPDUComm((byte) 0x80, (byte) 0x05, (byte) NUMBER_OF_UNDISCLOSED_ATTRIBUTES, (byte) 0x00, 0, null, (byte) (4*CURVE_POINT_SIZE+2*CURVE_SIZE));
        rData  = sendAPDU(comm);
                
        byte[] btVerify = new byte[1+2*CURVE_SIZE];      
        System.arraycopy(rData, 0, btVerify, 0, btVerify.length);
        ECPoint tVerify = curve.decodePoint(btVerify);
        
        byte[] bSigmaCard =  new byte[1+2*CURVE_SIZE];
        System.arraycopy(rData, btVerify.length, bSigmaCard, 0, bSigmaCard.length);
        ECPoint sigmaCARD = curve.decodePoint(bSigmaCard);
        
        byte[] bc = new byte[1+2*CURVE_SIZE];
        System.arraycopy(rData, btVerify.length+bSigmaCard.length, bc, 0, bc.length);
        ECPoint c = curve.decodePoint(bc);
        
        byte[] bt_revoke = new byte[1+2*CURVE_SIZE];
        System.arraycopy(rData, btVerify.length+bSigmaCard.length+bc.length, bt_revoke, 0, bt_revoke.length);
        ECPoint t_revoke = curve.decodePoint(bt_revoke);
         
        byte[] be =  new byte[CURVE_SIZE];
        System.arraycopy(rData, btVerify.length+bSigmaCard.length+bc.length+bt_revoke.length, be, 0, be.length);
        BigInteger e = Utils.byteArrayToBigInt(be);
        
        byte[] bsv = new byte[CURVE_SIZE];
        System.arraycopy(rData, btVerify.length+bSigmaCard.length+bc.length+bt_revoke.length+be.length, bsv, 0, bsv.length);
        BigInteger sv = Utils.byteArrayToBigInt(bsv);
         
       
        long t2;
        t2 = System.currentTimeMillis();
        comm = createAPDUComm((byte) 0x80, (byte) 0x07, (byte) 0x00, (byte) 0x00, 0, null, (byte) 0x00);
        for(int i =0; i<repetition; i++)
            rData  = sendAPDU(comm);
        t2 = (System.currentTimeMillis() - t2)/repetition;
       
        comm = createAPDUComm((byte) 0x80, (byte) 0x07, (byte) 0x00, (byte) 0x00, 0, null, (byte) (NUMBER_OF_ISSUED_ATTRIBUTES*CURVE_SIZE));
        rData  = sendAPDU(comm); 
        
        HashMap<Integer, BigInteger> signaturesXi = new HashMap<Integer, BigInteger>();
        byte[] signatureXi = new byte[CURVE_SIZE];
        
        for(int i=0; i<NUMBER_OF_UNDISCLOSED_ATTRIBUTES;i++){
            System.arraycopy(rData, 0+i*CURVE_SIZE, signatureXi, 0, CURVE_SIZE);
            signaturesXi.put(i, Utils.byteArrayToBigInt(signatureXi));
        }        
        
        HashMap<Integer, BigInteger> attributesDisclosed = new HashMap<Integer, BigInteger>();
        byte[] attributeDisclosed = new byte[CURVE_SIZE];
        
        for(int i=0; i<NUMBER_OF_ISSUED_ATTRIBUTES-NUMBER_OF_UNDISCLOSED_ATTRIBUTES;i++){
            System.arraycopy(rData, NUMBER_OF_UNDISCLOSED_ATTRIBUTES*CURVE_SIZE+i*CURVE_SIZE, attributeDisclosed, 0, CURVE_SIZE);
            attributesDisclosed.put(i, Utils.byteArrayToBigInt(attributeDisclosed));
        }
        
        // Verification
        long t3;
        t3 = System.currentTimeMillis();
        for(int y =0; y<repetition; y++){
            HashMap<Integer, ECPoint> sigmasXi = new HashMap<Integer, ECPoint>();
            byte[] sigmaXi = new byte[1+2*CURVE_SIZE];

            for(int i=0; i<NUMBER_OF_UNDISCLOSED_ATTRIBUTES;i++){
                sigmasXi.put(i, sigmaCARD.multiply(issKeys.get(i+1)));
            }

            ECPoint sigmaCom2 = sigmaCARD.multiply(issKeys.get(0));        
            BigInteger cNeg = e.negate();

            ECPoint tComm = curve.getInfinity();
            for(int i=0; i<NUMBER_OF_ISSUED_ATTRIBUTES-NUMBER_OF_UNDISCLOSED_ATTRIBUTES;i++){
                tComm = tComm.add((sigmaCARD.multiply(issKeys.get(i+NUMBER_OF_UNDISCLOSED_ATTRIBUTES+1))
                             .multiply(attributesDisclosed.get(i))));
            }
            tComm = tComm.add(sigmaCom2).multiply(cNeg);

            for(int i=0; i<NUMBER_OF_UNDISCLOSED_ATTRIBUTES;i++){
                    tComm = tComm.add(sigmasXi.get(i).multiply(signaturesXi.get(i)));
            }
            tComm = tComm.add(G.multiply(sv));


            System.out.println("");
            if(tComm.equals(tVerify)){
                System.out.println("//**********************************//");
                System.out.println("//***** Successful verification ****//");
                System.out.println("//**********************************//");
            }else{
                System.err.println("//**********************************//");
                System.err.println("//***** Verification failed*********//");
                System.err.println("//**********************************//");
           }
            
        }
        t3 = (System.currentTimeMillis() - t3)/repetition;
        System.out.println("ON CARD FINAL TIME = "+(t1+t2)+" ms");
        System.out.println("ON PC TIME = "+(t3)+" ms");
        System.out.println("TOTAL TIME = "+(t1+t2+t3)+" ms");
        
    }
    
    
    private void generateIssuerKeys(int number, BigInteger order){
        
        //genarate keys
        /*
        BigInteger xi;
        
        for(int i=0; i<number; i++){
            do{
                xi = new BigInteger(CURVE_SIZE * Byte.SIZE, new SecureRandom());
                xi = xi.mod(order);
            }while(xi.bitLength()!=CURVE_SIZE * Byte.SIZE);
            
            this.issKeys.put(i, xi);
            System.out.println("key "+i+" = "+this.issKeys.get(i));
        }*/
        
        this.issKeys.put(0, new BigInteger("4445591337973376559762931093108790250282238344929145215472",10));
        this.issKeys.put(1, new BigInteger("3801312058808399940147966045575368921951119825165577559753",10));
        this.issKeys.put(2, new BigInteger("3898358749678386661828473442097715440134693011680280933142",10));
        this.issKeys.put(3, new BigInteger("3359846741865011428178977786003887902900072783099049193045",10));
        this.issKeys.put(4, new BigInteger("4072642103774316716573584190002156174697956287116688830982",10));
        this.issKeys.put(5, new BigInteger("3385661590845438143381931979209953159724937883734702659554",10));
        this.issKeys.put(6, new BigInteger("4389321053384338718394631984940919087584595658161266424542",10));
        this.issKeys.put(7, new BigInteger("4798684483529062310216470974955192983458419447507571663165",10));
        this.issKeys.put(8, new BigInteger("5012200544632088925087532924983478076120934257578111014587",10));
        this.issKeys.put(9, new BigInteger("5463528332484867488298714836253167354201879819057146923053",10));
        this.issKeys.put(10, new BigInteger("4130944334089052989915622847015675170255284805205747997486",10));
        //this.issKeys.put(11, new BigInteger("5525852786681124171342080329310254961881581081947409471308",10));
        
        for(int i=0; i<issKeys.size();i++){
            System.out.println("key "+i+" = "+this.issKeys.get(i));
        }
    }
    
     /**
     * Connect card to the terminal
     * @throws CardException
     */
    private void cardConnection(){

        try {	

            while (!terminal.isCardPresent()){};

            card = terminal.connect("*");
            System.out.println("ATR: "+bytesToHex(card.getATR().getBytes()));
            channel = card.getBasicChannel();

            System.out.println("\nCard info: " + card);

            }catch (CardException ce){
                ce.printStackTrace();
            }
    }
    
    
    /**
     * Send APDU command to the card
     */
    public byte[] sendAPDU(byte[] data){

        
    byte[] baResponceAPDU = null;

    baCommandAPDU = data;

    System.out.println("APDU >>>: " + bytesToHex(baCommandAPDU));

    try {

        ResponseAPDU r = channel.transmit(new CommandAPDU(baCommandAPDU));

        baResponceAPDU = r.getBytes();
        
        System.out.println("APDU <<<: " + bytesToHex(baResponceAPDU)+" SW ="+bytesToHex(new byte[]{(byte) r.getSW1(), (byte) r.getSW2()}));
        return r.getData();
        
        } catch (CardException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return null;
        }

    }
    
    
    public void CardDisconnect() {
        try {
                card.disconnect(true);
                System.out.println("Karta byla odpojena");
        } catch (CardException e) {
                System.err.println("Chyba odpojeni karty");
        }
    }
    
    
    /**
     * Convert bytes to hexadecimal string
     * @param bytes
     * @return 
     */
    public String bytesToHex(byte[] bytes) {

        char[] hexArray = "0123456789ABCDEF".toCharArray();
        char[] hexChars = new char[bytes.length * 2];
        for ( int j = 0; j < bytes.length; j++ ) {
            int v = bytes[j] & 0xFF;
            hexChars[j * 2] = hexArray[v >>> 4];
            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
        }
        return new String(hexChars);
    }
    
    
    /**
     * Create APDU command
     * @param CLA
     * @param INS
     * @param P1
     * @param P2
     * @param Lc
     * @param data
     * @param Le
     * @return 
     */
    public byte[] createAPDUComm(byte CLA, byte INS, byte P1, byte P2, int Lc, byte[] data, byte Le){
        
        byte[] apduComm;
        int LcSize = 1;
        int LeSize = 1;

        if(Lc > 255){
            LcSize = 3;
        }
        
        if(Lc > 255){
            LeSize = 3;
        }
        
        if(Le == 0 && Lc == 0){                                         //Case 1
            apduComm = new byte[4];
        }
        else if (Le != 0 && Lc == 0){                                   //Case 2
           apduComm = new byte[5]; 
           apduComm[apduComm.length-1] = Le;
           
        }
        else if (Le == 0 && Lc != 0)                                    //Case 3
            apduComm = new byte[4 + LcSize + data.length];
        
        else if (Le != 0 && Lc != 0){                                   //Case 4
            apduComm = new byte[4 + LcSize + data.length + LeSize];
            apduComm[apduComm.length-1] = Le;
        }
        else{
            return null;
        }
        
        apduComm[0] = CLA;
        apduComm[1] = INS;
        apduComm[2] = P1;
        apduComm[3] = P2;
        
         if(Lc != 0){
             apduComm[4] = (byte) data.length;
             System.arraycopy(data, 0, apduComm, 5, data.length);
         }
         
        return apduComm;
    }
    
    
    public String bytesToHexPrint(byte[] bytes) {

        int i = -2;
        char[] hexArray = "0123456789ABCDEF".toCharArray();
        char[] hexChars = new char[bytes.length * 4];
        for ( int j = 0; j < bytes.length; j++ ) {
            int v = bytes[j] & 0xFF;
            hexChars[j * 2] = hexArray[v >>> 4];
            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
            
          //  System.out.print("0x"+hexChars[j * 2]+hexChars[j * 2 + 1]+", ");
        } 
     //   System.out.println("");
        return new String(hexChars);
    }
    
    private byte[] createEpochHash()
    {
        //creating sha1
        try
        {
                    MessageDigest md = MessageDigest.getInstance("SHA-1");
                    byte[] epoch = BigInteger.valueOf(CURRENT_EPOCH).toByteArray();
                    byte[] epochSHA1 = md.digest(epoch);
                    BigInteger epochHash = new BigInteger(1, epochSHA1);
                    System.out.println("VALUE OF epochHash: " + epochHash + " with length " + epochSHA1.length);
                    return epochSHA1;
        }      
        catch (NoSuchAlgorithmException e) 
        {
            throw new RuntimeException(e);
        }
    }
    
    private void checkEpochChangeForRevocation(){ // test
        
        System.out.println("//*********************************//");
        System.out.println("//****  Check epoch iteration  ****//");
        System.out.println("//*********************************//");
        
        cardConnection();
        System.out.println("Select AID ");
        byte[] AID = new byte[]{(byte) 0x00, (byte) 0xA4, (byte) 0x04, (byte) 0x00, 
                                (byte) 0x04, 
                                (byte) 0xF0, (byte) 0x00, (byte) 0xE00, (byte) 0x01,
                                (byte) 0x00};
            
        byte[] rData = sendAPDU(AID);
        
        
        byte[] epochHash = createEpochHash();
        byte[] numPrevVerifications = new byte[CURVE_SIZE];
        byte[] epochHash_CARD = new byte[CURVE_SIZE];
        
        byte[] comm = createAPDUComm((byte) 0x80, (byte) 0x35, (byte) 0x00, (byte) 0x00, CURVE_SIZE, epochHash, (byte) 0x00);
        
        for(int i =0; i<5; i++){
            comm = createAPDUComm((byte) 0x80, (byte) 0x35, (byte) 0x00, (byte) 0x00, SHA1_SIZE, epochHash, (byte) 0x00);
            rData  = sendAPDU(comm);
            comm = createAPDUComm((byte) 0x80, (byte) 0x40, (byte) 0x00, (byte) 0x00, 0, null, (byte) (2*CURVE_SIZE));
            rData  = sendAPDU(comm);
            
            System.arraycopy(rData, 0, numPrevVerifications, 0, numPrevVerifications.length);
            System.out.println("NUMBER OF PREVIOUS VERIFICATIONS: " + Utils.byteArrayToBigInt(numPrevVerifications));
            
            System.arraycopy(rData, numPrevVerifications.length, epochHash_CARD, 0, epochHash_CARD.length);
            System.out.println("EPOCH HASH CARD:     " + Utils.byteArrayToBigInt(epochHash_CARD)); 
            System.out.println("EPOCH HASH TERMINAL: " + Utils.byteArrayToBigInt(epochHash)); 
        }
        
        //changing epoch, which resets numPrevVerifications to 0
        CURRENT_EPOCH = 1454563;
        epochHash = createEpochHash(); 
        System.out.println("                 !");
        System.out.println("CHANGING EPOCH TO A DIFFERENT ONE");
        System.out.println("                 !");
   
        for(int i =0; i<5; i++)
        {
            comm = createAPDUComm((byte) 0x80, (byte) 0x35, (byte) 0x00, (byte) 0x00, SHA1_SIZE, epochHash, (byte) 0x00);
            rData  = sendAPDU(comm);
            comm = createAPDUComm((byte) 0x80, (byte) 0x40, (byte) 0x00, (byte) 0x00, 0, null, (byte) (2*CURVE_SIZE));
            rData  = sendAPDU(comm);
            
            System.arraycopy(rData, 0, numPrevVerifications, 0, numPrevVerifications.length);
            System.out.println("NUMBER OF PREVIOUS VERIFICATIONS: " + Utils.byteArrayToBigInt(numPrevVerifications));
            
            System.arraycopy(rData, numPrevVerifications.length, epochHash_CARD, 0, epochHash_CARD.length);
            System.out.println("EPOCH HASH CARD:     " + Utils.byteArrayToBigInt(epochHash_CARD)); 
            System.out.println("EPOCH HASH TERMINAL: " + Utils.byteArrayToBigInt(epochHash)); 
        }      
    }
    
    // CHECK THAT R IS COMPUTED CORRECTLY
    private void checkR()
    {
        System.out.println("//*********************************//");
        System.out.println("//***********  Check r  ***********//");
        System.out.println("//*********************************//");
        
        ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("prime192v1");
        ECCurve curve = ecSpec.getCurve();
        //ECPoint G = ecSpec.getG();

        cardConnection();
        
        System.out.println("Select AID ");
        byte[] AID = new byte[]{(byte) 0x00, (byte) 0xA4, (byte) 0x04, (byte) 0x00, 
                                (byte) 0x04, 
                                (byte) 0xF0, (byte) 0x00, (byte) 0xE00, (byte) 0x01,
                                (byte) 0x00};
            
        byte[] rData = sendAPDU(AID);
        
        byte[] comm = createAPDUComm((byte) 0x80, (byte) 0x36, (byte) CURRENT_EPOCH, (byte) 0x00, 0, null, (byte) (CURVE_SIZE)); 
        rData  = sendAPDU(comm);
        
        byte[] br = new byte[CURVE_SIZE];
        System.arraycopy(rData, 0, br, 0, br.length);
        BigInteger r = Utils.byteArrayToBigInt(br);
        System.out.println("VALUE OF r: " + r);
        
        int comparevalue;
        BigInteger computed_r;
        
        for(int j=0; j<NUMBER_OF_LS;j++){
            for (int k=0; k<NUMBER_OF_LS;k++)
            {
                computed_r = alphas.get(0).multiply(ls.get(k)).add(alphas.get(1).multiply(ls.get(j)));
                computed_r = computed_r.mod(curve.getOrder());
                comparevalue = r.compareTo(computed_r);
                
                if (comparevalue == 0)
                {
                    System.out.println("r = (alpha1*l"+(k+1)+")+(alpha2*l"+(j+1)+")");
                    System.out.println("Correctly computed r.");
                }
            }
        }  
        System.out.println("VALUE OF alpha1 " + alphas.get(0));
        System.out.println("VALUE OF l3 " + ls.get(2));
    }
    
    private ECPoint checkC()
    {
        System.out.println("//*********************************//");
        System.out.println("//***********  Check c  ***********//");
        System.out.println("//*********************************//");
        
        ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("prime192v1");
        ECCurve curve = ecSpec.getCurve();
        ECPoint G = ecSpec.getG();

        cardConnection();
        
        System.out.println("Select AID ");
        byte[] AID = new byte[]{(byte) 0x00, (byte) 0xA4, (byte) 0x04, (byte) 0x00, 
                                (byte) 0x04, 
                                (byte) 0xF0, (byte) 0x00, (byte) 0xE00, (byte) 0x01,
                                (byte) 0x00};
            
        byte[] rData = sendAPDU(AID);

        byte[] comm = createAPDUComm((byte) 0x80, (byte) 0x37, (byte) 0x00, (byte) 0x00, 0, null, (byte) (CURVE_POINT_SIZE+3*CURVE_SIZE)); //
        rData  = sendAPDU(comm);
     
        byte[] bc = new byte[1+2*CURVE_SIZE];
        System.arraycopy(rData, 0, bc, 0, bc.length);
        ECPoint c = curve.decodePoint(bc);

        byte[] bc_exp = new byte[CURVE_SIZE];
        System.arraycopy(rData, bc.length, bc_exp, 0, bc_exp.length);
        BigInteger c_exp = Utils.byteArrayToBigInt(bc_exp);

        byte[] br = new byte[CURVE_SIZE];
        System.arraycopy(rData, bc.length+bc_exp.length, br, 0, br.length);
        BigInteger r = Utils.byteArrayToBigInt(br);
        
        byte[] brev = new byte[CURVE_SIZE];
        System.arraycopy(rData, bc.length+bc_exp.length+br.length, brev, 0, brev.length);
        BigInteger rev = Utils.byteArrayToBigInt(brev);
        
        System.out.println("VALUE OF revAttr on the card    " + rev);
        System.out.println("VALUE OF revAttr on the teminal " + revAttr);
     
        byte[] epochHashByte = createEpochHash();
        
        BigInteger exponent_of_c = r; // c = r
        exponent_of_c = exponent_of_c.add(Utils.byteArrayToBigInt(epochHashByte)); // c = r + currentEpoch
        exponent_of_c = exponent_of_c.subtract(revAttr); // c = r + currentEpoch - revAttr
 
        exponent_of_c = exponent_of_c.modInverse(curve.getOrder()); // c = 1/(r+currentEpoch-revAttr)
        System.out.println("VALUE OF c exponent computed on the card    " + c_exp);
        System.out.println("VALUE OF c exponent computed on the teminal " + exponent_of_c);
        
        //compute c ECPoint
        ECPoint computed_c = G.multiply(exponent_of_c);
        
        int comparevalue;
        BigInteger computed_r;
        System.out.println("VALUE OF r: " + r);

        for(int j=0; j<NUMBER_OF_LS;j++){
            for (int k=0; k<NUMBER_OF_LS;k++)
            {
                computed_r = alphas.get(0).multiply(ls.get(k)).add(alphas.get(1).multiply(ls.get(j)));
                computed_r = computed_r.mod(curve.getOrder());
                comparevalue = r.compareTo(computed_r);
                
                if (comparevalue == 0)
                {
                    System.out.println("r = (alpha1*l"+(k+1)+")+(alpha2*l"+(j+1)+")");
                    System.out.println("Correctly computed r.");
                }
            }
        }          

        comparevalue = c_exp.compareTo(exponent_of_c);
        if (comparevalue == 0)
        {
            System.out.println("Successfully computed exponent of c.");
        }
                else
        {
            System.out.println("Not quite yet with computed exponent of c. ");
        }

        if (c.equals(computed_c))
        {
            System.out.println("Successfully computed c.");
        }
        else
        {
            System.out.println("Not quite yet with computed c.");
        }

        System.out.println("VALUE OF alpha1 " + alphas.get(0));
        System.out.println("VALUE OF l3 " + ls.get(2));
        
        System.out.println("WITHIN THE CHECKC FUNCTION");
       blacklistCardForEpoch(curve, G);
       if (everyC_card_epoch.containsKey(c))
       {
            System.out.println("Successfully added c to the blacklist.");
        }
        else
        {
            System.out.println("C is not on the blacklist.");
        }
        
        return c;
    }
    
    // compares given c to the additionally computed one on card
    private void checkC(ECPoint cForCheck)
    {
        ECPoint computed_c = checkC();
        if(cForCheck.equals(computed_c))
        {
            System.out.println("C corresponds with computed c.");
        }
        else
        {
            System.out.println("C does not correspond with computed c.");
        }
    }
    
    private void checkTRevoke()
    { 
        System.out.println("//*********************************//");
        System.out.println("//*******  Check t_revoke  ********//");
        System.out.println("//*********************************//");
        
        ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("prime192v1");
        ECCurve curve = ecSpec.getCurve();

        cardConnection();
        
        System.out.println("Select AID ");
        byte[] AID = new byte[]{(byte) 0x00, (byte) 0xA4, (byte) 0x04, (byte) 0x00, 
                                (byte) 0x04, 
                                (byte) 0xF0, (byte) 0x00, (byte) 0xE00, (byte) 0x01,
                                (byte) 0x00};
            
        byte[] rData = sendAPDU(AID);

        byte[] comm = createAPDUComm((byte) 0x80, (byte) 0x38, (byte) 0x00, (byte) 0x00, 0, null, (byte) (2*CURVE_POINT_SIZE+2*CURVE_SIZE)); //
        rData  = sendAPDU(comm);

        byte[] bc = new byte[1+2*CURVE_SIZE];
        System.arraycopy(rData, 0, bc, 0, bc.length);
        ECPoint c = curve.decodePoint(bc);

        byte[] bt_revoke = new byte[1+2*CURVE_SIZE];
        System.arraycopy(rData, bc.length, bt_revoke, 0, bt_revoke.length);
        ECPoint tRevoke = curve.decodePoint(bt_revoke);
        
        byte[] brho_mR = new byte[CURVE_SIZE];
        System.arraycopy(rData, bc.length+bt_revoke.length, brho_mR, 0, brho_mR.length);
        BigInteger rho_mR = Utils.byteArrayToBigInt(brho_mR);
        
        byte[] brhoR = new byte[CURVE_SIZE];
        System.arraycopy(rData, bc.length+bt_revoke.length+brho_mR.length, brhoR, 0, brhoR.length);
        BigInteger rhoR = Utils.byteArrayToBigInt(brhoR);
        
        // t_revoke = c^rho_mR * c^rhoR
        ECPoint computed_tRevoke = c.multiply(rhoR);
        computed_tRevoke = computed_tRevoke.add(c.multiply(rho_mR));
        
        if (tRevoke.equals(computed_tRevoke))
        {
            System.out.println("Successfully computed t_revoke.");
        }
        else
        {
            System.out.println("Not quite yet with computed t_revoke.");
        }
    }
   
    
      private void checkSRevoke()
    { 
        System.out.println("//*********************************//");
        System.out.println("//*****  Check s_mR and s_r  ******//");
        System.out.println("//*********************************//");
        
        ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("prime192v1");
        ECCurve curve = ecSpec.getCurve();

        cardConnection();
        
        System.out.println("Select AID ");
        byte[] AID = new byte[]{(byte) 0x00, (byte) 0xA4, (byte) 0x04, (byte) 0x00, 
                                (byte) 0x04, 
                                (byte) 0xF0, (byte) 0x00, (byte) 0xE00, (byte) 0x01,
                                (byte) 0x00};
            
        byte[] rData = sendAPDU(AID);

        byte[] comm = createAPDUComm((byte) 0x80, (byte) 0x39, (byte) CURRENT_EPOCH, (byte) 0x00, 0, null, (byte) (6*CURVE_SIZE)); //
        rData  = sendAPDU(comm);
        
        byte[] bsr = new byte[CURVE_SIZE];
        System.arraycopy(rData, 0, bsr, 0, bsr.length);
        BigInteger sr = Utils.byteArrayToBigInt(bsr);
        System.out.println("VALUE OF s_r: " + sr);
        
        byte[] bsmr = new byte[CURVE_SIZE];
        System.arraycopy(rData, bsr.length, bsmr, 0, bsmr.length);
        BigInteger smr = Utils.byteArrayToBigInt(bsmr);
        System.out.println("VALUE OF s_mR: " + smr);
        
        byte[] brhor = new byte[CURVE_SIZE];
        System.arraycopy(rData, bsr.length+bsmr.length, brhor, 0, brhor.length);
        BigInteger rho_r = Utils.byteArrayToBigInt(brhor);
        System.out.println("VALUE OF rho_r: " + rho_r);
        
        byte[] brho_mR = new byte[CURVE_SIZE];
        System.arraycopy(rData, bsr.length+bsmr.length+brhor.length, brho_mR, 0, brho_mR.length);
        BigInteger rho_mR = Utils.byteArrayToBigInt(brho_mR);
        System.out.println("VALUE OF rho_mR: " + rho_mR);
        
        byte[] be = new byte[CURVE_SIZE];
        System.arraycopy(rData, bsr.length+bsmr.length+brhor.length+brho_mR.length, be, 0, be.length);
        BigInteger e = Utils.byteArrayToBigInt(be);
        System.out.println("VALUE OF e: " + e);
        
        byte[] br = new byte[CURVE_SIZE];
        System.arraycopy(rData, bsr.length+bsmr.length+brhor.length+be.length+brho_mR.length, br, 0, br.length);
        BigInteger r = Utils.byteArrayToBigInt(br);
        System.out.println("VALUE OF r: " + r);
       
    
        //int comparevalue;
        BigInteger computed_sr;
        computed_sr = rho_r.add(e.multiply(r));
        computed_sr = computed_sr.mod(curve.getOrder());
        System.out.println("VALUE OF s_r computed on terminal: " + computed_sr);
        
        if (sr.equals(computed_sr))
        {
            System.out.println("Successfully computed sr.");
        }
        else
        {
            System.out.println("Not quite yet with computed sr.");
        }
        
        BigInteger computed_smR;
        computed_smR = rho_mR.subtract(e.multiply(revAttr));
        computed_smR = computed_smR.mod(curve.getOrder());
        System.out.println("VALUE OF s_mR computed on terminal: " + computed_smR); //computed_smR);
        System.out.println("VALUE OF s_mR:                      " + smr);
        
        if (smr.equals(computed_smR))
        {
            System.out.println("Successfully computed s_mR.");
        }
        else
        {
            System.out.println("Not quite yet with computed s_mR.");
        }       
    } 
      
    private void blacklistCardForEpoch(ECCurve curve, ECPoint G)
    {
        byte[] epochHashByte = createEpochHash();
        BigInteger computed_exponent_c;
        ECPoint c;
        int i = 0;
        
        for(int j=0; j<NUMBER_OF_LS;j++)
        {
            for (int k=0; k<NUMBER_OF_LS;k++)
            {
                computed_exponent_c = alphas.get(0).multiply(ls.get(j)).add(alphas.get(1).multiply(ls.get(k))); // exp_c = alpha1 * l_k + alpha2 * l_j
                computed_exponent_c = computed_exponent_c.mod(curve.getOrder()); // exp_c = r mod q
                computed_exponent_c = computed_exponent_c.add(Utils.byteArrayToBigInt(epochHashByte)); // c = r + currentEpoch
                computed_exponent_c = computed_exponent_c.subtract(revAttr); // exp_c = r + currentEpoch - revAttr
                computed_exponent_c = computed_exponent_c.modInverse(curve.getOrder()); // exp_c = 1/(r+currentEpoch-revAttr)
                c = G.multiply(computed_exponent_c); // c = g^(1/(r+currentEpoch-revAttr))
                everyC_card_epoch.put(c, i); // store computed c on the i place of computed c hashmap
                i++;
            }
        }
    }
}
