package application;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;

import java.math.BigInteger;
import java.net.URL;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.ResourceBundle;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.smartcardio.*;
import javax.xml.bind.DatatypeConverter;

import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.macs.CMac;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Hex;

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.Label;

public class SampleController implements Initializable {
	
	ObservableList<String> nistList = FXCollections.observableArrayList("AES128, EC-256 (NIST), SHA256","AES256, EC-512 (NIST), SHA256","AES256, EC-512 (Brainpool), SHA256"); 
	
	@FXML
	private Label cardConLabel;
	
	@FXML
	private Label taLabel, tbLabel, sLabel, kpiLabel, hxLabel, hyLabel, kxLabel, kyLabel, kencLabel, kmacLabel, kmac2Label, paceLabel; 
	
	@FXML
	private Label cas1Label, cas2Label, cas3Label, cas4Label, cas5Label;

	@FXML
	private ChoiceBox<CardTerminal> choiceBox;
	
	@FXML
	private ChoiceBox<String> choiceBoxNIST;
	
	public Card card;
	private long startTime, stopTime, startTimeFull, stopTimeFull;
	
	// Pripojeni karty, protokol T=1
	public void Connect (ActionEvent event){
		try{
		CardTerminal terminal = choiceBox.getValue();
		card = terminal.connect("T=1");
		cardConLabel.setText("Karta: " + card);
		}catch (CardException e) {
			cardConLabel.setText("Karta není ve čtečce");
		}		
	}
	
	// Odpojeni karty
	public void Disconnect (ActionEvent event){
		try{
	    card.disconnect(false);
		cardConLabel.setText("Karta odpojena");
		}catch (CardException e) {
			cardConLabel.setText("Karta není ve čtečce");
		}		
	}
	
	// PACE
	public void doPace (ActionEvent paceRun) throws Exception{
		CardChannel channel = null;
		paceLabel.setText("");
		try{
		channel = card.getBasicChannel();
		}catch (Exception e) {
			paceLabel.setText("Není načtená karta");
			return;
		}
		
		int c, i;
		String apdu, heslo, X, Y, s, cmacTa;
		ResponseAPDU r;
		MessageDigest md;
		byte[] Kpi, decrypteds, xb, yb, YaTb, Kenc, Kmac, Kmac2, macTa, macTb;
		SecretKeySpec skeySpec;
		Cipher cipher;
		ECParameterSpec params;
		ECCurve curve;
		ECPoint G, Xb, Xa, H, Gstr, Yb, Ya, K;
		BigInteger Xax, Xay, Yax, Yay;
		CipherParameters params2;
		BlockCipher aes;
		CMac mac;
		
		
		// Switch pro vyber parametru PACE
		
		String NISTparam = choiceBoxNIST.getValue();
		switch(NISTparam){
		case "AES256, EC-512 (NIST), SHA256":
			
			// Zaslani zvolene krivky na kartu
			c = 19;
			apdu = "880800000200" + Integer.toHexString(c);
			r = channel.transmit(new CommandAPDU(DatatypeConverter.parseHexBinary(apdu)));
			
			startTimeFull = System.currentTimeMillis();
			
	        // Hash tajneho hesla + 0
	        heslo = "tajne heslo";
	        md = MessageDigest.getInstance("SHA-256");
	        md.update(new byte[1]);
	        md.update(heslo.getBytes());
			Kpi = md.digest();
			md.reset();
			kpiLabel.setText(Hex.toHexString(Kpi));
			
			//Ziskani "z" a desifrovani "s"
			apdu = "8800000012";
			r = channel.transmit(new CommandAPDU(DatatypeConverter.parseHexBinary(apdu)));
			
			skeySpec = new SecretKeySpec(Kpi, "AES");
			cipher = Cipher.getInstance("AES/ECB/NoPadding");
	        cipher.init(Cipher.DECRYPT_MODE, skeySpec);
	        
	        decrypteds = cipher.doFinal(Arrays.copyOfRange(r.getBytes(), 0, 16));
	        sLabel.setText(Hex.toHexString(decrypteds));
	        
	        
	        // Ziskani parametru krivky
	        // Krivku sice zname, jelikoz jsme ji zvolili na zacatku, ale pro korektnost protokolu ziskame udaje z karty
	        apdu = "8801000004";
			r = channel.transmit(new CommandAPDU(DatatypeConverter.parseHexBinary(apdu)));
			i = Integer.parseInt(Hex.toHexString(Arrays.copyOfRange(r.getBytes(), 0, 2)), 16);
	        
			// Nastaveni krivky v jazyce Java dle cisla krivky na strane Basic card
			
	        switch(i){
	        case 13: params = ECNamedCurveTable.getParameterSpec("brainpoolp512r1");
			 		 break;
	        case 19: params = ECNamedCurveTable.getParameterSpec("P-521");
	        		 break;
	        case 17: params = ECNamedCurveTable.getParameterSpec("P-256");
			 		 break;	
			default: params = ECNamedCurveTable.getParameterSpec("P-521");
					 break;
	        }
	        curve = params.getCurve();
	        
	        stopTime = System.currentTimeMillis() - startTimeFull;
	        cas1Label.setText("1. fáze: " + stopTime + "ms");
	        startTime = System.currentTimeMillis();
	        
	        // Vypocet Xb
	        xb = new byte[16];
	        new Random().nextBytes(xb);
	        
	        G = params.getG();
	        Xb = G.multiply(new BigInteger(xb));
	        
	        // Poslat Xb na kartu
	        // U krivky NIST P-512 je nutne zarovnat souradnice na 66B (132 znaku)
	        X = padding(Xb.normalize().getXCoord().toString(),132);
	        Y = padding(Xb.normalize().getYCoord().toString(),132);
	        
	        apdu = "8802000084" + X + Y;
	        r = channel.transmit(new CommandAPDU(DatatypeConverter.parseHexBinary(apdu)));
	        
	        // Ziskat Xa z karty
	        apdu = "8803000084";
			r = channel.transmit(new CommandAPDU(DatatypeConverter.parseHexBinary(apdu)));
			Xax = new BigInteger(Arrays.copyOfRange(r.getBytes(), 0, 66));
			Xay = new BigInteger(Arrays.copyOfRange(r.getBytes(), 66, 132));
			Xa = curve.createPoint(Xax, Xay);
			
			// Vypocet H
			H = Xa.multiply(new BigInteger(xb));
			hxLabel.setText(H.normalize().getXCoord().toString());
	        hyLabel.setText(H.normalize().getYCoord().toString());
	       
			
			// Vypocet Gstr
			s = Hex.toHexString(decrypteds);
			
			Gstr = G.multiply(new BigInteger(s,16));
			Gstr = Gstr.add(H);
			
			stopTime = System.currentTimeMillis() - startTime;
	        cas2Label.setText("2. fáze: " + stopTime + "ms");
	        startTime = System.currentTimeMillis();
	        
	        // Vypocet Yb
	        yb = new byte[16];
	        new Random().nextBytes(yb);
	        
	        Yb = Gstr.multiply(new BigInteger(yb));
	        
	        // Poslat Yb na kartu
	        // U krivky NIST P-512 je nutne zarovnat souradnice na 66B (132 znaku)
	        X = padding(Yb.normalize().getXCoord().toString(),132);
	        Y = padding(Yb.normalize().getYCoord().toString(),132);
	        
	        // Ulozeni souradnic bodu Yb pro pozdejsi pouziti u MAC Ta
	        cmacTa = X+Y;
	        
	        apdu = "8804000084" + X + Y;
	        r = channel.transmit(new CommandAPDU(DatatypeConverter.parseHexBinary(apdu)));
	        
	        // Ziskat Ya z karty
	        apdu = "8805000084";
			r = channel.transmit(new CommandAPDU(DatatypeConverter.parseHexBinary(apdu)));
			
			Yax = new BigInteger(Arrays.copyOfRange(r.getBytes(), 0, 66));
			Yay = new BigInteger(Arrays.copyOfRange(r.getBytes(), 66, 132));
			Ya = curve.createPoint(Yax, Yay);
			YaTb = Arrays.copyOfRange(r.getBytes(), 0, 132);
	        
			// Vypocet K a zarovnani na 66B (132 znaku) pro spravny vypocet hashu
			K = Ya.multiply(new BigInteger(yb));
			kxLabel.setText(K.normalize().getXCoord().toString());
			kyLabel.setText(K.normalize().getYCoord().toString());
			
			X = padding(K.normalize().getXCoord().toString(),132);
	        Y = padding(K.normalize().getYCoord().toString(),132);
	        
	        stopTime = System.currentTimeMillis() - startTime;
	        cas3Label.setText("3. fáze: " + stopTime + "ms");
	        startTime = System.currentTimeMillis();
			
			// Hashe Kenc, Kmac, Kmac' a CMAC
	        md.update(DatatypeConverter.parseHexBinary("01"));
	        md.update(DatatypeConverter.parseHexBinary(X));
	        md.update(DatatypeConverter.parseHexBinary(Y));
			Kenc = md.digest();
			md.reset();
			
			md.update(DatatypeConverter.parseHexBinary("02"));
	        md.update(DatatypeConverter.parseHexBinary(X));
	        md.update(DatatypeConverter.parseHexBinary(Y));
			Kmac = md.digest();
			md.reset();
			
	        md.update(DatatypeConverter.parseHexBinary("03"));
	        md.update(DatatypeConverter.parseHexBinary(X));
	        md.update(DatatypeConverter.parseHexBinary(Y));
			Kmac2 = md.digest();
			md.reset();
			
			
			kencLabel.setText(Hex.toHexString(Kenc));
			kmacLabel.setText(Hex.toHexString(Kmac));
			kmac2Label.setText(Hex.toHexString(Kmac2));
			
			// Vypocet MAC Ta a ziskani Ta z karty
			macTa = new byte[16];
			params2 = new KeyParameter(Kmac2);
		    aes = new AESEngine();
		    mac = new CMac(aes);
		    mac.init(params2);
		    mac.update(DatatypeConverter.parseHexBinary(cmacTa),0,132);
		    mac.update(DatatypeConverter.parseHexBinary("0013"),0,2);
		    mac.doFinal(macTa, 0);
		    mac.reset();
		    
		    apdu = "8806000012";
			r = channel.transmit(new CommandAPDU(DatatypeConverter.parseHexBinary(apdu)));
			
			// Kontrola, jestli se Ta z karty shoduje s vypoctenym
			if(Hex.toHexString(macTa).equals(Hex.toHexString(Arrays.copyOfRange(r.getBytes(), 0, 16)))) taLabel.setText("MAC Ta se shoduje");
			else{
				taLabel.setText("MAC Ta se neshoduje");
			}
			
			// Vypocet MAC Tb
			
			macTb = new byte[16];
			params2 = new KeyParameter(Kmac2);
		    aes = new AESEngine();
		    mac = new CMac(aes);
		    mac.init(params2);
		    mac.update(YaTb, 0, 132);
		    mac.update(DatatypeConverter.parseHexBinary("0013"), 0, 2);
		    mac.doFinal(macTb, 0);
		    
			apdu = "8807000010" + Hex.toHexString(macTb);
			r = channel.transmit(new CommandAPDU(DatatypeConverter.parseHexBinary(apdu)));
			
			// Kontrola, jestli karta odpovedela, ze se Tb shoduje
			if(Hex.toHexString(r.getBytes()).equals("9000")) tbLabel.setText("MAC Tb se shoduje");
			else{
				tbLabel.setText("MAC Tb se neshoduje");
			}
			
			stopTime = System.currentTimeMillis() - startTime;
	        cas4Label.setText("4. fáze: " + stopTime + "ms");
	        
			stopTimeFull = System.currentTimeMillis() - startTimeFull;
			cas5Label.setText("celkem: " + stopTimeFull + "ms");
			break;
			
		case "AES256, EC-512 (Brainpool), SHA256":
			c = 13;
			apdu = "8808000002000" + Integer.toHexString(c);
			r = channel.transmit(new CommandAPDU(DatatypeConverter.parseHexBinary(apdu)));
			
			startTimeFull = System.currentTimeMillis();
	        // Hash tajneho hesla + 0
	        heslo = "tajne heslo";
	        
	        md = MessageDigest.getInstance("SHA-256");
	        md.update(new byte[1]);
	        md.update(heslo.getBytes());
			Kpi = md.digest();
			md.reset();
			kpiLabel.setText(Hex.toHexString(Kpi));
			
			//Ziskani "z" a desifrovani "s"
			apdu = "8800000080";
			r = channel.transmit(new CommandAPDU(DatatypeConverter.parseHexBinary(apdu)));
			
			skeySpec = new SecretKeySpec(Kpi, "AES");
			cipher = Cipher.getInstance("AES/ECB/NoPadding");
	        cipher.init(Cipher.DECRYPT_MODE, skeySpec);
	        
	        decrypteds = cipher.doFinal(Arrays.copyOfRange(r.getBytes(), 0, 16));
	        sLabel.setText(Hex.toHexString(decrypteds));
	        
	        
	        // Ziskani parametru krivky
	        apdu = "8801000080";
			r = channel.transmit(new CommandAPDU(DatatypeConverter.parseHexBinary(apdu)));
			i = Integer.parseInt(Hex.toHexString(Arrays.copyOfRange(r.getBytes(), 0, 2)), 16);
	        
			
	        switch(i){
	        case 13: params = ECNamedCurveTable.getParameterSpec("brainpoolp512r1");
			 		 break;
	        case 19: params = ECNamedCurveTable.getParameterSpec("P-521");
	        		 break;
	        case 17: params = ECNamedCurveTable.getParameterSpec("P-256");
			 		 break;	
			default: params = ECNamedCurveTable.getParameterSpec("P-521");
					 break;
	        }
	        curve = params.getCurve();
	        
	        stopTime = System.currentTimeMillis() - startTimeFull;
	        cas1Label.setText("1. fáze: " + stopTime + "ms");
	        startTime = System.currentTimeMillis();
	        
	        // Vypocet Xb
	        xb = new byte[16];
	        new Random().nextBytes(xb);
	        
	        G = params.getG();
	        Xb = G.multiply(new BigInteger(xb));
	        
	        // Poslat Xb na kartu
	        X = padding(Xb.normalize().getXCoord().toString(),128);
	        Y = padding(Xb.normalize().getYCoord().toString(),128);
	        
	        apdu = "8802000080" + X + Y;
	        r = channel.transmit(new CommandAPDU(DatatypeConverter.parseHexBinary(apdu)));
	        
	        // Ziskat Xa z karty
	        apdu = "8803000080";
			r = channel.transmit(new CommandAPDU(DatatypeConverter.parseHexBinary(apdu)));
			Xax = new BigInteger(1,Arrays.copyOfRange(r.getBytes(), 0, 64));
			Xay = new BigInteger(1,Arrays.copyOfRange(r.getBytes(), 64, 128));
			Xa = curve.createPoint(Xax, Xay);
			
			// Vypocet H
			H = Xa.multiply(new BigInteger(xb));
			hxLabel.setText(H.normalize().getXCoord().toString());
	        hyLabel.setText(H.normalize().getYCoord().toString());
	       
			
			// Vypocet Gstr
			s = Hex.toHexString(decrypteds);
			
			Gstr = G.multiply(new BigInteger(s,16));
			Gstr = Gstr.add(H);
			
			stopTime = System.currentTimeMillis() - startTime;
	        cas2Label.setText("2. fáze: " + stopTime + "ms");
	        startTime = System.currentTimeMillis();
	        
	        // Vypocet Yb
	        yb = new byte[16];
	        new Random().nextBytes(yb);
	        
	        Yb = Gstr.multiply(new BigInteger(yb));
	        
	        // Poslat Yb na kartu
	        X = padding(Yb.normalize().getXCoord().toString(),128);
	        Y = padding(Yb.normalize().getYCoord().toString(),128);
	        
	        cmacTa = X+Y;
	        
	        apdu = "8804000080" + X + Y;
	        r = channel.transmit(new CommandAPDU(DatatypeConverter.parseHexBinary(apdu)));
	        
	        // Ziskat Ya z karty
	        apdu = "8805000080";
			r = channel.transmit(new CommandAPDU(DatatypeConverter.parseHexBinary(apdu)));
			Yax = new BigInteger(1,Arrays.copyOfRange(r.getBytes(), 0, 64));
			Yay = new BigInteger(1,Arrays.copyOfRange(r.getBytes(), 64, 128));
			Ya = curve.createPoint(Yax, Yay);
			YaTb = Arrays.copyOfRange(r.getBytes(), 0, 128);
	        
			// Vypocet K a zarovnani na 128B pro spravny vypocet hashu
			K = Ya.multiply(new BigInteger(yb));
			kxLabel.setText(K.normalize().getXCoord().toString());
			kyLabel.setText(K.normalize().getYCoord().toString());
			
			X = padding(K.normalize().getXCoord().toString(),128);
	        Y = padding(K.normalize().getYCoord().toString(),128);
			
	        stopTime = System.currentTimeMillis() - startTime;
	        cas3Label.setText("3. fáze: " + stopTime + "ms");
	        startTime = System.currentTimeMillis();
	        
			// Hashe a CMAC
	        
	        md.update(DatatypeConverter.parseHexBinary("01"));
	        md.update(DatatypeConverter.parseHexBinary(X));
	        md.update(DatatypeConverter.parseHexBinary(Y));
			Kenc = md.digest();
			md.reset();
			
			md.update(DatatypeConverter.parseHexBinary("02"));
	        md.update(DatatypeConverter.parseHexBinary(X));
	        md.update(DatatypeConverter.parseHexBinary(Y));
			Kmac = md.digest();
			md.reset();
			
			md.update(DatatypeConverter.parseHexBinary("03"));
	        md.update(DatatypeConverter.parseHexBinary(X));
	        md.update(DatatypeConverter.parseHexBinary(Y));
			Kmac2 = md.digest();
			md.reset();
			
			
			kencLabel.setText(Hex.toHexString(Kenc));
			kmacLabel.setText(Hex.toHexString(Kmac));
			kmac2Label.setText(Hex.toHexString(Kmac2));
			
			
			macTa = new byte[16];
			params2 = new KeyParameter(Kmac2);
		    aes = new AESEngine();
		    mac = new CMac(aes);
		    mac.init(params2);
		    mac.update(DatatypeConverter.parseHexBinary(cmacTa), 0, 128);
		    mac.update(DatatypeConverter.parseHexBinary("000d"), 0, 2);
		    mac.doFinal(macTa, 0);
		    mac.reset();
		    
		    apdu = "8806000080";
			r = channel.transmit(new CommandAPDU(DatatypeConverter.parseHexBinary(apdu)));
			
			if(Hex.toHexString(macTa).equals(Hex.toHexString(Arrays.copyOfRange(r.getBytes(), 0, 16)))) taLabel.setText("MAC Ta se shoduje");
			else{
				taLabel.setText("MAC Ta se neshoduje");
			}
			
			// CMAC Tb
			
			macTb = new byte[16];
			params2 = new KeyParameter(Kmac2);
		    aes = new AESEngine();
		    mac = new CMac(aes);
		    mac.init(params2);
		    mac.update(YaTb, 0, 128);
		    mac.update(DatatypeConverter.parseHexBinary("000d"), 0, 2);
		    mac.doFinal(macTb, 0);
		    
			apdu = "8807000010" + Hex.toHexString(macTb);
			r = channel.transmit(new CommandAPDU(DatatypeConverter.parseHexBinary(apdu)));
			
			if(Hex.toHexString(r.getBytes()).equals("9000")) tbLabel.setText("MAC Tb se shoduje");
			else{
				tbLabel.setText("MAC Tb se neshoduje");
			}

			stopTime = System.currentTimeMillis() - startTime;
	        cas4Label.setText("4. fáze: " + stopTime + "ms");
	        
			stopTimeFull = System.currentTimeMillis() - startTimeFull;
			cas5Label.setText("celkem: " + stopTimeFull + "ms");
			
			break;
			
		case "AES128, EC-256 (NIST), SHA256":
			c = 17;
			apdu = "880800000200" + Integer.toHexString(c);
			r = channel.transmit(new CommandAPDU(DatatypeConverter.parseHexBinary(apdu)));
			
			startTimeFull = System.currentTimeMillis();
	        // Hash tajneho hesla + 0
	        heslo = "tajne heslo";
	        
	        md = MessageDigest.getInstance("SHA-256");
	        md.update(new byte[1]);
	        md.update(heslo.getBytes());
			Kpi = md.digest();
			md.reset();
			kpiLabel.setText(Hex.toHexString(Kpi));
			
			//Ziskani "z" a desifrovani "s"
			apdu = "8800000080";
			r = channel.transmit(new CommandAPDU(DatatypeConverter.parseHexBinary(apdu)));
			
			skeySpec = new SecretKeySpec(Arrays.copyOfRange(Kpi,0,16), "AES");
			cipher = Cipher.getInstance("AES/ECB/NoPadding");
	        cipher.init(Cipher.DECRYPT_MODE, skeySpec);
	        
	        decrypteds = cipher.doFinal(Arrays.copyOfRange(r.getBytes(), 0, 16));
	        sLabel.setText(Hex.toHexString(decrypteds));
	        
	        
	        // Ziskani parametru krivky
	        apdu = "8801000080";
			r = channel.transmit(new CommandAPDU(DatatypeConverter.parseHexBinary(apdu)));
			i = Integer.parseInt(Hex.toHexString(Arrays.copyOfRange(r.getBytes(), 0, 2)), 16);
	        
			
	        switch(i){
	        case 13: params = ECNamedCurveTable.getParameterSpec("brainpoolp512r1");
			 		 break;
	        case 19: params = ECNamedCurveTable.getParameterSpec("P-521");
	        		 break;
	        case 17: params = ECNamedCurveTable.getParameterSpec("P-256");
			 		 break;	
			default: params = ECNamedCurveTable.getParameterSpec("P-521");
					 break;
	        }
	        curve = params.getCurve();
	        
	        stopTime = System.currentTimeMillis() - startTimeFull;
	        cas1Label.setText("1. fáze: " + stopTime + "ms");
	        startTime = System.currentTimeMillis();
	        
	        // Vypocet Xb
	        xb = new byte[16];
	        new Random().nextBytes(xb);
	        
	        G = params.getG();
	        Xb = G.multiply(new BigInteger(xb));
	        
	        // Poslat Xb na kartu
	        X = padding(Xb.normalize().getXCoord().toString(),64);
	        Y = padding(Xb.normalize().getYCoord().toString(),64);
	        
	        apdu = "8802000040" + X + Y;
	        r = channel.transmit(new CommandAPDU(DatatypeConverter.parseHexBinary(apdu)));
	        
	        // Ziskat Xa z karty
	        apdu = "8803000040";
			r = channel.transmit(new CommandAPDU(DatatypeConverter.parseHexBinary(apdu)));
			Xax = new BigInteger(1,Arrays.copyOfRange(r.getBytes(), 0, 32));
			Xay = new BigInteger(1,Arrays.copyOfRange(r.getBytes(), 32, 64));
			Xa = curve.createPoint(Xax, Xay);
			
			// Vypocet H
			H = Xa.multiply(new BigInteger(xb));
			hxLabel.setText(H.normalize().getXCoord().toString());
	        hyLabel.setText(H.normalize().getYCoord().toString());
	       
			
			// Vypocet Gstr
			s = Hex.toHexString(decrypteds);
			
			Gstr = G.multiply(new BigInteger(s,16));
			Gstr = Gstr.add(H);
			
			stopTime = System.currentTimeMillis() - startTime;
	        cas2Label.setText("2. fáze: " + stopTime + "ms");
	        startTime = System.currentTimeMillis();
	        
	        // Vypocet Yb
	        yb = new byte[16];
	        new Random().nextBytes(yb);
	        
	        Yb = Gstr.multiply(new BigInteger(yb));
	        
	        // Poslat Yb na kartu
	        X = padding(Yb.normalize().getXCoord().toString(),64);
	        Y = padding(Yb.normalize().getYCoord().toString(),64);
	        
	        cmacTa = X+Y;
	        
	        apdu = "8804000040" + X + Y;
	        r = channel.transmit(new CommandAPDU(DatatypeConverter.parseHexBinary(apdu)));
	        
	        // Ziskat Ya z karty
	        apdu = "8805000080";
			r = channel.transmit(new CommandAPDU(DatatypeConverter.parseHexBinary(apdu)));
			Yax = new BigInteger(1,Arrays.copyOfRange(r.getBytes(), 0, 32));
			Yay = new BigInteger(1,Arrays.copyOfRange(r.getBytes(), 32, 64));
			Ya = curve.createPoint(Yax, Yay);
			YaTb = Arrays.copyOfRange(r.getBytes(), 0, 64);
	        
			// Vypocet K a zarovnani na 64B pro spravny vypocet hashu
			K = Ya.multiply(new BigInteger(yb));
			kxLabel.setText(K.normalize().getXCoord().toString());
			kyLabel.setText(K.normalize().getYCoord().toString());
			
			
	        X = padding(K.normalize().getXCoord().toString(),64);
	        Y = padding(K.normalize().getYCoord().toString(),64);
	        
	        stopTime = System.currentTimeMillis() - startTime;
	        cas3Label.setText("3. fáze: " + stopTime + "ms");
	        startTime = System.currentTimeMillis();
			
			// Hashe a CMAC
	        
			md.update(DatatypeConverter.parseHexBinary("01"));
	        md.update(DatatypeConverter.parseHexBinary(X));
	        md.update(DatatypeConverter.parseHexBinary(Y));
			Kenc = md.digest();
			md.reset();
			
			md.update(DatatypeConverter.parseHexBinary("02"));
	        md.update(DatatypeConverter.parseHexBinary(X));
	        md.update(DatatypeConverter.parseHexBinary(Y));
			Kmac = md.digest();
			md.reset();
			
	        md.update(DatatypeConverter.parseHexBinary("03"));
	        md.update(DatatypeConverter.parseHexBinary(X));
	        md.update(DatatypeConverter.parseHexBinary(Y));
			Kmac2 = md.digest();
			md.reset();
			
			
			kencLabel.setText(Hex.toHexString(Kenc));
			kmacLabel.setText(Hex.toHexString(Kmac));
			kmac2Label.setText(Hex.toHexString(Kmac2));
			
			macTa = new byte[16];
			params2 = new KeyParameter(Kmac2);
		    aes = new AESEngine();
		    mac = new CMac(aes);
		    mac.init(params2);
		    mac.update(DatatypeConverter.parseHexBinary(cmacTa), 0, 64);
		    mac.update(DatatypeConverter.parseHexBinary("0011"), 0, 2);
		    mac.doFinal(macTa, 0);
		    mac.reset();
		    
		    apdu = "8806000080";
			r = channel.transmit(new CommandAPDU(DatatypeConverter.parseHexBinary(apdu)));
			
			if(Hex.toHexString(macTa).equals(Hex.toHexString(Arrays.copyOfRange(r.getBytes(), 0, 16)))) taLabel.setText("MAC Ta se shoduje");
			else{
				taLabel.setText("MAC Ta se neshoduje");
			}
			
			// CMAC Tb
			
			macTb = new byte[16];
			params2 = new KeyParameter(Kmac2);
		    aes = new AESEngine();
		    mac = new CMac(aes);
		    mac.init(params2);
		    mac.update(YaTb, 0, 64);
		    mac.update(DatatypeConverter.parseHexBinary("0011"), 0, 2);
		    mac.doFinal(macTb, 0);
		    
			apdu = "8807000010" + Hex.toHexString(macTb);
			r = channel.transmit(new CommandAPDU(DatatypeConverter.parseHexBinary(apdu)));
			
			if(Hex.toHexString(r.getBytes()).equals("9000")) tbLabel.setText("MAC Tb se shoduje");
			else{
				tbLabel.setText("MAC Tb se neshoduje");
			}
			
			stopTime = System.currentTimeMillis() - startTime;
	        cas4Label.setText("4. fáze: " + stopTime + "ms");
	        
			stopTimeFull = System.currentTimeMillis() - startTimeFull;
			cas5Label.setText("celkem: " + stopTimeFull + "ms");
			
			break;
			
		default:
            throw new IllegalArgumentException("Zvol parametry PACE");
		}
		
		
        
		
	}

	@Override
	public void initialize(URL location, ResourceBundle resource){
	    try {		
		// Vypis dostupnych terminalu
	    TerminalFactory factory = TerminalFactory.getDefault();
	    List<CardTerminal> terminals = factory.terminals().list();
		ObservableList<CardTerminal> terminals2 = FXCollections.observableArrayList(terminals);
		choiceBox.setValue(terminals.get(0));
		choiceBox.setItems(terminals2);
		choiceBoxNIST.setValue("AES128, EC-256 (NIST), SHA256");
		choiceBoxNIST.setItems(nistList);
		} catch (CardException e) {
			e.printStackTrace();
		}
	    
	}
	
	private String padding(String s, int l){
		if(s.length()!=l) {
        	int j = l - s.length();
        	String vypln = new String(new char[j]).replace("\0", "0");
        	s = vypln + s;
		}
		return s;	
	}
	
	
}
