package sut;

import java.io.File;
import java.io.IOException;

import javax.smartcardio.*;
import javax.swing.JOptionPane;


/**
 * API for "coarse" high-level testing of an EMV card.
 * <p>
 * Methods for testing typically return a boolean, which is true if the
 * card responds with 0x9000, and false if the card responds anything 
 * else.
 * <p>
 * Any Exceptions escaping from these method signal, incl CardException,
 * indicate low-level failures that the testing framework should not try 
 * to interpret. These are not the TOE's fault, but problems with the 
 * test infrastructure (incl. the card reader).
 */


//@SuppressWarnings("restriction")
public class EMVTestService {
	private Card card;
	private CardChannel channel;
	private byte[] pin;
	private byte[] application;
	private byte[] pdol;
	private byte[] ddol;
	private byte[] cdol1;
	private byte[] cdol2;
	private byte[] record_valid;
	private byte[] record_invalid;
	private byte[] data_valid;
	private byte[] data_invalid;
	private boolean logging =  true;
	private boolean pin_enabled = false;
	private int ac_type_position;
	
	private byte[] lastATC = {0x00, 0x00};
	
    /* AC types. To be used as P1 in the GENERATE AC command */
	public static final byte TC = 0x40;
	public static final byte ARQC = (byte)0x80;
	public static final byte AAC = 0x00;
	
    /* EMV Tags. To be used as P1 and P2 in the GET DATA command */
	public static final byte[] ATC = {(byte)0x9F, 0x36};
	public static final byte[] LastOnlineATC = {(byte)0x9F, 0x13};
	public static final byte[] PINTryCounter = {(byte)0x9F, 0x17};
	public static final byte[] LogFormat = {(byte)0x9F, 0x4F};
	
	public static EMVTestService createEMVTestService() throws CardException {
		System.setProperty("sun.security.smartcardio.t0GetResponse", "false");
		System.setProperty("sun.security.smartcardio.t1GetResponse", "false");
		
		//Card card = TerminalFactory.getDefault().terminals().list().get(0).connect("*");
		Card card = TerminalFactory.getDefault().terminals().list().get(2).connect("*");
		
		CardChannel channel = card.getBasicChannel();
		EMVTestService service = new EMVTestService(card, channel);
		return service;
	}
	
	protected EMVTestService(Card card, CardChannel channel) {
		this.card = card;
		this.channel = channel;
		
		String pin = (String)JOptionPane.showInputDialog("Enter PIN code (will be displayed here!)");
		if(!pin.isEmpty()) {
			System.out.println("Set PIN");
			setPIN(pin);
		}
/*		
 		// For use with cards 4,5,6,7,8,ASN,Ronny
		// MAESTRO
		setApplication("A0000000043060");
		setPDOL("83 00");
		setDDOL("36 25 2E 81");
		setCDOL("00 00 00 00 00 05 00 00 00 00 00 00 05 28 00 00 00 80 00 09 78 11 05 19 00 36 25 2E 81 21 00 00 68 3A 99 73 7E E7 CB 41 04 03 02", "3E 85 5D D7 69 BB 60 39 03 18 30 30 00 00 00 80 00 36 25 2E 81 68 3A 99 73 7E E7 CB 41");
		setRecords("0101", "0110");
		setData("9F17","9F36");		
		//setPIN("4165");
		setACTypePosition(5);
*/


/*
		// For use with cards 4,5,6,7,8,ASN,Ronny
		// SecureCode Aut
		setApplication("A0000000048002");
		setPDOL("83 00");
		setDDOL("36 25 2E 81");
		setCDOL("00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 34 00 00 00 00 00 00 00 00 00 00 01 00 02",	"00 00 00 00 00 00 00 00 00 00 5A 33 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00");
		setRecords("0101", "0110");
		setData("9F17","9F36");		
		//setPIN("4165");
		setACTypePosition(5);
*/
		
/*
		// For use with cards 4,5 
		// PIN
		setApplication("A00000031510100528");
		setPDOL("83 00");
		setDDOL("36 25 2E 81");
		setCDOL("00 00 00 00 00 05 00 00 00 00 00 00 05 28 00 00 00 80 00 09 78 11 05 19 00 36 25 2E 81 21 00 00 68 3A 99 73 7E E7 CB 41 04 03 02", "3E 85 5D D7 69 BB 60 39 03 18 30 30 00 00 00 80 00 36 25 2E 81 68 3A 99 73 7E E7 CB 41"); 
		setRecords("0101", "0110");
		setData("9F17","9F36");
		setACTypePosition(5);
*/
	
/*
		// For use with card 10
 		// MasterCard
		setApplication("A0000000041010");
		setPDOL("83 00");
		setDDOL("36 25 2E 81");
		setCDOL("00 00 00 00 00 05 00 00 00 00 00 00 05 28 00 00 00 80 00 09 78 11 05 19 00 36 25 2E 81 21 00 00 68 3A 99 73 7E E7 CB 41 04 03 02", "3E 85 5D D7 69 BB 60 39 03 18 30 30 00 00 00 80 00 36 25 2E 81 68 3A 99 73 7E E7 CB 41");
		setRecords("0101", "0110"); //TODO CHECK
		setData("9F17","9F36");	//TODO CHECK
		setACTypePosition(5);	
*/

/*
		// For use with cards 1,2,3,9
 		// MasterCard
		setApplication("A0000000041010");
		setPDOL("83 00");
		setDDOL("36 25 2E 81");
		setCDOL("00 00 00 00 00 05 00 00 00 00 00 00 05 28 00 00 00 80 00 09 78 11 05 19 00 36 25 2E 81 21 00 00 04 03 02", "3E 85 5D D7 69 BB 60 39 03 18 30 30 00 00 00 80 00 ");		
		setRecords("0102", "0110");
		setData("9F17","9F36");
		setACTypePosition(5);
*/		

/*
		// For use with Volksbank
		// MAESTRO
		setApplication("A0000000043060");
		setPDOL("83 0B FF FF FF 02 76 21 FF FF FF FF FF");
		setDDOL("36 25 2E 81");
		// Datum transactie moet voor verloopdatum kaart liggen
		setCDOL("00 00 00 00 00 05 00 00 00 00 00 00 00 00 00 80 00 09 78 06 05 19 00 36 25 2E 81 68 3A 99 73 7E E7 CB 41 00 00", "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 30 30 00 00 00 80 00 68 3A 99 73 7E E7 CB 41");
		setRecords("0102", "0110");
		setData("9F17","9F13");
		setACTypePosition(5);
*/
		
		
		// VISA debit
		setApplication("A0000000031010");
		setPDOL("83 00");
		setDDOL("36 25 2E 81");
		setCDOL("00 00 00 00 00 05 00 00 00 00 00 00 08 26 00 00 00 00 00 08 26 11 05 19 00 36 25 2E 81", "00 00 00 00 00 00 00 05 00 00 00 00 00 00 08 26 00 00 00 00 00 08 26 11 05 19 00 36 25 2E 81");
		setRecords("0101", "0110");
		setData("9F36", "9F4F");
		setACTypePosition(2);

/*	
		setApplication("A0000000048002");
		setPDOL("83 01 34");
		setDDOL("36 25 2E 81");
		setCDOL("00000000000000000000000000008000000000000000000000000000000000010002","5A330000000000000000000000000000800000000000000000000000000000000001000200000000000000000000");
		setRecords("0101","0110");
		setData("9F17","9F00");
		setACTypePosition(5);
*/		
	}
	
	/** Source: JMRTD ,Martijn Oostdijk (martijno@cs.ru.nl) */
	private static String byteToHexString(byte b) {
		int n = b & 0x000000FF;
		String result = (n < 0x00000010 ? "0" : "") + Integer.toHexString(n);
		return result.toUpperCase();
	}	
	
	/** Source: JMRTD ,Martijn Oostdijk (martijno@cs.ru.nl) */
	private static String toHexString(byte[] text) {
		return bytesToHexString(text,0,text.length, 1000);
	}
	
	/** Source: JMRTD ,Martijn Oostdijk (martijno@cs.ru.nl) */
	private static String bytesToHexString(byte[] text, int offset, int length, int numRow) {
		if(text == null) return "NULL";
		StringBuffer result = new StringBuffer();
		for (int i = 0; i < length; i++) {
			if(i != 0 && i % numRow == 0) result.append("\n");
			result.append(byteToHexString(text[offset + i]));
		}
		return result.toString();
	}
	
	/** Enable/disable logging of the APDUs */
	public void setLogging(boolean enable) {
		this.logging = enable;
	}
	
	/** Log a command APDU if logging is enabled. For the VERIFY command, the PIN code is replaced by 1234. */
	private void logCommand(CommandAPDU command) {
		if(!this.logging) return;
		
		byte[] apdu = command.getBytes();
		if(command.getINS() == 0x20) {
			apdu[6] = 0x12;
			apdu[7] = 0x34;
		}
		
		System.out.println(">> " + toHexString(apdu));
	}
	
	/** Log a response APDU if logging is enabled. */
	private void logResponse(ResponseAPDU response) {
		if(!this.logging) return;
		
		byte[] apdu = response.getBytes();
		System.out.println("<< " + toHexString(apdu));
	}
	
	/** Log a custom message */
	private void logMessage(String msg) {
		if(!this.logging) return;

		System.out.println(msg);
	}
	
	public void setACTypePosition(int pos) {
		this.ac_type_position = pos;
	}
	
	/** Set the PIN code used in the VERIFY commands */
	public void setPIN(String pin) {
		this.pin = Hex.hexStringToBytes(pin);
		pin_enabled = true;
	}
	
	/** Set the application to be selected */
	public void setApplication(String application) {
		this.application = Hex.hexStringToBytes(application);
	}
	
	/** Set the PDOL used in the GET PROCESSING OPTIONS command */
	public void setPDOL(String pdol) {
		this.pdol = Hex.hexStringToBytes(pdol);
	}
	
	/** Set the DDOL used in DDA */
	public void setDDOL(String ddol) {
		this.ddol = Hex.hexStringToBytes(ddol);
	}
	
	/** Set CDOL1 and CDOL2 used in the GENERATE AC commands */
	public void setCDOL(String cdol1, String cdol2) {
		this.cdol1 = Hex.hexStringToBytes(cdol1);
		this.cdol2 = Hex.hexStringToBytes(cdol2);
	}
	
	public void setRecords(String valid, String invalid) {
		this.record_valid = Hex.hexStringToBytes(valid);
		this.record_invalid = Hex.hexStringToBytes(invalid);
	}
	
	public void setData(String valid, String invalid) {
		this.data_valid = Hex.hexStringToBytes(valid);
		this.data_invalid = Hex.hexStringToBytes(invalid);
	}
	
	private ResponseAPDU sendAPDU(CommandAPDU apdu) throws CardException {
		logCommand(apdu);
		ResponseAPDU response = channel.transmit(apdu);
		logResponse(response);
		while(response.getSW1() == 0x61 || response.getSW1() == 0x6C) {
			if(response.getSW1() == 0x61) {
				CommandAPDU command = new CommandAPDU(0x00, 0xC0, 0x00, 0x00, response.getSW2());
				logCommand(command);
				response = channel.transmit(command);
				logResponse(response);
			}
			else if(response.getSW1() == 0x6C) {
				CommandAPDU command = new CommandAPDU(apdu.getCLA(), apdu.getINS(), apdu.getP1(), apdu.getP2(), response.getSW2());
				logCommand(command);
				response = channel.transmit(command);
				logResponse(response);
			}
		}
		return response;
	}
	
	/** Reset the card */
	public void reset() throws CardException {
		// TODO Check
		card.disconnect(false);
		
		card = TerminalFactory.getDefault().terminals().list().get(2).connect("*");
		channel = card.getBasicChannel();
		logMessage("***CARD RESET***");
		
		selectApplication();
	}
	
	/** Read the ATC */
	public int getATC() throws CardException {
		CommandAPDU apdu = new CommandAPDU(0x80, 0xCA, (byte)0x9F, (byte)0x36, 0x100);
		ResponseAPDU response = sendAPDU(apdu);
		logMessage(Hex.intToHexString((response.getBytes()[3] & 0xFF) * 256 + (response.getBytes()[4] & 0xFF)));
		return (response.getBytes()[3] & 0xFF) * 256 + (response.getBytes()[4] & 0xFF);
	}
	
	/** Select the application */
	public String selectApplication() throws CardException {
		CommandAPDU apdu = new CommandAPDU(0x00, 0xA4, 0x04, 0x00, application, 0x100);
		ResponseAPDU response = sendAPDU(apdu);
		return Hex.intToHexString(response.getSW());
	}
	
	/** Verify a plaintext PIN code 
	 * @throws Exception */
	public String verifyPINPlaintextCorrect() throws Exception {
		if(!pin_enabled) throw new Exception("No PIN configured");
			
		byte[] pin = new byte[]{0x24, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF};
		System.arraycopy(this.pin, 0, pin, 1, this.pin.length);
		CommandAPDU apdu = new CommandAPDU(0x00, 0x20, 0x00, 0x80, pin, 0x100);
		ResponseAPDU response = sendAPDU(apdu);
		return Hex.intToHexString(response.getSW());
	}

    /** Perform DDA */
	public String internalAuthenticate() throws CardException {
		CommandAPDU apdu = new CommandAPDU(0x00, 0x88, 0x00, 0x00, ddol, 0x100);
		ResponseAPDU response = sendAPDU(apdu);
		return Hex.intToHexString(response.getSW());
	}
	
	/** Try to read the given record */
	private String readRecord(byte record, byte sfi) throws CardException {
		CommandAPDU apdu = new CommandAPDU(0x00, 0xB2, record, (sfi << 3) | 0x04, 0x100);
		ResponseAPDU response = sendAPDU(apdu);
		return Hex.intToHexString(response.getSW());
	}
	
	/** Read an existing record (one that exists on Dutch banking cards) */
	public String readValidRecord() throws CardException {
		return readRecord(record_valid[0],record_valid[1]);
	}
	
	/** Read a non-existing record (one that does not exists on Dutch banking cards) */
	public String readInvalidRecord() throws CardException {
		return readRecord(record_invalid[0],record_invalid[1]);
	}
	
	/** Read the given data element */
	private String getData(byte[] data) throws CardException {
		CommandAPDU apdu = new CommandAPDU(0x80, 0xCA, data[0], data[1], 0x100);
		ResponseAPDU response = sendAPDU(apdu);
		return Hex.intToHexString(response.getSW());
	}
	
	/** Read an existing data element (one that exists on Dutch banking cards) */
	public String getValidData() throws CardException {
		return getData(data_valid);
	}
	
	/** Read a non-existing data element (one that does not exists on Dutch banking cards) */
	public String getInvalidData() throws CardException {
		return getData(data_invalid);
	}
	
	/** Get the supported options from the card using the PDOL */
	public String getProcessingOptions() throws CardException {
		CommandAPDU apdu = new CommandAPDU(0x80, 0xA8, 0x00, 0x00, pdol, 0x100);
		ResponseAPDU response = sendAPDU(apdu);
		return Hex.intToHexString(response.getSW());
	}
	
	/** Send an invalid command to get the supported options from the card */
	public String getProcessingOptionsInvalid() throws CardException {
		CommandAPDU apdu = new CommandAPDU(0x80, 0xA8, 0x00, 0x00, new byte[]{(byte)0xFF,(byte)0xFF}, 0x100);
		ResponseAPDU response = sendAPDU(apdu);
		return Hex.intToHexString(response.getSW());
	}	
	
	// TODO Include option to use CDA in the GENERATE AC commands?
	
	/** Request a Transaction Certificate (TC) using the CDOL1, response is the status word optionally followed by the message type */
	public String generateACFirstTC() throws CardException {
		CommandAPDU apdu = new CommandAPDU(0x80, 0xAE, TC, 0x00, cdol1, 0x100);
		ResponseAPDU response = sendAPDU(apdu);
		if(response.getSW() == 0x9000) {
			// TODO Works only if ATC is at the 10th and 11th byte
			String changed = "00";
			if(lastATC[0] != response.getBytes()[9] || lastATC[1] != response.getBytes()[10]) {
				lastATC[0] = response.getBytes()[9];
				lastATC[1] = response.getBytes()[10];
				changed = "01";
			}
			// TODO Works only if CID is at the 6th byte						
			return Hex.intToHexString(response.getSW()) + Hex.byteToHexString(response.getBytes()[this.ac_type_position]) + changed;
		}
		else {
			return Hex.intToHexString(response.getSW());
		}
	}
	
	/** Request an Authorization Request (ARQC) using the CDOL1, response is the status word optionally followed by the message type */
	public String generateACFirstARQC() throws CardException {
		CommandAPDU apdu = new CommandAPDU(0x80, 0xAE, ARQC, 0x00, cdol1, 0x100);
		ResponseAPDU response = sendAPDU(apdu);
		if(response.getSW() == 0x9000) {
			// TODO Works only if ATC is at the 10th and 11th byte
			String changed = "00";
			if(lastATC[0] != response.getBytes()[9] || lastATC[1] != response.getBytes()[10]) {
				lastATC[0] = response.getBytes()[9];
				lastATC[1] = response.getBytes()[10];
				changed = "01";
			}
			// TODO Works only if CID is at the 6th byte						
			return Hex.intToHexString(response.getSW()) + Hex.byteToHexString(response.getBytes()[this.ac_type_position]) + changed;
		}
		else {
			return Hex.intToHexString(response.getSW());
		}
	}
	
	/** Request an Application Authentication (AAC), to cancel the transaction, using the CDOL1, response is the status word optionally followed by the message type */
	public String generateACFirstAAC() throws CardException {
		CommandAPDU apdu = new CommandAPDU(0x80, 0xAE, AAC, 0x00, cdol1, 0x100);
		ResponseAPDU response = sendAPDU(apdu);
		if(response.getSW() == 0x9000) {
			// TODO Works only if ATC is at the 10th and 11th byte
			String changed = "00";
			if(lastATC[0] != response.getBytes()[9] || lastATC[1] != response.getBytes()[10]) {
				lastATC[0] = response.getBytes()[9];
				lastATC[1] = response.getBytes()[10];
				changed = "01";
			}
			// TODO Works only if CID is at the 6th byte						
			return Hex.intToHexString(response.getSW()) + Hex.byteToHexString(response.getBytes()[this.ac_type_position]) + changed;
		}
		else {
			return Hex.intToHexString(response.getSW());
		}
	}
	
	/** Request a Transaction Certificate (TC) using the CDOL2, response is the status word optionally followed by the message type */
	public String generateACSecondTC() throws CardException {
		CommandAPDU apdu = new CommandAPDU(0x80, 0xAE, TC, 0x00, cdol2, 0x100);
		ResponseAPDU response = sendAPDU(apdu);
		if(response.getSW() == 0x9000) {
			// TODO Works only if ATC is at the 10th and 11th byte
			String changed = "00";
			if(lastATC[0] != response.getBytes()[9] || lastATC[1] != response.getBytes()[10]) {
				lastATC[0] = response.getBytes()[9];
				lastATC[1] = response.getBytes()[10];
				changed = "01";
			}
			// TODO Works only if CID is at the 6th byte						
			return Hex.intToHexString(response.getSW()) + Hex.byteToHexString(response.getBytes()[this.ac_type_position]) + changed;
		}
		else {
			return Hex.intToHexString(response.getSW());
		}		
	}
	
	/** Request an Authorization Request (ARQC) using the CDOL2, response is the status word optionally followed by the message type */
	public String generateACSecondARQC() throws CardException {
		CommandAPDU apdu = new CommandAPDU(0x80, 0xAE, ARQC, 0x00, cdol2, 0x100);
		ResponseAPDU response = sendAPDU(apdu);
		if(response.getSW() == 0x9000) {
			// TODO Works only if ATC is at the 10th and 11th byte
			String changed = "00";
			if(lastATC[0] != response.getBytes()[9] || lastATC[1] != response.getBytes()[10]) {
				lastATC[0] = response.getBytes()[9];
				lastATC[1] = response.getBytes()[10];
				changed = "01";
			}
			return Hex.intToHexString(response.getSW()) + Hex.byteToHexString(response.getBytes()[this.ac_type_position]) + changed;
		}
		else {
			return Hex.intToHexString(response.getSW());
		}
	}
	
	/** Request an Application Authentication (AAC), to cancel the transaction, using the CDOL2, response is the status word optionally followed by the message type */
	public String generateACSecondAAC() throws CardException {
		CommandAPDU apdu = new CommandAPDU(0x80, 0xAE, AAC, 0x00, cdol2, 0x100);
		ResponseAPDU response = sendAPDU(apdu);
		if(response.getSW() == 0x9000) {
			// TODO Works only if ATC is at the 10th and 11th byte
			String changed = "0";
			if(lastATC[0] != response.getBytes()[9] || lastATC[1] != response.getBytes()[10]) {
				lastATC[0] = response.getBytes()[9];
				lastATC[1] = response.getBytes()[10];
				changed = "1";
			}
			// TODO Works only if CID is at the 6th byte						
			return Hex.intToHexString(response.getSW()) + Hex.byteToHexString(response.getBytes()[this.ac_type_position]) + changed;
		}
		else {
			return Hex.intToHexString(response.getSW());
		}	
	}

	public String processSymbol(String input) throws Exception {
		String inAction = input;
		System.out.println(inAction);
		try{
			if (inAction.equals("selectApplication")) {
				String outAction;
				outAction = selectApplication();
				System.out.println(outAction.toString());
				return outAction.toString();
			}
			else if (inAction.equals("verifyPINPlaintextCorrect")) {
				String outAction;
				outAction = verifyPINPlaintextCorrect();
				System.out.println(outAction.toString());
				return outAction.toString();
			}			
			else if (inAction.equals("internalAuthenticate")) {
				String outAction;
				outAction = internalAuthenticate();
				System.out.println(outAction.toString());
				return outAction.toString();
			}
			else if (inAction.equals("readValidRecord")) {
				String outAction;
				outAction = readValidRecord();
				System.out.println(outAction.toString());
				return outAction.toString();
			}
			else if (inAction.equals("readInvalidRecord")) {
				String outAction;
				outAction = readInvalidRecord();
				System.out.println(outAction.toString());
				return outAction.toString();
			}
			else if (inAction.equals("getValidData")) {
				String outAction;
				outAction = getValidData();
				System.out.println(outAction.toString());
				return outAction.toString();
			}
			else if (inAction.equals("getInvalidData")) {
				String outAction;
				outAction = getInvalidData();
				System.out.println(outAction.toString());
				return outAction.toString();
			}
			else if (inAction.equals("getProcessingOptions")) {
				String outAction;
				outAction = getProcessingOptions();
				System.out.println(outAction.toString());
				return outAction.toString();
			}
			else if (inAction.equals("getProcessingOptionsInvalid")) {
				String outAction;
				outAction = getProcessingOptionsInvalid();
				System.out.println(outAction.toString());
				return outAction.toString();
			}
			else if (inAction.equals("generateACFirstTC")) {
				String outAction;
				outAction = generateACFirstTC();
				System.out.println(outAction.toString());
				return outAction.toString();
			}
			else if (inAction.equals("generateACFirstARQC")) {
				String outAction;
				outAction = generateACFirstARQC();
				System.out.println(outAction.toString());
				return outAction.toString();
			}
			else if (inAction.equals("generateACFirstAAC")) {
				String outAction;
				outAction = generateACFirstAAC();
				System.out.println(outAction.toString());
				return outAction.toString();
			}
			else if (inAction.equals("generateACSecondTC")) {
				String outAction;
				outAction = generateACSecondTC();
				System.out.println(outAction.toString());
				return outAction.toString();
			}
			else if (inAction.equals("generateACSecondARQC")) {
				String outAction;
				outAction = generateACSecondARQC();
				System.out.println(outAction.toString());
				return outAction.toString();
			}
			else if (inAction.equals("generateACSecondAAC")) {
				String outAction;
				outAction = generateACSecondAAC();
				System.out.println(outAction.toString());
				return outAction.toString();
			}
			else if (inAction.equals("getATC")) {
				String outAction;
				outAction = Integer.toString(getATC());
				System.out.println(outAction.toString());
				return outAction.toString();
			}
			else {
				System.out.println("Unknown input symbol ...");
				System.exit(0);
			}
		}
		catch (CardException cex) {
			System.out.println("CardException in main ...");
			cex.printStackTrace();	
		}
		return null;
	}	

	public static void main(String[] args) throws Exception {
		EMVTestService service = EMVTestService.createEMVTestService();
		//service.setApplication("A0000000041010"); // MEASTRO

		service.reset();
		//System.out.println("selectApplication(): " + (service.selectApplication()));
		//System.out.println("getValidData(): " + (service.getValidData()));
		System.out.println("getATC(): " + (service.getATC()));
		//System.out.println("getValidData(): " + (service.getValidData()));
		//System.out.println("getProcessingOptionsInvalid(): " + service.getProcessingOptionsInvalid());
		//System.out.println("getProcessingOptions(): " + (service.getProcessingOptions()));
		//System.out.println("verifyPINPlaintextCorrect(): " + (service.verifyPINPlaintextCorrect()));
		//System.out.println("readValidRecord(): " + (service.readValidRecord()));
		//System.out.println("readInvalidRecord(): " + service.readInvalidRecord());
		//System.out.println("getValidData(): " + (service.getValidData()));
		//System.out.println("getInvalidData(): " + service.getInvalidData());
		//System.out.println("internalAuthenticate(): " + (service.internalAuthenticate()));
		//System.out.println("generateACFirstTC(): " + (service.generateACFirstTC()));
		//System.out.println("generateACFirstARQC(): " + (service.generateACFirstARQC()));
		//System.out.println("getValidData(): " + (service.getValidData()));
		//System.out.println("generateACFirstAAC(): " + (service.generateACFirstAAC()));
		//System.out.println("generateACSecondAAC(): " + (service.generateACSecondAAC()));
		//System.out.println("getValidData(): " + (service.getValidData()));
		//System.out.println("generateACSecondTC(): " + (service.generateACSecondTC()));
		//System.out.println("internalAuthenticate(): " + (service.internalAuthenticate()));
		//System.out.println("getValidData(): " + (service.getValidData()));
		//System.out.println("getValidData(): " + (service.getValidData()));
		//System.out.println("selectApplication(): " + (service.selectApplication()));
		//System.out.println("getProcessingOptions(): " + (service.getProcessingOptions()));
		//System.out.println("generateACFirstAAC(): " + (service.generateACFirstAAC()));
		
		//System.out.println("selectApplication(): " + (service.selectApplication()));
		//System.out.println("getProcessingOptions(): " + (service.getProcessingOptions()));
		//System.out.println("generateACFirstTC(): " + (service.generateACFirstTC()));		
	}

	
}
