package applet;

import javacard.framework.Applet;
import javacard.framework.ISO7816;
import javacard.framework.ISOException;
import javacard.framework.APDU;
import javacard.framework.JCSystem;
import javacard.framework.Util;
import javacard.framework.OwnerPIN;
import javacard.security.CryptoException;
import javacard.security.RandomData;
import org.globalplatform.GPSystem;


public class MainApplet extends Applet {
	// SIM Command from ts 102221
	public static final byte INS_SELECT_FILE = (byte) 0xA4;
	public static final byte INS_READ_BINARY = (byte) 0xB0;
	public static final byte INS_UPDATE_BINARY = (byte) 0xD6;
	public static final byte INS_READ_RECORD = (byte) 0xB2;
	public static final byte INS_UPDATE_RECORD = (byte) 0xDC;
	public static final byte INS_CREATE_FILE = (byte) 0xE0;
	public static final byte INS_DELETE_FILE = (byte) 0xE4;
	public static final byte INS_VERIFY_PIN = (byte) 0x20;
	public static final byte INS_CHANGE_PIN = (byte) 0x24;
	public static final byte INS_DISABLE_PIN = (byte) 0x26;
	public static final byte INS_ENABLE_PIN = (byte) 0x28;
	public static final byte INS_UNBLOCK_PIN = (byte) 0x2C;
	public static final byte INS_GET_RESPONSE = (byte) 0xC0;
	public static final byte INS_GET_CHALLENGE = (byte) 0x84;
	public static final byte AUTHENTICATE = (byte) 0x88;

	/* CLEAR TRACE： Clear the trace history
	 * CLA: 0x00; INS: 0x97; P1: 0x00, P2: 0x00
	 * e.g. 0097000000
	 * */
	public static final byte INS_CLEAR_TRACE = (byte) 0x97;
	/* START TRACE： toggle the tracer function
	 * CLA: 0x00; INS: 0x98; P1: 0x00, P2: 0x00
	 * e.g.  0098000000
	 * */
	public static final byte INS_START_TRACE = (byte) 0x98;
	/* READ TRACE： get the tracer history
	 * CLA: 0x00; INS: 0x98; P1: 0x00, P2: 0x00
	 * e.g.  0099000000
	 * return the latest 10 APDUs in the trace history
	 * */
	public static final byte INS_READ_TRACE = (byte) 0x99;
	/* INSTALL PROFILE
	* CLA: 0x00; INS: 0xA9; P1: 0x01-0x03 (Profile ID), P2: 0x00
	* e.g. switch to profile 1: 00a9010000
	* */
	public static final byte INS_SWITCH_PROFILE = (byte) 0xa9;
	/* PROFILE STATUS
	 * CLA: 0x00; INS: 0xA8; P1: 0x00, P2: 0x00
	 * e.g. 00a8000000
	 * Status Format (2 bytes for each slot): name&status (enable:1, disable:2, deleted/not-init:3)
	 * e.g. '010102020303' represent Profile1-enabled, Profile2-disabled, Profile3-deleted
	 * */
	public static final byte INS_PROFILE_STATUS = (byte) 0xa8;
	/* PROFILE STATUS
	 * CLA: 0x00; INS: 0xa7; P1: 0x00, P2: 0x00
	 * e.g. 00A70000290829430191348767650644092A26DCEBA00503CF739EC097EEC8D74B739DAB27C6CEAB9BA5C98B7D67
	 * IMSI: 082943019134876765
	 * OPC: 0644092A26DCEBA00503CF739EC097EE
	 * Key: C8D74B739DAB27C6CEAB9BA5C98B7D67
	 * */
	public static final byte INS_INSTALL_PROFILE = (byte) 0xa7;
	/* DELETE PROFILE
	 * CLA: 0x00; INS: 0xA6; P1: 0x01-0x03 (Profile ID), P2: 0x00
	 * e.g. delete profile 2: 00a6020000
	 * */
	public static final byte INS_DELETE_PROFILE = (byte) 0xa6;
	/* RESET HISBYTE: reset the historical bytes for the card to make it recognized as a SIM
	 * CLA: 0x00; INS: 0xA5; P1: 0x00, P2: 0x00
	 * e.g. 00a5000000
	 * */
	public static final byte INS_RESET_HISBYTE = (byte) 0xa5;
	public static final short SW_PIN_TRIES_REMAINING = 0x63C0;
	public static final short SW_COMMAND_NOT_ALLOWED = 0x6900;

	private static final byte PIN_MAX_TRIES = 3;
	private static final byte PIN_MIN_LENGTH = 4;
	private static final byte PIN_MAX_LENGTH = 8;
	private static final byte PUK_MAX_TRIES = 5;
	private static final byte PUK_LENGTH = 16;

	private static final byte STATE_CREATION = (byte) 0x00;
	private static final byte STATE_INITIALISATION = (byte) 0x01;
	private static final byte STATE_ACTIVATED = (byte) 0x02;
	private static final byte STATE_ENABLED = (byte) 0x03;
	private static final byte STATE_DISABLED = (byte) 0x04;

	private static final short RAM_BUF_LEN = (short) 512;
	private static final short RAM_CHAINING_LEN = (short) 4;
	private static final short BYTES_REMAINING = (short) 0;
	private static final short CURRENT_POS = (short) 1;
	private static final short CURRENT_INS = (short) 2;
	private static final short CURRENT_P1P2 = (short) 3;

	/* Member variables: */
	private byte state;

	private OwnerPIN pin = null;
	private OwnerPIN puk = null;
	private byte[] ram_buf = null;
	private short[] ram_chaining = null;
	private RandomData randomData = null;
//	private byte[] trace_his =  new byte[256];
	private byte[] proactive_cmd =  new byte[64];
	private short prcmd_length;
	private short ins_length = 0;
	private boolean startTrace = false;
	//Status Format (2 bytes for each slot): name&status (enable:1, disable:2, deleted/not-init:3)
	private byte[] profile_status = new byte[6];
	private static byte[] HistByteArray = { (byte) 0x80, (byte) 0x31,
			(byte) 0xA0, (byte) 0x73, (byte) 0xBE, (byte) 0x21, (byte) 0x13,
			(byte) 0x67, (byte) 0x43, (byte) 0x20,  (byte) 0x07};

	private SIMFiles sf;
	private short currentFileSystem = 1;

	public static void install(byte[] bArray, short bOffset, byte bLength) {
		new MainApplet();
	}

	protected MainApplet() {
		pin = new OwnerPIN(PIN_MAX_TRIES, PIN_MAX_LENGTH);
		puk = new OwnerPIN(PUK_MAX_TRIES, PUK_LENGTH);

		sf = new SIMFiles();
		state = STATE_CREATION;
		ram_buf = JCSystem.makeTransientByteArray(RAM_BUF_LEN, JCSystem.CLEAR_ON_DESELECT);
		ram_chaining = JCSystem.makeTransientShortArray(RAM_CHAINING_LEN, JCSystem.CLEAR_ON_DESELECT);
		try {
			randomData = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM);
		} catch (CryptoException e) {
			if(e.getReason() == CryptoException.NO_SUCH_ALGORITHM) {
				randomData = null;
			} else {
				throw e;
			}
		}
		register();
	}

	public void deselect() {
		pin.reset();
		puk.reset();
		sf.setUserAuthed(false);
	}

	public boolean select() {
		sf.setUserAuthed(state == STATE_CREATION || state == STATE_INITIALISATION);
		sf.selectFile(null);
		return true;
	}

	public void installFileSystem(APDU apdu){
		apdu.setIncomingAndReceive();
		byte[] buffer = apdu.getBuffer();
		byte[] imsi = new byte[9];
		byte[] opc = new byte[16];
		byte[] key = new byte[16];

		Util.arrayCopyNonAtomic(buffer, (short) 5, imsi, (short) 0, (short) 9);
		Util.arrayCopyNonAtomic(buffer, (short) 14, opc, (short) 0, (short) 16);
		Util.arrayCopyNonAtomic(buffer, (short) 30, key, (short) 0, (short) 16);

		sf.sfInstallProfile(imsi,opc,key);
		profile_status[1] = (byte) 0x01;
		if(currentFileSystem != 0){
			profile_status[(short)(2*currentFileSystem-1)] = (byte) 0x02;
		}
		currentFileSystem = 1;
	}

	/**
	 * \brief Processes an incoming APDU.
	 *
	 * \see APDU.
	 *
	 * \param apdu The incoming APDU.
	 */
	public void process(APDU apdu) {
		byte[] buffer = apdu.getBuffer();
		byte cla = buffer[ISO7816.OFFSET_CLA];
		byte ins = buffer[ISO7816.OFFSET_INS];
		short le;
		short maxle;

		/*
		byte[] cmdSplit = { (byte) 0x01, (byte) 0x23, (byte) 0x45, (byte) 0x67, (byte) 0x89};
		short traceLen = 20;
		if(startTrace){
			Util.arrayCopy(buffer, (short) 0, trace_his, ins_length, traceLen);
			ins_length += traceLen;
			if (ins_length > 230){
				ins_length = 0;
			}
			Util.arrayCopy(cmdSplit, (short) 0, trace_his, ins_length, (short) 5);
			trace_his[250] = (byte) ((short)(trace_his[250]+1)%256);
			// if (cla==(byte)0x80 && ins == (byte)0x14) startTrace=false;
		} */

		if(selectingApplet()) {
			return;
		}

		if(apdu.isSecureMessagingCLA()) {
			ISOException.throwIt(ISO7816.SW_SECURE_MESSAGING_NOT_SUPPORTED);
		}

		if(ram_chaining[CURRENT_INS] != 0 || checkChainCLA(apdu)) {
			short p1p2 = Util.getShort(buffer, ISO7816.OFFSET_P1);
			if(ram_chaining[CURRENT_INS] == 0 && ram_chaining[CURRENT_P1P2] == 0) {
				ram_chaining[CURRENT_INS] = ins;
				ram_chaining[CURRENT_P1P2] = p1p2;
			} else if(ram_chaining[CURRENT_INS] != ins || ram_chaining[CURRENT_P1P2] != p1p2) {
				ISOException.throwIt(SW_COMMAND_NOT_ALLOWED);
			} else if(!checkChainCLA(apdu)) {
				ram_chaining[CURRENT_INS] = 0;
				ram_chaining[CURRENT_P1P2] = 0;
			}
		}

		if(ram_chaining[BYTES_REMAINING] > 0 && ins != INS_GET_RESPONSE) {
			ISOException.throwIt(SW_COMMAND_NOT_ALLOWED);
		}

		if( apdu.isISOInterindustryCLA() && cla!= (byte) 0xA0) {
			switch (ins) {
				case INS_SELECT_FILE:
					sf.sfSelectFile(apdu);
					break;
				case INS_READ_BINARY:
					sf.sfReadBinary(apdu);
					break;
				case INS_READ_RECORD:
					sf.sfReadRecord(apdu);
					break;
				case INS_VERIFY_PIN:
					processVerify(apdu);
					break;
				case INS_CREATE_FILE:
					sf.sfCreateFile(apdu);
					break;
				case INS_UPDATE_BINARY:
					sf.sfUpdateBinary(apdu);
					break;
				case INS_UPDATE_RECORD:
					sf.sfUpdateRecord(apdu);
					break;
				case INS_CHANGE_PIN:
					processChangePin(apdu);
					break;
				case INS_DISABLE_PIN:
					processDisablePin(apdu);
					break;
				case INS_ENABLE_PIN:
					processEnablePin(apdu);
					break;
				case INS_DELETE_FILE:
					sf.sfDeleteFile(apdu);
					break;
				case INS_UNBLOCK_PIN:
					processUnblockPin(apdu);
					break;
				case INS_GET_RESPONSE:
					processGetResponse(apdu);
					break;
				case INS_GET_CHALLENGE:
					processGetChallenge(apdu);
					break;
				case AUTHENTICATE:
					sf.milenage.process(apdu);
					break;
				case INS_INSTALL_PROFILE:
					installFileSystem(apdu);
					break;
				case INS_PROFILE_STATUS:
					le = apdu.setOutgoing();
					maxle = (short)(profile_status.length);
					if(le > maxle) {
						le = maxle;
					}
					apdu.setOutgoingLength(le);
					apdu.sendBytesLong(profile_status, (short) 0, le);
					break;
				case INS_RESET_HISBYTE:
					GPSystem.setATRHistBytes(HistByteArray, (short) 0, (byte) HistByteArray.length);
					break;
				default:
					ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
			}
		} else if (cla== (byte) 0x80){
			switch (ins) {
				case (byte)0x10: // TERMINAL PROFILE: return stk_menu
					prcmd_length = (short)40;
					Util.arrayCopyNonAtomic(new byte[]{(byte)0xD0, (byte)0x26, (byte)0x81, (byte)0x03, (byte)0x01, (byte)0x25, (byte)0x00, (byte)0x82, (byte)0x02, (byte)0x81, (byte)0x82, (byte)0x85, (byte)0x09, (byte)0x4D, (byte)0x65, (byte)0x6E, (byte)0x75, (byte)0x20, (byte)0x6E, (byte)0x61, (byte)0x6D, (byte)0x65, (byte)0x8F, (byte)0x07, (byte)0x80, (byte)0x49, (byte)0x74, (byte)0x65, (byte)0x6D, (byte)0x20, (byte)0x31, (byte)0x8F, (byte)0x07, (byte)0x81, (byte)0x49, (byte)0x74, (byte)0x65, (byte)0x6D, (byte)0x20, (byte)0x32}, (short) 0, proactive_cmd, (short) 0, prcmd_length);
					// ISOException.throwIt((short)37160); // short menu
					ISOException.throwIt((short)((short)37120+prcmd_length));
					break;
				case (byte)0x12: // FETCH
					le = apdu.setOutgoing();
					maxle = prcmd_length;
					if(le > maxle) {
						le = maxle;
					}
					apdu.setOutgoingLength(le);
					apdu.sendBytesLong(proactive_cmd, (short) 0, le);
					break;
				case (byte)0x14: // TERMINAL RESPONSE
					break;
				case (byte)0x11: // REFRESH trigger
                    prcmd_length = (short) 11;
					Util.arrayCopyNonAtomic(new byte[]{(byte)0xD0, (byte)0x09, (byte)0x81, (byte)0x03, (byte)0x01, (byte)0x01, buffer[ISO7816.OFFSET_P1], (byte)0x82, (byte)0x02, (byte)0x81, (byte)0x82}, (short) 0, proactive_cmd, (short) 0, prcmd_length);
					ISOException.throwIt((short)((short)37120+prcmd_length));
					break;
				default:
					ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
			}
		} else{
			ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
		}
	}

	static boolean checkChainCLA(APDU apdu) {
		return ((byte)(apdu.getBuffer()[0] & (byte)0x10) == (byte)0x10);
	}

	private void processVerify(APDU apdu) throws ISOException {
		byte[] buffer = apdu.getBuffer();
		short offset_cdata;
		short lc;

		if(buffer[ISO7816.OFFSET_P1] != 0x00 || buffer[ISO7816.OFFSET_P2] != 0x01) {
			ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
		}

		lc = apdu.setIncomingAndReceive();
		if(lc != apdu.getIncomingLength()) {
			ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
		}
		offset_cdata = apdu.getOffsetCdata();

		if((lc > 0 && (lc < PIN_MIN_LENGTH) || lc > PIN_MAX_LENGTH)) {
			ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
		}else if (lc == 0){
			if(state != STATE_CREATION && state != STATE_INITIALISATION) {
				ISOException.throwIt((short)(SW_PIN_TRIES_REMAINING | pin.getTriesRemaining()));
			} else {
				ISOException.throwIt(ISO7816.SW_NO_ERROR);
			}
		}

		Util.arrayFillNonAtomic(buffer, (short)(offset_cdata + lc), (short)(PIN_MAX_LENGTH - lc), (byte) 0x00);
		if(!pin.check(buffer, offset_cdata, PIN_MAX_LENGTH)) {
			sf.setUserAuthed(false);
			ISOException.throwIt((short)(SW_PIN_TRIES_REMAINING | pin.getTriesRemaining()));
		} else {
			sf.setUserAuthed(true);
		}
	}

	private void processEnablePin(APDU apdu) throws ISOException {
		byte[] buffer = apdu.getBuffer();
		short offset_cdata;
		short lc;

		if(buffer[ISO7816.OFFSET_P1] != 0x00 || buffer[ISO7816.OFFSET_P2] != 0x01) {
			ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
		}

		lc = apdu.setIncomingAndReceive();
		if(lc != apdu.getIncomingLength()) {
			ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
		}
		offset_cdata = apdu.getOffsetCdata();

		Util.arrayFillNonAtomic(buffer, (short)(offset_cdata + lc), (short)(PIN_MAX_LENGTH - lc), (byte) 0x00);
		if(!pin.check(buffer, offset_cdata, PIN_MAX_LENGTH)) {
			sf.setUserAuthed(false);
			ISOException.throwIt((short)(SW_PIN_TRIES_REMAINING | pin.getTriesRemaining()));
		} else {
			if(state == STATE_DISABLED || state == STATE_ACTIVATED){
				state = STATE_ENABLED;
				sf.setUserAuthed(true);
			}else{
				ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED);
			}
		}
	}

	private void processDisablePin(APDU apdu) throws ISOException {
		byte[] buffer = apdu.getBuffer();
		short offset_cdata;
		short lc;

		if(buffer[ISO7816.OFFSET_P1] != 0x00 || buffer[ISO7816.OFFSET_P2] != 0x01) {
			ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
		}

		lc = apdu.setIncomingAndReceive();
		if(lc != apdu.getIncomingLength()) {
			ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
		}
		offset_cdata = apdu.getOffsetCdata();

		Util.arrayFillNonAtomic(buffer, (short)(offset_cdata + lc), (short)(PIN_MAX_LENGTH - lc), (byte) 0x00);
		if(!pin.check(buffer, offset_cdata, PIN_MAX_LENGTH)) {
			sf.setUserAuthed(false);
			ISOException.throwIt((short)(SW_PIN_TRIES_REMAINING | pin.getTriesRemaining()));
		} else {
			if(state == STATE_ENABLED){
				state = STATE_DISABLED;
			}else{
				ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED);
			}
		}
	}

	private void processChangePin(APDU apdu) throws ISOException {
		byte[] buffer = apdu.getBuffer();
		byte p1 = buffer[ISO7816.OFFSET_P1];
		byte p2 = buffer[ISO7816.OFFSET_P2];
		short lc;
		short offset_cdata;

		lc = apdu.setIncomingAndReceive();
		if(lc != apdu.getIncomingLength()) {
			ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
		}
		offset_cdata = apdu.getOffsetCdata();

		if(state == STATE_CREATION) {
			if(p1 != 0x01 || (p2 != 0x02 && p2 != 0x01) ) {
				ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
			}
			if(p2 == 0x01) {
				// Set the PUK before PIN
				ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED);
			} else {
				if(lc != PUK_LENGTH) {
					ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
				}
				puk.update(buffer, offset_cdata, (byte)lc);
				puk.resetAndUnblock();
				state = STATE_INITIALISATION;
			}
		} else if(state == STATE_INITIALISATION) {
			if(buffer[ISO7816.OFFSET_P1] != 0x00 || buffer[ISO7816.OFFSET_P2] != 0x01) {
				ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
			}
			if(lc > PIN_MAX_LENGTH || lc < PIN_MIN_LENGTH) {
				ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
			}
			Util.arrayFillNonAtomic(buffer, (short)(offset_cdata + lc), (short)(PIN_MAX_LENGTH - lc), (byte) 0x00);
			pin.update(buffer, offset_cdata, PIN_MAX_LENGTH);
			pin.resetAndUnblock();
			state = STATE_ACTIVATED;
		} else {
			if(buffer[ISO7816.OFFSET_P1] != 0x00 || buffer[ISO7816.OFFSET_P2] != 0x01) {
				ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
			}
			if(lc != (short)(2*PIN_MAX_LENGTH)) {
				ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
			}
			if(!pin.check(buffer, offset_cdata, PIN_MAX_LENGTH)) {
				ISOException.throwIt((short)(SW_PIN_TRIES_REMAINING | pin.getTriesRemaining()));
			}
			pin.update(buffer, (short) (offset_cdata+PIN_MAX_LENGTH), PIN_MAX_LENGTH);

		}
	}

	public void	processUnblockPin(APDU apdu) throws ISOException {
		byte[] buffer = apdu.getBuffer();
		byte p1 = buffer[ISO7816.OFFSET_P1];
		byte p2 = buffer[ISO7816.OFFSET_P2];
		short lc;
		short offset_cdata;

		if(state != STATE_ACTIVATED) {
			ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED);
		}
		lc = apdu.setIncomingAndReceive();
		if(lc != apdu.getIncomingLength()) {
			ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
		}
		offset_cdata = apdu.getOffsetCdata();
		if(lc < (short)(PUK_LENGTH + PIN_MIN_LENGTH) || lc > (short)(PUK_LENGTH + PIN_MAX_LENGTH)) {
			ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
		}

		if(p1 != (byte) 0x00 || p2 != (byte) 0x01) {
			ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
		}

		if(!puk.check(buffer, offset_cdata, PUK_LENGTH)) {
			ISOException.throwIt((short)(SW_PIN_TRIES_REMAINING | puk.getTriesRemaining()));
		}
		Util.arrayFillNonAtomic(buffer, (short)(offset_cdata + lc), (short)(PUK_LENGTH + PIN_MAX_LENGTH - lc), (byte) 0x00);
		pin.update(buffer, (short)(offset_cdata+PUK_LENGTH), PIN_MAX_LENGTH);
		pin.resetAndUnblock();
	}

	private void processGetResponse(APDU apdu) {
		byte[] buffer = apdu.getBuffer();
		short le = apdu.setOutgoing();

		if( !pin.isValidated() ) {
			ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
		}

		if(ram_chaining[BYTES_REMAINING] <= (short) 0) {
			ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
		}

		short ram_len = ram_chaining[BYTES_REMAINING] > 256 ? 256 : ram_chaining[BYTES_REMAINING];
		if(le != ram_len) {
			ISOException.throwIt( (short)(ISO7816.SW_CORRECT_LENGTH_00 | ram_len));
		}

		if((short)(ram_chaining[CURRENT_POS] + ram_chaining[BYTES_REMAINING]) > RAM_BUF_LEN) {
			ISOException.throwIt(ISO7816.SW_UNKNOWN);
		}

		short sendLen = ram_chaining[BYTES_REMAINING] > 256 ? 256 : ram_chaining[BYTES_REMAINING];
		apdu.setOutgoingLength(sendLen);
		apdu.sendBytesLong(ram_buf, ram_chaining[CURRENT_POS], sendLen);
		short bytesLeft = (short)(ram_chaining[BYTES_REMAINING] - sendLen);
		if(bytesLeft > 0) {
			ram_chaining[BYTES_REMAINING] = bytesLeft;
			ram_chaining[CURRENT_POS] = (short)(ram_chaining[CURRENT_POS] + sendLen);
			short getRespLen = bytesLeft > 256 ? 256 : bytesLeft;
			ISOException.throwIt( (short)(ISO7816.SW_BYTES_REMAINING_00 | getRespLen) );
		} else {
			ram_chaining[BYTES_REMAINING] = 0;
			ram_chaining[CURRENT_POS] = 0;
			ISOException.throwIt(ISO7816.SW_NO_ERROR);
		}

	}

	private void processGetChallenge(APDU apdu) {
		byte[] buffer = apdu.getBuffer();
		byte p1 = buffer[ISO7816.OFFSET_P1];
		byte p2 = buffer[ISO7816.OFFSET_P2];

		if(randomData == null) {
			ISOException.throwIt(ISO7816.SW_FUNC_NOT_SUPPORTED);
		}

		if(p1 != 0x00 || p2 != 0x00) {
			ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
		}
		short le = apdu.setOutgoing();
		if(le <= 0 || le > 256) {
			ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
		}
		randomData.generateData(buffer, (short)0, le);
		apdu.setOutgoingLength(le);
		apdu.sendBytes((short)0, le);
	}

}