/*
 * Copyright (c) 2001
 * BSD ?
 */
//
// $Workfile: CardEdge.java $
// $Revision: 288 $
// $Date: 2009-05-29 12:55:50 +0200 (Fri, 29 May 2009) $
// $Author: rousseau $
// $Archive: CardEdge $
// $Modtime: 5/02/00 8:48p $
//
package com.musclecard.CardEdge;

import javacard.framework.*;
import javacard.security.*;
import javacardx.crypto.*;

/**
 * Implements MUSCLE's Card Edge Specification.<p>
 *
 * TODO:<ul>
 * <li> Allows maximum number of keys and PINs and total mem
 *      to be specified at the instantiation moment.<p>
 * <li> How do transactions fit in the methods ?
 *      <li> Where should we issue begin/end transaction ?
 *      <li> Should we ever abort transaction ? Where ?
 * <li> Everytime there is an "if (avail < )" check, call
 *      ThrowDeleteObjects().
 * </ul>
 * NOTES:<ul>
 * <li> C preprocessor flags<ul>
 *        <li> Encrytpion algorithms:
 *             WITH_RSA, WITH_DSA, WITH_DES, WITH_3DES
 *        <li> ComputeCrypt directions:
 *             WITH_ENCRYPT, WITH_DECRYPT, WITH_SIGN
 *        <li> Enable/Disable External Authenticate:
 *             WITH_EXT_AUTH
 *        <li> Enable/Disable PIN Policy enforcement:
 *             WITH_PIN_POLICY
 *        <li> Enable/Disable Extended APDU support:
 *             WITH_EXT_APDU
 *      </ul>
 * <li> C preprocessor defines<ul>
 *        <li> JAVA_PACKAGE:	The name of Java package for this Applet
 *        <li> JAVA_APPLET:	The name of Java class for the Applet
 *      </ul>
 * </ul>
 */
import javacard.framework.ISO7816;
import javacard.framework.ISOException;
import javacard.security.RandomData;
import javacard.framework.ISO7816;
import javacard.framework.JCSystem;
import javacard.security.CryptoException;
import javacard.framework.ISOException;
public class CardEdge extends javacard.framework.Applet { 
	private short fault_resistant_short_g[] = new short[1];	/* help array to store negations of global short variables*/ 
	private boolean fault_resistant_boolean_g[] = new boolean[2];	/* help array to store negations of global boolean variables*/ 
	private byte fault_resistant_byte_g[] = new byte[4];	/* help array to store negations of global byte variables*/


    /* constants declaration */
    // Maximum number of keys handled by the Cardlet
    private final static byte MAX_NUM_KEYS = (byte) 8;
    // Maximum number of PIN codes
    private final static byte MAX_NUM_PINS = (byte) 8;
    // Maximum number of keys allowed for ExtAuth
    private final static byte MAX_NUM_AUTH_KEYS = (byte) 6;
    /* Pin policies constants (OR-ed in var pinPolicies) */
    /** Enable pin size check		*/
    private final static byte PIN_POLICY_SIZE = (byte) 0x01;
    /** Enable pin charset check	*/
    private final static byte PIN_POLICY_CHARSET = (byte) 0x02;
    /** Enable charset mixing check	*/
    private final static byte PIN_POLICY_MIXED = (byte) 0x04;
    /* Options for POLICY_CHARSET policy (OR-ed in var pinCharset) */
    /** Numbers are allowed */
    private final static byte PIN_CHARSET_NUMBERS = (byte) 0x01;
    /** Upper case letters */
    private final static byte PIN_CHARSET_UC_LETTERS = (byte) 0x02;
    /** Lower case letters */
    private final static byte PIN_CHARSET_LC_LETTERS = (byte) 0x04;
    /** Punctuation symbols: , . ; : ? */
    private final static byte PIN_CHARSET_PUNCT = (byte) 0x08;
    /** Other binary codes (NUMBERS | OTHERS excludes LETTERS and PUNCT) */
    private final static byte PIN_CHARSET_OTHERS = (byte) 0x80;
    /* Options for POLICY_MIXED policy (OR-ed in var pinMixType) */
    /** PIN must contain chars from at least 2 different char sets */
    private final static byte PIN_MIXED_TWO = (byte) 0x01;
    /** PIN must at least contain chars from both upper and lower case */
    private final static byte PIN_MIXED_CASE = (byte) 0x02;
    /** PIN must at least contain 1 char from each char set */
    private final static byte PIN_MIXED_ALL = (byte) 0x04;
    // Actually enforced PIN Policies
    private final static byte pinPolicies =
            (byte) (PIN_POLICY_SIZE | PIN_POLICY_CHARSET | PIN_POLICY_MIXED);
    // Minimum PIN size
    private final static byte pinMinSize = (byte) 4;
    // Maximum PIN size
    private final static byte pinMaxSize = (byte) 16;
    // Maximum external authentication tries per key
    private final static byte MAX_KEY_TRIES = (byte) 5;
    // Initial PIN 0 value
    private static byte[] PIN_INIT_VALUE; /* = {
    (byte) 0x30, (byte) 0x30, (byte) 0x30, (byte) 0x30,
    (byte) 0x30, (byte) 0x30, (byte) 0x30, (byte) 0x30
    };
     */
    // Import/Export Object ID

    private final static short IN_OBJECT_CLA = (short) 0xFFFF;
    private final static short IN_OBJECT_ID = (short) 0xFFFE;
    private final static short OUT_OBJECT_CLA = (short) 0xFFFF;
    private final static short OUT_OBJECT_ID = (short) 0xFFFF;
    private final static byte KEY_ACL_SIZE = (byte) 6;
    // Standard public ACL
    private static byte[] STD_PUBLIC_ACL;/* = {
    0x0000,	// Read always allowed
    0x0000,	// Write always allowed
    0x0000	// Delete always allowed
    };
     */

    private static byte[] acl; // Temporary ACL
    // code of CLA byte in the command APDU header
    private final static byte CardEdge_CLA = (byte) 0xB0;
    /****************************************
     *          Instruction codes           *
     ****************************************/
    // Keys' use and management
    private final static byte INS_SETUP = (byte) 0x2A;
    // Keys' use and management
    private final static byte INS_GEN_KEYPAIR = (byte) 0x30;
    private final static byte INS_IMPORT_KEY = (byte) 0x32;
    private final static byte INS_EXPORT_KEY = (byte) 0x34;
    private final static byte INS_COMPUTE_CRYPT = (byte) 0x36;
    // External authentication
    private final static byte INS_CREATE_PIN = (byte) 0x40;
    private final static byte INS_VERIFY_PIN = (byte) 0x42;
    private final static byte INS_CHANGE_PIN = (byte) 0x44;
    private final static byte INS_UNBLOCK_PIN = (byte) 0x46;
    private final static byte INS_LOGOUT_ALL = (byte) 0x60;
    private final static byte INS_GET_CHALLENGE = (byte) 0x62;
    private final static byte INS_EXT_AUTH = (byte) 0x38;
    // Objects' use and management
    private final static byte INS_CREATE_OBJ = (byte) 0x5A;
    private final static byte INS_DELETE_OBJ = (byte) 0x52;
    private final static byte INS_READ_OBJ = (byte) 0x56;
    private final static byte INS_WRITE_OBJ = (byte) 0x54;
    // Status information
    private final static byte INS_LIST_OBJECTS = (byte) 0x58;
    private final static byte INS_LIST_PINS = (byte) 0x48;
    private final static byte INS_LIST_KEYS = (byte) 0x3A;
    private final static byte INS_GET_STATUS = (byte) 0x3C;
    // TODO: Allocate error return codes
    /** There have been memory problems on the card */
    private final static short SW_NO_MEMORY_LEFT = ObjectManager.SW_NO_MEMORY_LEFT;
    /** Entered PIN is not correct */
    private final static short SW_AUTH_FAILED = (short) 0x9C02;
    /** Required operation is not allowed in actual circumstances */
    private final static short SW_OPERATION_NOT_ALLOWED = (short) 0x9C03;
    /** Required feature is not (yet) supported */
    private final static short SW_UNSUPPORTED_FEATURE = (short) 0x9C05;
    /** Required operation was not authorized because of a lack of privileges */
    private final static short SW_UNAUTHORIZED = (short) 0x9C06;
    /** Required object is missing */
    private final static short SW_OBJECT_NOT_FOUND = (short) 0x9C07;
    /** New object ID already in use */
    private final static short SW_OBJECT_EXISTS = (short) 0x9C08;
    /** Algorithm specified is not correct */
    private final static short SW_INCORRECT_ALG = (short) 0x9C09;
    /** Incorrect P1 parameter */
    private final static short SW_INCORRECT_P1 = (short) 0x9C10;
    /** Incorrect P2 parameter */
    private final static short SW_INCORRECT_P2 = (short) 0x9C11;
    /** No more data available */
    private final static short SW_SEQUENCE_END = (short) 0x9C12;
    /** Invalid input parameter to command */
    private final static short SW_INVALID_PARAMETER = (short) 0x9C0F;
    /** Verify operation detected an invalid signature */
    private final static short SW_SIGNATURE_INVALID = (short) 0x9C0B;
    /** Operation has been blocked for security reason	*/
    private final static short SW_IDENTITY_BLOCKED = (short) 0x9C0C;
    /** Unspecified error */
    private final static short SW_UNSPECIFIED_ERROR = (short) 0x9C0D;
    /** For debugging purposes */
    private final static short SW_INTERNAL_ERROR = (short) 0x9CFF;
    // Algorithm Type in APDUs
    private final static byte ALG_RSA = (byte) 0x00;
    private final static byte ALG_RSA_CRT = (byte) 0x01;
    private final static byte ALG_DSA = (byte) 0x02;
    private final static byte ALG_DES = (byte) 0x03;
    private final static byte ALG_3DES = (byte) 0x04;
    private final static byte ALG_3DES3 = (byte) 0x05;
    // Key Type in Key Blobs
    private final static byte KEY_RSA_PUBLIC = (byte) 0x01;
    private final static byte KEY_RSA_PRIVATE = (byte) 0x02;
    private final static byte KEY_RSA_PRIVATE_CRT = (byte) 0x03;
    private final static byte KEY_DSA_PUBLIC = (byte) 0x04;
    private final static byte KEY_DSA_PRIVATE = (byte) 0x05;
    private final static byte KEY_DES = (byte) 0x06;
    private final static byte KEY_3DES = (byte) 0x07;
    private final static byte KEY_3DES3 = (byte) 0x08;
    // KeyBlob Encoding in Key Blobs
    private final static byte BLOB_ENC_PLAIN = (byte) 0x00;
    // Cipher Operations admitted in ComputeCrypt()
    private final static byte OP_INIT = (byte) 0x01;
    private final static byte OP_PROCESS = (byte) 0x02;
    private final static byte OP_FINALIZE = (byte) 0x03;
    // Cipher Directions admitted in ComputeCrypt()
    private final static byte CD_SIGN = (byte) 0x01;
    private final static byte CD_VERIFY = (byte) 0x02;
    private final static byte CD_ENCRYPT = (byte) 0x03;
    private final static byte CD_DECRYPT = (byte) 0x04;
    // Cipher Modes admitted in ComputeCrypt()
    private final static byte CM_RSA_NOPAD = (byte) 0x00;
    private final static byte CM_RSA_PAD_PKCS1 = (byte) 0x01;
    private final static byte CM_DSA_SHA = (byte) 0x10;
    private final static byte CM_DES_CBC_NOPAD = (byte) 0x20;
    private final static byte CM_DES_ECB_NOPAD = (byte) 0x21;
    private final static byte DL_APDU = (byte) 0x01;
    private final static byte DL_OBJECT = (byte) 0x02;
    private final static byte LIST_OPT_RESET = (byte) 0x00;
    private final static byte LIST_OPT_NEXT = (byte) 0x01;
    private final static byte OPT_DEFAULT =
            (byte) 0x00; // For every algorithm: use default JavaCard generation parameters
    private final static byte OPT_RSA_PUB_EXP =
            (byte) 0x01; // For RSA: provide public exponent
    private final static byte OPT_DSA_GPQ =
            (byte) 0x02; // For DSA: provide p,q,g public key parameters
    // Offsets in buffer[] for key generation
    private final static short OFFSET_GENKEY_ALG =
            (short) (ISO7816.OFFSET_CDATA);
    private final static short OFFSET_GENKEY_SIZE =
            (short) (ISO7816.OFFSET_CDATA + 1);
    private final static short OFFSET_GENKEY_PRV_ACL =
            (short) (ISO7816.OFFSET_CDATA + 3);
    private final static short OFFSET_GENKEY_PUB_ACL =
            (short) (OFFSET_GENKEY_PRV_ACL + KEY_ACL_SIZE);
    private final static short OFFSET_GENKEY_OPTIONS =
            (short) (OFFSET_GENKEY_PUB_ACL + KEY_ACL_SIZE);
    private final static short OFFSET_GENKEY_RSA_PUB_EXP_LENGTH =
            (short) (OFFSET_GENKEY_OPTIONS + 1);
    private final static short OFFSET_GENKEY_RSA_PUB_EXP_VALUE =
            (short) (OFFSET_GENKEY_RSA_PUB_EXP_LENGTH + 2);
    private final static short OFFSET_GENKEY_DSA_GPQ =
            (short) (OFFSET_GENKEY_OPTIONS + 1);
    /****************************************
     *    Instance variables declaration    *
     ****************************************/
    // Memory Manager
    private MemoryManager mem;
    // Object Manager
    private ObjectManager om;
    // Key objects (allocated on demand)
    private Key[] keys;
    // Key ACLs
    private byte[] keyACLs;
    // Key Tries Left
    private byte[] keyTries;
    // Key iterator for ListKeys: it's an offset in the keys[] array.
    private byte key_it=__set_byte_g((byte)(0),(short)0);
    // True if a GetChallenge() has been issued
    private boolean getChallengeDone=__set_boolean_g((boolean)(false),(short)0);
    /* KeyPair, Cipher and Signature objects		*
     *   These are allocated on demand			*
     *   TODO: Here we could jave just 1 Object[] and	*
     *         make proper casts when needed		*/
    private Cipher[] ciphers;
    private Signature[] signatures;
    // Says if we are using a signature or a cipher object
    private byte[] ciph_dirs;
    private KeyPair[] keyPairs;
    private RandomData randomData; // RandomData class instance
    // OwnerPIN objects, allocated on demand
    private OwnerPIN[] pins, ublk_pins;
    // Buffer for storing extended APDUs
    private byte[] recvBuffer;
    /* Logged identities: this is used for faster access	*
     * control, so we don't have to ping each PIN object	*/
    private short logged_ids=__set_short_g((short)(0),(short)0);
    /* For the setup function - should only be called once      */
    private boolean setupDone = __set_boolean_g((boolean)(false),(short)1);
    private byte create_object_ACL=__set_byte_g((byte)(0),(short)1);
    private byte create_key_ACL=__set_byte_g((byte)(0),(short)2);
    private byte create_pin_ACL=__set_byte_g((byte)(0),(short)3);

    /****************************************
     *               Methods                *
     ****************************************/
    private CardEdge(byte[] bArray, short bOffset, byte bLength) {
        PIN_INIT_VALUE = new byte[8];
        /* Muscle00 satisfies all current policies (if charset allows it) */
        PIN_INIT_VALUE[0] = (byte) 'M';
        PIN_INIT_VALUE[1] = (byte) 'u';
        PIN_INIT_VALUE[2] = (byte) 's';
        PIN_INIT_VALUE[3] = (byte) 'c';
        PIN_INIT_VALUE[4] = (byte) 'l';
        PIN_INIT_VALUE[5] = (byte) 'e';
        PIN_INIT_VALUE[6] = (byte) '0';
        PIN_INIT_VALUE[7] = (byte) '0';
        /* If init pin code does not satisfy policies, internal error */
        if (!CheckPINPolicy(PIN_INIT_VALUE, (short) 0,
                (byte) PIN_INIT_VALUE.length)) {
            ISOException.throwIt(SW_INTERNAL_ERROR);
        }
        ublk_pins = new OwnerPIN[MAX_NUM_PINS];
        pins = new OwnerPIN[MAX_NUM_PINS];
        // TODO: pass in starting PIN setting with instantiation
 /* Setting initial PIN n.0 value */
        pins[0] = new OwnerPIN((byte) 3, pinMaxSize);
        pins[0].update(PIN_INIT_VALUE, (short) 0,
                (byte) PIN_INIT_VALUE.length);
    } // end of the constructor

    public static void install(byte[] bArray, short bOffset, byte bLength) {
        CardEdge wal = new CardEdge(bArray, bOffset, bLength);
        /* Register the Applet (copied code) */
	/***** BEGIN ORIGINAL [IF_SWITCH_2] *****
	        if (bArray[bOffset] == 0) {
	            wal.register();
	        } else {
	            wal.register(bArray, (short) (bOffset + 1),
	                    (byte) (bArray[bOffset]));
	        } 
	***** END ORIGINAL [IF_SWITCH_2] *****/ 
	/***** BEGIN REPLACE [IF_SWITCH_2] *****/ 
	IF_SWITCH_2: {
		switch (__getRandomBit()){
			case -1: 
				ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
			case 0:
				if ((bArray[bOffset] == 0)) {
					{
					            wal.register();
					        }
				} else {
					{
					            wal.register(bArray, (short) (bOffset + 1),
					                    (byte) (bArray[bOffset]));
					        }
				}
	break IF_SWITCH_2;
			case 1:
				if (!(bArray[bOffset] == 0)) {
					{
					            wal.register(bArray, (short) (bOffset + 1),
					                    (byte) (bArray[bOffset]));
					        }
				} else {
					{
					            wal.register();
					        }
				}
	break IF_SWITCH_2;
			default:
				ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
		}
	}
	/***** END REPLACE [IF_SWITCH_2] *****/ 
    } // end of install method

    public boolean select() {
        /* Application has been selected:
        Do session cleanup operation */
        // Destroy the IO objects (if they exist)
        if (__get_boolean_g(setupDone,(short)1)) {
            om.destroyObject(IN_OBJECT_CLA, IN_OBJECT_ID, true);
            om.destroyObject(OUT_OBJECT_CLA, OUT_OBJECT_ID, true);
        }
        LogOutAll();
        return true;
    }// end of select method

    public void deselect() {
        // Destroy the IO objects (if they exist)
        if (__get_boolean_g(setupDone,(short)1)) {
            om.destroyObject(IN_OBJECT_CLA, IN_OBJECT_ID, true);
            om.destroyObject(OUT_OBJECT_CLA, OUT_OBJECT_ID, true);
        }
        LogOutAll();
    }

    public void process(APDU apdu) {
        // APDU object carries a byte array (buffer) to
        // transfer incoming and outgoing APDU header
        // and data bytes between card and CAD
        // At this point, only the first header bytes
        // [CLA, INS, P1, P2, P3] are available in
        // the APDU buffer.
        // The interface javacard.framework.ISO7816
        // declares constants to denote the offset of
        // these bytes in the APDU buffer
        if (selectingApplet()) {
            ISOException.throwIt(ISO7816.SW_NO_ERROR);
        }
        byte[] buffer = apdu.getBuffer();
        // check SELECT APDU command
        if ((buffer[ISO7816.OFFSET_CLA] == 0)
                && (buffer[ISO7816.OFFSET_INS] == (byte) 0xA4)) {
            return;
        }
        // verify the reset of commands have the
        // correct CLA byte, which specifies the
        // command structure
        //if (buffer[ISO7816.OFFSET_CLA] != CardEdge_CLA) {
        //ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
        //}
        byte ins = buffer[ISO7816.OFFSET_INS];
        if (!__get_boolean_g(setupDone,(short)1) && (ins != (byte) INS_SETUP)) {
            ISOException.throwIt(SW_UNSUPPORTED_FEATURE);
        }
        if (__get_boolean_g(setupDone,(short)1) && (ins == (byte) INS_SETUP)) {
            ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
        }
	/***** BEGIN ORIGINAL [IDENTIFY_BLOCKS_1] *****
	        switch (ins) {
	            case INS_SETUP:
	                setup(apdu, buffer);
	                break;
	            case INS_GEN_KEYPAIR:
	                GenerateKeyPair(apdu, buffer);
	                break;
	            case INS_IMPORT_KEY:
	                ImportKey(apdu, buffer);
	                break;
	            case INS_EXPORT_KEY:
	                ExportKey(apdu, buffer);
	                break;
	            case INS_COMPUTE_CRYPT:
	                ComputeCrypt(apdu, buffer);
	                break;
	            case INS_VERIFY_PIN:
	                VerifyPIN(apdu, buffer);
	                break;
	            case INS_CREATE_PIN:
	                CreatePIN(apdu, buffer);
	                break;
	            case INS_CHANGE_PIN:
	                ChangePIN(apdu, buffer);
	                break;
	            case INS_UNBLOCK_PIN:
	                UnblockPIN(apdu, buffer);
	                break;
	            case INS_LOGOUT_ALL:
	                LogOutAll();
	                break;
	            case INS_GET_CHALLENGE:
	                GetChallenge(apdu, buffer);
	                break;
	            case INS_EXT_AUTH:
	                ISOException.throwIt(SW_UNSUPPORTED_FEATURE);
	                break;
	            case INS_CREATE_OBJ:
	                CreateObject(apdu, buffer);
	                break;
	            case INS_DELETE_OBJ:
	                DeleteObject(apdu, buffer);
	                break;
	            case INS_READ_OBJ:
	                ReadObject(apdu, buffer);
	                break;
	            case INS_WRITE_OBJ:
	                WriteObject(apdu, buffer);
	                break;
	            case INS_LIST_PINS:
	                ListPINs(apdu, buffer);
	                break;
	            case INS_LIST_OBJECTS:
	                ListObjects(apdu, buffer);
	                break;
	            case INS_LIST_KEYS:
	                ListKeys(apdu, buffer);
	                break;
	            case INS_GET_STATUS:
	                GetStatus(apdu, buffer);
	                break;
	            default:
	                ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
	        } 
	***** END ORIGINAL [IDENTIFY_BLOCKS_1] *****/ 
	/***** BEGIN REPLACE [IDENTIFY_BLOCKS_1] *****/ 
	BLOCK_1: { switch (ins) {
	            case INS_SETUP:
	                setup(apdu, buffer);
	                break BLOCK_1;
	            case INS_GEN_KEYPAIR:
	                GenerateKeyPair(apdu, buffer);
	                break BLOCK_1;
	            case INS_IMPORT_KEY:
	                ImportKey(apdu, buffer);
	                break BLOCK_1;
	            case INS_EXPORT_KEY:
	                ExportKey(apdu, buffer);
	                break BLOCK_1;
	            case INS_COMPUTE_CRYPT:
	                ComputeCrypt(apdu, buffer);
	                break BLOCK_1;
	            case INS_VERIFY_PIN:
	                VerifyPIN(apdu, buffer);
	                break BLOCK_1;
	            case INS_CREATE_PIN:
	                CreatePIN(apdu, buffer);
	                break BLOCK_1;
	            case INS_CHANGE_PIN:
	                ChangePIN(apdu, buffer);
	                break BLOCK_1;
	            case INS_UNBLOCK_PIN:
	                UnblockPIN(apdu, buffer);
	                break BLOCK_1;
	            case INS_LOGOUT_ALL:
	                LogOutAll();
	                break BLOCK_1;
	            case INS_GET_CHALLENGE:
	                GetChallenge(apdu, buffer);
	                break BLOCK_1;
	            case INS_EXT_AUTH:
	                ISOException.throwIt(SW_UNSUPPORTED_FEATURE);
	                break BLOCK_1;
	            case INS_CREATE_OBJ:
	                CreateObject(apdu, buffer);
	                break BLOCK_1;
	            case INS_DELETE_OBJ:
	                DeleteObject(apdu, buffer);
	                break BLOCK_1;
	            case INS_READ_OBJ:
	                ReadObject(apdu, buffer);
	                break BLOCK_1;
	            case INS_WRITE_OBJ:
	                WriteObject(apdu, buffer);
	                break BLOCK_1;
	            case INS_LIST_PINS:
	                ListPINs(apdu, buffer);
	                break BLOCK_1;
	            case INS_LIST_OBJECTS:
	                ListObjects(apdu, buffer);
	                break BLOCK_1;
	            case INS_LIST_KEYS:
	                ListKeys(apdu, buffer);
	                break BLOCK_1;
	            case INS_GET_STATUS:
	                GetStatus(apdu, buffer);
	                break BLOCK_1;
	            default:
	                ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
	        } }
	/***** END REPLACE [IDENTIFY_BLOCKS_1] *****/ 
        ;
    } // end of process method

    /********** SETUP FUNCTION *********/
    private void setup(APDU apdu, byte[] buffer) {
        short bytesLeft = Util.makeShort((byte) 0x00,
                buffer[ISO7816.OFFSET_LC]);
        if (bytesLeft != apdu.setIncomingAndReceive()) {
            ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
        }
        short base = (short) (ISO7816.OFFSET_CDATA);
        byte numBytes = buffer[base++];
        OwnerPIN pin = pins[0];
        if (!CheckPINPolicy(buffer, base, numBytes)) {
            ISOException.throwIt(SW_INVALID_PARAMETER);
        }
        if (pin.getTriesRemaining() == (byte) 0x00) {
            ISOException.throwIt(SW_IDENTITY_BLOCKED);
        }
        if (!pin.check(buffer, base, numBytes)) {
            ISOException.throwIt(SW_AUTH_FAILED);
        }
        base += numBytes;
        byte pin_tries = buffer[base++];
        byte ublk_tries = buffer[base++];
        numBytes = buffer[base++];
        if (!CheckPINPolicy(buffer, base, numBytes)) {
            ISOException.throwIt(SW_INVALID_PARAMETER);
        }
        pins[0] = new OwnerPIN(pin_tries, pinMaxSize);
        pins[0].update(buffer, base, numBytes);
        base += numBytes;
        numBytes = buffer[base++];
        if (!CheckPINPolicy(buffer, base, numBytes)) {
            ISOException.throwIt(SW_INVALID_PARAMETER);
        }
        ublk_pins[0] = new OwnerPIN(ublk_tries, pinMaxSize);
        ublk_pins[0].update(buffer, base, numBytes);
        base += numBytes;
        pin_tries = buffer[base++];
        ublk_tries = buffer[base++];
        numBytes = buffer[base++];
        if (!CheckPINPolicy(buffer, base, numBytes)) {
            ISOException.throwIt(SW_INVALID_PARAMETER);
        }
        pins[1] = new OwnerPIN(pin_tries, pinMaxSize);
        pins[1].update(buffer, base, numBytes);
        base += numBytes;
        numBytes = buffer[base++];
        if (!CheckPINPolicy(buffer, base, numBytes)) {
            ISOException.throwIt(SW_INVALID_PARAMETER);
        }
        ublk_pins[1] = new OwnerPIN(ublk_tries, pinMaxSize);
        ublk_pins[1].update(buffer, base, numBytes);
        base += numBytes;
        base += (short) 2;
        short mem_size = Util.getShort(buffer, base);
        base += (short) 2;
        create_object_ACL=__set_byte_g((byte)(buffer[base++]),(short)1);
        create_key_ACL=__set_byte_g((byte)(buffer[base++]),(short)2);
        create_pin_ACL=__set_byte_g((byte)(buffer[base++]),(short)3);
        mem = new MemoryManager((short) mem_size);
        om = new ObjectManager(mem);
        keys = new Key[MAX_NUM_KEYS];
        keyACLs = new byte[(short) (MAX_NUM_KEYS * KEY_ACL_SIZE)];
        keyTries = new byte[MAX_NUM_KEYS];
        for (byte i = (byte) 0; i < (byte) MAX_NUM_KEYS; i++) {
            keyTries[i] = MAX_KEY_TRIES;
        }
        keyPairs = new KeyPair[MAX_NUM_KEYS];
        ciphers = new Cipher[MAX_NUM_KEYS];
        signatures = new Signature[MAX_NUM_KEYS];
        ciph_dirs = new byte[MAX_NUM_KEYS];
        for (byte i = (byte) 0; i < (byte) MAX_NUM_KEYS; i++) {
            ciph_dirs[i] = (byte) 0xFF;
        }
        logged_ids=__set_short_g((short)(0x00),(short)0); // No identities logged in
        getChallengeDone=__set_boolean_g((boolean)(false),(short)0); // No GetChallenge() issued so far
        randomData = null; // Will be created on demand when needed
        STD_PUBLIC_ACL = new byte[KEY_ACL_SIZE];
        for (byte i = (byte) 0; i < (byte) KEY_ACL_SIZE; i += (short) 2) {
            Util.setShort(STD_PUBLIC_ACL, i, (short) 0x0000);
        }
        setupDone=__set_boolean_g((boolean)(true),(short)1);
    }

    /********** UTILITY FUNCTIONS **********/
    /* SendData() wraps the setGoing(), setLength(), .. stuff		*
     *   that could be necessary to be fully JavaCard compliant. 	*/
    private void sendData(APDU apdu, byte[] data, short offset, short size) {
        if (size > 255) {
            ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
        }
        apdu.setOutgoing();
        apdu.setOutgoingLength(size);
        apdu.sendBytesLong(data, offset, size);
    }
    /* Retrieves the Cipher object to be used w/ the specified key	*
     *   and algorithm id (Cipher.ALG_XX).				*
     *   If exists, check it has the proper algorithm and throws	*
     *   SW_OP_NOT_ALLOWED if not					*
     *   If not, creates it						*/

    private Cipher getCipher(byte key_nb, byte alg_id) {
	/***** BEGIN ORIGINAL [IF_SWITCH_18] *****
	        if (ciphers[key_nb] == null) {
	            ciphers[key_nb] = Cipher.getInstance(alg_id, false);
	        } else if (ciphers[key_nb].getAlgorithm() != alg_id) {
	            ISOException.throwIt(SW_OPERATION_NOT_ALLOWED);
	        } 
	***** END ORIGINAL [IF_SWITCH_18] *****/ 
	/***** BEGIN REPLACE [IF_SWITCH_18] *****/ 
	IF_SWITCH_18: {
		switch (__getRandomBit()){
			case -1: 
				ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
			case 0:
				if ((ciphers[key_nb] == null)) {
					{
					            ciphers[key_nb] = Cipher.getInstance(alg_id, false);
					        }
				} else {
					if (ciphers[key_nb].getAlgorithm() != alg_id) {
					            ISOException.throwIt(SW_OPERATION_NOT_ALLOWED);
					        }
				}
	break IF_SWITCH_18;
			case 1:
				if (!(ciphers[key_nb] == null)) {
					if (ciphers[key_nb].getAlgorithm() != alg_id) {
					            ISOException.throwIt(SW_OPERATION_NOT_ALLOWED);
					        }
				} else {
					{
					            ciphers[key_nb] = Cipher.getInstance(alg_id, false);
					        }
				}
	break IF_SWITCH_18;
			default:
				ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
		}
	}
	/***** END REPLACE [IF_SWITCH_18] *****/ 
        return ciphers[key_nb];
    }
    /* Retrieves the Signature object to be used w/ the specified key	*
     *   and algorithm id (Signature.ALG_XX).				*
     *   If exists, check it has the proper algorithm and throws	*
     *   SW_OPERATION_NOT_ALLOWED if not				*
     *   If does not exist, creates it					*/

    private Signature getSignature(byte key_nb, byte alg_id) {
	/***** BEGIN ORIGINAL [IF_SWITCH_20] *****
	        if (signatures[key_nb] == null) {
	            signatures[key_nb] = Signature.getInstance(alg_id, false);
	        } else if (signatures[key_nb].getAlgorithm() != alg_id) {
	            ISOException.throwIt(SW_OPERATION_NOT_ALLOWED);
	        } 
	***** END ORIGINAL [IF_SWITCH_20] *****/ 
	/***** BEGIN REPLACE [IF_SWITCH_20] *****/ 
	IF_SWITCH_20: {
		switch (__getRandomBit()){
			case -1: 
				ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
			case 0:
				if ((signatures[key_nb] == null)) {
					{
					            signatures[key_nb] = Signature.getInstance(alg_id, false);
					        }
				} else {
					if (signatures[key_nb].getAlgorithm() != alg_id) {
					            ISOException.throwIt(SW_OPERATION_NOT_ALLOWED);
					        }
				}
	break IF_SWITCH_20;
			case 1:
				if (!(signatures[key_nb] == null)) {
					if (signatures[key_nb].getAlgorithm() != alg_id) {
					            ISOException.throwIt(SW_OPERATION_NOT_ALLOWED);
					        }
				} else {
					{
					            signatures[key_nb] = Signature.getInstance(alg_id, false);
					        }
				}
	break IF_SWITCH_20;
			default:
				ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
		}
	}
	/***** END REPLACE [IF_SWITCH_20] *****/ 
        return signatures[key_nb];
    }

    /** Retrieves the Key object to be used w/ the specified key number,*
     *   key type (KEY_XX) and size.					*
     *   If exists, check it has the proper key type			*
     *   If not, creates it.						*
     *   @return Retrieved Key object or throws SW_UNATUTHORIZED, SW_OPERATION_NOT_ALLOWED
     */
    private Key getKey(byte key_nb, byte key_type, short key_size) {
        byte jc_key_type = keyType2JCType(key_type);
	/***** BEGIN ORIGINAL [IF_SWITCH_22] *****
	        if (keys[key_nb] == null) {
	            // We have to create the Key
	     
	            if ((__get_byte_g(create_key_ACL,(short)2) == (byte) 0xFF)
	                    || (((__get_short_g(logged_ids,(short)0) & __get_byte_g(create_key_ACL,(short)2)) == (short) 0x0000)
	                    && (__get_byte_g(create_key_ACL,(short)2) != (byte) 0x00))) {
	                ISOException.throwIt(SW_UNAUTHORIZED);
	            }
	            keys[key_nb] = KeyBuilder.buildKey(jc_key_type, key_size, false);
	        } else {
	            // Key already exists: check size & type
	     
	            if ((keys[key_nb].getSize() != key_size)
	                    || (keys[key_nb].getType() != jc_key_type)) {
	                ISOException.throwIt(SW_OPERATION_NOT_ALLOWED);
	            }
	        } 
	***** END ORIGINAL [IF_SWITCH_22] *****/ 
	/***** BEGIN REPLACE [IF_SWITCH_22] *****/ 
	IF_SWITCH_22: {
		switch (__getRandomBit()){
			case -1: 
				ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
			case 0:
				if ((keys[key_nb] == null)) {
					{
					            // We have to create the Key
					     /* Check that Identity n.0 is logged */
					            if ((__get_byte_g(create_key_ACL,(short)2) == (byte) 0xFF)
					                    || (((__get_short_g(logged_ids,(short)0) & __get_byte_g(create_key_ACL,(short)2)) == (short) 0x0000)
					                    && (__get_byte_g(create_key_ACL,(short)2) != (byte) 0x00))) {
					                ISOException.throwIt(SW_UNAUTHORIZED);
					            }
					            keys[key_nb] = KeyBuilder.buildKey(jc_key_type, key_size, false);
					        }
				} else {
					{
					            // Key already exists: check size & type
					     /* TODO: As an option, we could just discard and recreate
					            if not of the correct type, but creates trash objects */
					            if ((keys[key_nb].getSize() != key_size)
					                    || (keys[key_nb].getType() != jc_key_type)) {
					                ISOException.throwIt(SW_OPERATION_NOT_ALLOWED);
					            }
					        }
				}
	break IF_SWITCH_22;
			case 1:
				if (!(keys[key_nb] == null)) {
					{
					            // Key already exists: check size & type
					     /* TODO: As an option, we could just discard and recreate
					            if not of the correct type, but creates trash objects */
					            if ((keys[key_nb].getSize() != key_size)
					                    || (keys[key_nb].getType() != jc_key_type)) {
					                ISOException.throwIt(SW_OPERATION_NOT_ALLOWED);
					            }
					        }
				} else {
					{
					            // We have to create the Key
					     /* Check that Identity n.0 is logged */
					            if ((__get_byte_g(create_key_ACL,(short)2) == (byte) 0xFF)
					                    || (((__get_short_g(logged_ids,(short)0) & __get_byte_g(create_key_ACL,(short)2)) == (short) 0x0000)
					                    && (__get_byte_g(create_key_ACL,(short)2) != (byte) 0x00))) {
					                ISOException.throwIt(SW_UNAUTHORIZED);
					            }
					            keys[key_nb] = KeyBuilder.buildKey(jc_key_type, key_size, false);
					        }
				}
	break IF_SWITCH_22;
			default:
				ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
		}
	}
	/***** END REPLACE [IF_SWITCH_22] *****/ 
        return keys[key_nb];
    }
    // Converts a Applet's Key Type to the JavaCard one.

    private byte keyType2JCType(byte key_type) {
        switch (key_type) {
            case KEY_RSA_PUBLIC:
                return KeyBuilder.TYPE_RSA_PUBLIC;
            case KEY_RSA_PRIVATE:
                return KeyBuilder.TYPE_RSA_PRIVATE;
            case KEY_RSA_PRIVATE_CRT:
                return KeyBuilder.TYPE_RSA_CRT_PRIVATE;
            case KEY_DSA_PUBLIC:
                ISOException.throwIt(SW_UNSUPPORTED_FEATURE);
            case KEY_DSA_PRIVATE:
                ISOException.throwIt(SW_UNSUPPORTED_FEATURE);
            case KEY_DES:
                return KeyBuilder.TYPE_DES;
            case KEY_3DES:
            case KEY_3DES3:
                ISOException.throwIt(SW_UNSUPPORTED_FEATURE);
            default:
                ISOException.throwIt(SW_INVALID_PARAMETER);
        }
        return (byte) 0; // Avoid compiler warning
    }
    // Converts a JavaCard's Key Type to the Applet one.

    private byte getKeyType(Key key) {
        switch (key.getType()) {
            case KeyBuilder.TYPE_RSA_PUBLIC:
                return KEY_RSA_PUBLIC;
            case KeyBuilder.TYPE_RSA_PRIVATE:
                return KEY_RSA_PRIVATE;
            case KeyBuilder.TYPE_RSA_CRT_PRIVATE:
                return KEY_RSA_PRIVATE_CRT;
            case KeyBuilder.TYPE_DES:
                if (key.getSize() == (short) 64) {
                    return KEY_DES;
                }
            default:
                ISOException.throwIt(SW_INTERNAL_ERROR);
        }
        return (byte) 0; // Avoid compiler warning
    }

    /** Check from ACL if a key can be read */
    boolean authorizeKeyRead(byte key_nb) {
        short acl_offset = (short) (key_nb * KEY_ACL_SIZE);
        short required_ids = Util.getShort(keyACLs, acl_offset);
        return ((required_ids != (short) 0xFFFF)
                && ((short) (required_ids & __get_short_g(logged_ids,(short)0)) == required_ids));
    }

    /** Check from ACL if a key can be overwritten */
    boolean authorizeKeyWrite(byte key_nb) {
        short acl_offset = (short) (key_nb * KEY_ACL_SIZE + 2);
        short required_ids = Util.getShort(keyACLs, acl_offset);
        return ((required_ids != (short) 0xFFFF)
                && ((short) (required_ids & __get_short_g(logged_ids,(short)0)) == required_ids));
    }

    /** Check from ACL if a key can be used */
    boolean authorizeKeyUse(byte key_nb) {
        short acl_offset = (short) (key_nb * KEY_ACL_SIZE + 4);
        short required_ids = Util.getShort(keyACLs, acl_offset);
        return ((required_ids != (short) 0xFFFF)
                && ((short) (required_ids & __get_short_g(logged_ids,(short)0)) == required_ids));
    }

    /** Returns an ACL that requires current logged in identities. */
    byte[] getCurrentACL() {
        if (acl == null) {
            acl = new byte[KEY_ACL_SIZE];
        }
        byte i;
        for (i = (byte) 0; i < KEY_ACL_SIZE; i += (byte) 2) {
            Util.setShort(acl, i, __get_short_g(logged_ids,(short)0));
        }
        return acl;
    }

    /** Returns an ACL that disables all operations for the application. */
    byte[] getRestrictedACL() {
        if (acl == null) {
            acl = new byte[KEY_ACL_SIZE];
        }
        byte i;
        for (i = (byte) 0; i < KEY_ACL_SIZE; i += (byte) 2) {
            Util.setShort(acl, i, (short) 0xFFFF);
        }
        return acl;
    }

    /** Registers login of strong identity associated with a key number */
    private void LoginStrongIdentity(byte key_nb) {
        logged_ids=__set_short_g((short)(__get_short_g(logged_ids,(short)0)|(short) (((short) 0x01) << (key_nb + 8))),(short)0);
    }

    /** Registers logout of an identity. This must be called anycase
     *  when a PIN verification or external authentication fail		*/
    private void LogoutIdentity(byte id_nb) {
        logged_ids=__set_short_g((short)(__get_short_g(logged_ids,(short)0)&(short) ~(0x0001 << id_nb)),(short)0);
    }

    /** Deletes and zeros the IO objects and throws the passed in exception */
    private void ThrowDeleteObjects(short exception) {
        om.destroyObject(IN_OBJECT_CLA, IN_OBJECT_ID, true);
        om.destroyObject(OUT_OBJECT_CLA, OUT_OBJECT_ID, true);
        ISOException.throwIt(exception);
    }

    /** Checks if PIN policies are satisfied for a PIN code */
    private boolean CheckPINPolicy(byte[] pin_buffer, short pin_offset, byte pin_size) {
        if ((pinPolicies & PIN_POLICY_SIZE) != (byte) 0x00) {
            if ((pin_size < pinMinSize) || (pin_size > pinMaxSize)) {
                return false;
            }
        }
        return true;
    }

    /****************************************
     *            APDU handlers             *
     ****************************************/
    private void ComputeCrypt(APDU apdu, byte[] apduBuffer) {
        /* Buffer pointer */
        byte[] buffer = apduBuffer;
        short bytesLeft = Util.makeShort((byte) 0x00,
                buffer[ISO7816.OFFSET_LC]);
        short dataOffset = ISO7816.OFFSET_CDATA;
        if (bytesLeft != apdu.setIncomingAndReceive()) {
            ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
        }
        byte key_nb = buffer[ISO7816.OFFSET_P1];
        if ((key_nb < 0) || (key_nb >= MAX_NUM_KEYS)
                || (keys[key_nb] == null)) {
            ISOException.throwIt(SW_INCORRECT_P1);
        }
        /* Enforce Access Control */
        if (!authorizeKeyUse(key_nb)) {
            ISOException.throwIt(SW_UNAUTHORIZED);
        }
        byte op = buffer[ISO7816.OFFSET_P2];
        Key key = keys[key_nb];
        byte ciph_dir;
        byte data_location;
        byte[] src_buff;
        short src_base;
        short src_avail;
        short size;
	/***** BEGIN ORIGINAL [IDENTIFY_BLOCKS_9] *****
	        switch (op) {
	            case OP_INIT:
	                if (bytesLeft < 3) {
	                    ISOException.throwIt(SW_INVALID_PARAMETER);
	                }
	                byte ciph_mode = buffer[dataOffset];
	                ciph_dir = buffer[(short) (dataOffset + 1)];
	                byte ciph_alg_id = 0;
	                data_location = buffer[(short) (dataOffset + 2)];
	                switch (data_location) {
	                    case DL_APDU:
	                        src_buff = buffer;
	                        src_base = (short) (dataOffset + 3);
	                        src_avail = (short) (bytesLeft - 3);
	                        break;
	                    case DL_OBJECT:
	                        src_buff = mem.getBuffer();
	                        src_base = om.getBaseAddress(IN_OBJECT_CLA, IN_OBJECT_ID);
	                        if (src_base == mem.NULL_OFFSET) {
	                            ISOException.throwIt(SW_OBJECT_NOT_FOUND);
	                        }
	                        src_avail = om.getSizeFromAddress(src_base);
	                        break;
	                    default:
	                        ISOException.throwIt(SW_INVALID_PARAMETER);
	                        return; // Compiler warning
	                }
	                if (src_avail < 2) {
	                    ISOException.throwIt(SW_INVALID_PARAMETER);
	                }
	                size = Util.getShort(src_buff, src_base);
	                if (src_avail < (short) (2 + size)) {
	                    ISOException.throwIt(SW_INVALID_PARAMETER);
	                }
	                switch (ciph_dir) {
	                    case CD_SIGN:
	                    case CD_VERIFY:
	                        switch (key.getType()) {
	                            case KeyBuilder.TYPE_RSA_PUBLIC:
	                            case KeyBuilder.TYPE_RSA_PRIVATE:
	                                ciph_alg_id = Signature.ALG_RSA_MD5_PKCS1;
	                                ISOException.throwIt(SW_UNSUPPORTED_FEATURE);
	                                break;
	                            case KeyBuilder.TYPE_DSA_PUBLIC:
	                            case KeyBuilder.TYPE_DSA_PRIVATE:
	                                ISOException.throwIt(SW_UNSUPPORTED_FEATURE);
	                                return;
	                            default:
	                                // DSA Encryption/Decryption is not supported by JavaCard !!
	                                ISOException.throwIt(SW_INCORRECT_ALG);
	                                return; // Compiler warning (ciph_alg_id)
	                        }
	                        Signature sign = getSignature(key_nb, ciph_alg_id);
	                        if (size == (short) 0) {
	                            sign.init(key, (ciph_dir == CD_SIGN) ? Signature.MODE_SIGN : Signature.MODE_VERIFY);
	                        } else {
	                            sign.init(key, (ciph_dir == CD_SIGN) ? Signature.MODE_SIGN : Signature.MODE_VERIFY,
	                                    src_buff, (short) (src_base + 2), size);
	                        }
	                        ciph_dirs[key_nb] = ciph_dir;
	                        break;
	                    case CD_ENCRYPT:
	                    case CD_DECRYPT:
	                        switch (key.getType()) {
	                            case KeyBuilder.TYPE_RSA_PUBLIC:
	                            case KeyBuilder.TYPE_RSA_PRIVATE:
	                            case KeyBuilder.TYPE_RSA_CRT_PRIVATE:
	                                if (ciph_mode == CM_RSA_NOPAD) {
	                                    ciph_alg_id = Cipher.ALG_RSA_NOPAD;
	                                } else if (ciph_mode == CM_RSA_PAD_PKCS1) {
	                                    ciph_alg_id = Cipher.ALG_RSA_PKCS1;
	                                } else {
	                                    ISOException.throwIt(SW_INVALID_PARAMETER);
	                                    return;
	                                }
	                                break;
	                            case KeyBuilder.TYPE_DES:
	                                if (ciph_mode == CM_DES_CBC_NOPAD) {
	                                    ciph_alg_id = Cipher.ALG_DES_CBC_NOPAD;
	                                } else if (ciph_mode == CM_DES_ECB_NOPAD) {
	                                    ciph_alg_id = Cipher.ALG_DES_ECB_NOPAD;
	                                } else {
	                                    ISOException.throwIt(SW_INVALID_PARAMETER);
	                                    return;
	                                }
	                                break;
	                            case KeyBuilder.TYPE_DSA_PUBLIC:
	                            case KeyBuilder.TYPE_DSA_PRIVATE:
	                                // DSA Encryption/Decryption is not supported by JavaCard !!
	                                ISOException.throwIt(SW_INVALID_PARAMETER);
	                                return;
	                            default:
	                                ISOException.throwIt(SW_INTERNAL_ERROR);
	                                return; // Compiler warning (ciph_alg_id unset)
	                        }
	                        Cipher ciph = getCipher(key_nb, ciph_alg_id);
	                        if (size == (short) 0) {
	                            ciph.init(key, (ciph_dir == CD_ENCRYPT) ? Cipher.MODE_ENCRYPT : Cipher.MODE_DECRYPT);
	                        } else {
	                            ciph.init(key, (ciph_dir == CD_ENCRYPT) ? Cipher.MODE_ENCRYPT : Cipher.MODE_DECRYPT,
	                                    src_buff, (short) (src_base + 2), size);
	                        }
	                        ciph_dirs[key_nb] = ciph_dir;
	                        break;
	                    default:
	                        ISOException.throwIt(SW_INVALID_PARAMETER);
	                }
	                break;
	            case OP_PROCESS:
	            case OP_FINALIZE:
	                ciph_dir = ciph_dirs[key_nb];
	                switch (ciph_dir) {
	                    case CD_SIGN:
	                    case CD_VERIFY:
	                        Signature sign = signatures[key_nb];
	                        if (sign == null)  {
	                            ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
	                        }
	                        data_location = buffer[dataOffset];
	                        switch (data_location) {
	                            case DL_APDU:
	                                src_buff = mem.getBuffer();
	                                // Skip Data Location byte.
	                                src_base = (short) (dataOffset + 1);
	                                src_avail = (short) (bytesLeft - 1);
	                                break;
	                            case DL_OBJECT:
	                                src_buff = mem.getBuffer();
	                                src_base = om.getBaseAddress(IN_OBJECT_CLA, IN_OBJECT_ID);
	                                if (src_base == MemoryManager.NULL_OFFSET) {
	                                    ISOException.throwIt(SW_OBJECT_NOT_FOUND);
	                                }
	                                src_avail = om.getSizeFromAddress(src_base);
	                                break;
	                            default:
	                                ISOException.throwIt(SW_INVALID_PARAMETER);
	                                return;
	                        }
	                        if (src_avail < 2) {
	                            ISOException.throwIt(SW_INVALID_PARAMETER);
	                        }
	                        size = Util.getShort(src_buff, src_base);
	                        // IO objects are allowed to be larger than size of contained data
	                        if (src_avail < (short) (2 + size)) {
	                            ISOException.throwIt(SW_INVALID_PARAMETER);
	                        }
	                        if (op == OP_PROCESS) {
	                            sign.update(src_buff, (short) (src_base + 2), size);
	                        } else {
	                            // OP_FINALIZE
	                            if (ciph_dir == CD_SIGN) {
	                                om.destroyObject(OUT_OBJECT_CLA, OUT_OBJECT_ID, true);
	                                short dst_base = om.createObject(OUT_OBJECT_CLA, OUT_OBJECT_ID,
	                                        (short) (sign.getLength() + 2),
	                                        getCurrentACL(), (short) 0);
	                                if (dst_base == MemoryManager.NULL_OFFSET) {
	                                    ISOException.throwIt(SW_NO_MEMORY_LEFT);
	                                }
	                                short sign_size = sign.sign(src_buff, (short) (src_base + 2),
	                                        size, mem.getBuffer(), (short) (dst_base + 2));
	                                if (sign_size > sign.getLength()) // We got a buffer overflow (unless we were in memory end and got an exception...)
	                                {
	                                    ISOException.throwIt(SW_INTERNAL_ERROR);
	                                }
	                                mem.setShort(dst_base, sign_size);
	                                // Actually send data back (and clear output buffer) only if location is APDU
	                                if (data_location == DL_APDU) {
	                                    sendData(apdu, mem.getBuffer(), dst_base, (short) (sign_size + 2));
	                                    om.destroyObject(OUT_OBJECT_CLA, OUT_OBJECT_ID, true);
	                                }
	                            } else { // ciph_dir == CD_VERIFY
	                                if (src_avail < (short) (2 + size + 2)) {
	                                    ISOException.throwIt(SW_INVALID_PARAMETER);
	                                }
	                                short sign_size = Util.getShort(src_buff, (short) (src_base + 2 + size));
	                                if (src_avail < (short) (2 + size + 2 + sign_size)) {
	                                    ISOException.throwIt(SW_INVALID_PARAMETER);
	                                }
	                                if (sign_size != sign.getLength()) {
	                                    ISOException.throwIt(SW_INVALID_PARAMETER);
	                                }
	                                if (!sign.verify(src_buff, (short) (src_base + 2), size,
	                                        src_buff, (short) (src_base + 2 + size + 2), sign_size)) {
	                                    ISOException.throwIt(SW_SIGNATURE_INVALID);
	                                }
	                            }
	                        }
	                        break;
	                    case CD_ENCRYPT:
	                    case CD_DECRYPT:
	                        Cipher ciph = ciphers[key_nb];
	                        if (ciph == null)  {
	                            ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
	                        }
	                        data_location = buffer[dataOffset];
	                        switch (data_location) {
	                            case DL_APDU:
	                                src_buff = buffer;
	                                src_base = (short) (dataOffset + 1);
	                                src_avail = (short) (bytesLeft - 1);
	                                break;
	                            case DL_OBJECT:
	                                src_buff = mem.getBuffer();
	                                src_base = om.getBaseAddress(IN_OBJECT_CLA, IN_OBJECT_ID);
	                                if (src_base == MemoryManager.NULL_OFFSET) {
	                                    ISOException.throwIt(SW_OBJECT_NOT_FOUND);
	                                }
	                                src_avail = om.getSizeFromAddress(src_base);
	                                break;
	                            default:
	                                ISOException.throwIt(SW_INVALID_PARAMETER);
	                                return;
	                        }
	                        if (src_avail < 2) {
	                            ISOException.throwIt(SW_INVALID_PARAMETER);
	                        }
	                        size = Util.getShort(src_buff, src_base);
	                        if (src_avail < (short) (2 + size)) {
	                            ISOException.throwIt(SW_INVALID_PARAMETER);
	                        }
	                        // TODO: Don't destroy the out obj every time, but keep it
	                        om.destroyObject(OUT_OBJECT_CLA, OUT_OBJECT_ID, true);
	                        // Create object with 2 more bytes for DataChunk Size field
	                        short dst_base = om.createObject(OUT_OBJECT_CLA, OUT_OBJECT_ID,
	                                (short) (size + 2),
	                                getCurrentACL(), (short) 0);
	                        if (dst_base == MemoryManager.NULL_OFFSET) {
	                            ISOException.throwIt(SW_NO_MEMORY_LEFT);
	                        }
	                        mem.setShort(dst_base, size);
	                        if (op == OP_PROCESS) {
	                            ciph.update(src_buff, (short) (src_base + 2),
	                                    size, mem.getBuffer(), (short) (dst_base + 2));
	                        } else  {
	                            ciph.doFinal(src_buff, (short) (src_base + 2),
	                                    size, mem.getBuffer(), (short) (dst_base + 2));
	                        }
	                        if (data_location == DL_APDU) {
	                            // Also copies the Short size information
	                            Util.arrayCopyNonAtomic(mem.getBuffer(), dst_base,
	                                    buffer, (short) 0, (short) (size + 2));
	                            om.destroyObject(OUT_OBJECT_CLA, OUT_OBJECT_ID, true);
	                            sendData(apdu, buffer, (short) 0, (short) (size + 2));
	                        }
	                        break;
	                    default:
	                        // Internal error because it should have been checked on INIT
	                        ISOException.throwIt(SW_INTERNAL_ERROR);
	                }
	                break;
	            default:
	                ISOException.throwIt(SW_INCORRECT_P2);
	        } 
	***** END ORIGINAL [IDENTIFY_BLOCKS_9] *****/ 
	/***** BEGIN REPLACE [IDENTIFY_BLOCKS_9] *****/ 
	BLOCK_9: { switch (op) {
	            case OP_INIT:
	                if (bytesLeft < 3) {
	                    ISOException.throwIt(SW_INVALID_PARAMETER);
	                }
	                byte ciph_mode = buffer[dataOffset];
	                ciph_dir = buffer[(short) (dataOffset + 1)];
	                byte ciph_alg_id = 0;
	                data_location = buffer[(short) (dataOffset + 2)];
	                switch (data_location) {
	                    case DL_APDU:
	                        src_buff = buffer;
	                        src_base = (short) (dataOffset + 3);
	                        src_avail = (short) (bytesLeft - 3);
	                        break ;
	                    case DL_OBJECT:
	                        src_buff = mem.getBuffer();
	                        src_base = om.getBaseAddress(IN_OBJECT_CLA, IN_OBJECT_ID);
	                        if (src_base == mem.NULL_OFFSET) {
	                            ISOException.throwIt(SW_OBJECT_NOT_FOUND);
	                        }
	                        src_avail = om.getSizeFromAddress(src_base);
	                        break ;
	                    default:
	                        ISOException.throwIt(SW_INVALID_PARAMETER);
	                        return; // Compiler warning
	                }
	                if (src_avail < 2) {
	                    ISOException.throwIt(SW_INVALID_PARAMETER);
	                }
	                size = Util.getShort(src_buff, src_base);
	                if (src_avail < (short) (2 + size)) {
	                    ISOException.throwIt(SW_INVALID_PARAMETER);
	                }
	                switch (ciph_dir) {
	                    case CD_SIGN:
	                    case CD_VERIFY:
	                        switch (key.getType()) {
	                            case KeyBuilder.TYPE_RSA_PUBLIC:
	                            case KeyBuilder.TYPE_RSA_PRIVATE:
	                                ciph_alg_id = Signature.ALG_RSA_MD5_PKCS1;
	                                ISOException.throwIt(SW_UNSUPPORTED_FEATURE);
	                                break ;
	                            case KeyBuilder.TYPE_DSA_PUBLIC:
	                            case KeyBuilder.TYPE_DSA_PRIVATE:
	                                ISOException.throwIt(SW_UNSUPPORTED_FEATURE);
	                                return;
	                            default:
	                                // DSA Encryption/Decryption is not supported by JavaCard !!
	                                ISOException.throwIt(SW_INCORRECT_ALG);
	                                return; // Compiler warning (ciph_alg_id)
	                        }
	                        Signature sign = getSignature(key_nb, ciph_alg_id);
	/***** BEGIN ORIGINAL [IF_SWITCH_37] *****
		                        if (size == (short) 0) {
		                            sign.init(key, (ciph_dir == CD_SIGN) ? Signature.MODE_SIGN : Signature.MODE_VERIFY);
		                        } else {
		                            sign.init(key, (ciph_dir == CD_SIGN) ? Signature.MODE_SIGN : Signature.MODE_VERIFY,
		                                    src_buff, (short) (src_base + 2), size);
		                        } 
	***** END ORIGINAL [IF_SWITCH_37] *****/ 
	/***** BEGIN REPLACE [IF_SWITCH_37] *****/ 
	IF_SWITCH_37: {
		switch (__getRandomBit()){
			case -1: 
				ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
			case 0:
				if ((size == (short) 0)) {
					{
						                            sign.init(key, (ciph_dir == CD_SIGN) ? Signature.MODE_SIGN : Signature.MODE_VERIFY);
						                        }
				} else {
					{
						                            sign.init(key, (ciph_dir == CD_SIGN) ? Signature.MODE_SIGN : Signature.MODE_VERIFY,
						                                    src_buff, (short) (src_base + 2), size);
						                        }
				}
	break IF_SWITCH_37;
			case 1:
				if (!(size == (short) 0)) {
					{
						                            sign.init(key, (ciph_dir == CD_SIGN) ? Signature.MODE_SIGN : Signature.MODE_VERIFY,
						                                    src_buff, (short) (src_base + 2), size);
						                        }
				} else {
					{
						                            sign.init(key, (ciph_dir == CD_SIGN) ? Signature.MODE_SIGN : Signature.MODE_VERIFY);
						                        }
				}
	break IF_SWITCH_37;
			default:
				ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
		}
	}
	/***** END REPLACE [IF_SWITCH_37] *****/ 
	                        ciph_dirs[key_nb] = ciph_dir;
	                        break ;
	                    case CD_ENCRYPT:
	                    case CD_DECRYPT:
	                        switch (key.getType()) {
	                            case KeyBuilder.TYPE_RSA_PUBLIC:
	                            case KeyBuilder.TYPE_RSA_PRIVATE:
	                            case KeyBuilder.TYPE_RSA_CRT_PRIVATE:
	/***** BEGIN ORIGINAL [IF_SWITCH_38] *****
		                                if (ciph_mode == CM_RSA_NOPAD) {
		                                    ciph_alg_id = Cipher.ALG_RSA_NOPAD;
		                                } else if (ciph_mode == CM_RSA_PAD_PKCS1) {
		                                    ciph_alg_id = Cipher.ALG_RSA_PKCS1;
		                                } else {
		                                    ISOException.throwIt(SW_INVALID_PARAMETER);
		                                    return;
		                                } 
	***** END ORIGINAL [IF_SWITCH_38] *****/ 
	/***** BEGIN REPLACE [IF_SWITCH_38] *****/ 
	IF_SWITCH_38: {
		switch (__getRandomBit()){
			case -1: 
				ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
			case 0:
				if ((ciph_mode == CM_RSA_NOPAD)) {
					{
						                                    ciph_alg_id = Cipher.ALG_RSA_NOPAD;
						                                }
				} else {
					if (ciph_mode == CM_RSA_PAD_PKCS1) {
						                                    ciph_alg_id = Cipher.ALG_RSA_PKCS1;
						                                } else {
						                                    ISOException.throwIt(SW_INVALID_PARAMETER);
						                                    return;
						                                }
				}
	break IF_SWITCH_38;
			case 1:
				if (!(ciph_mode == CM_RSA_NOPAD)) {
					if (ciph_mode == CM_RSA_PAD_PKCS1) {
						                                    ciph_alg_id = Cipher.ALG_RSA_PKCS1;
						                                } else {
						                                    ISOException.throwIt(SW_INVALID_PARAMETER);
						                                    return;
						                                }
				} else {
					{
						                                    ciph_alg_id = Cipher.ALG_RSA_NOPAD;
						                                }
				}
	break IF_SWITCH_38;
			default:
				ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
		}
	}
	/***** END REPLACE [IF_SWITCH_38] *****/ 
	                                break ;
	                            case KeyBuilder.TYPE_DES:
	/***** BEGIN ORIGINAL [IF_SWITCH_40] *****
		                                if (ciph_mode == CM_DES_CBC_NOPAD) {
		                                    ciph_alg_id = Cipher.ALG_DES_CBC_NOPAD;
		                                } else if (ciph_mode == CM_DES_ECB_NOPAD) {
		                                    ciph_alg_id = Cipher.ALG_DES_ECB_NOPAD;
		                                } else {
		                                    ISOException.throwIt(SW_INVALID_PARAMETER);
		                                    return;
		                                } 
	***** END ORIGINAL [IF_SWITCH_40] *****/ 
	/***** BEGIN REPLACE [IF_SWITCH_40] *****/ 
	IF_SWITCH_40: {
		switch (__getRandomBit()){
			case -1: 
				ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
			case 0:
				if ((ciph_mode == CM_DES_CBC_NOPAD)) {
					{
						                                    ciph_alg_id = Cipher.ALG_DES_CBC_NOPAD;
						                                }
				} else {
					if (ciph_mode == CM_DES_ECB_NOPAD) {
						                                    ciph_alg_id = Cipher.ALG_DES_ECB_NOPAD;
						                                } else {
						                                    ISOException.throwIt(SW_INVALID_PARAMETER);
						                                    return;
						                                }
				}
	break IF_SWITCH_40;
			case 1:
				if (!(ciph_mode == CM_DES_CBC_NOPAD)) {
					if (ciph_mode == CM_DES_ECB_NOPAD) {
						                                    ciph_alg_id = Cipher.ALG_DES_ECB_NOPAD;
						                                } else {
						                                    ISOException.throwIt(SW_INVALID_PARAMETER);
						                                    return;
						                                }
				} else {
					{
						                                    ciph_alg_id = Cipher.ALG_DES_CBC_NOPAD;
						                                }
				}
	break IF_SWITCH_40;
			default:
				ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
		}
	}
	/***** END REPLACE [IF_SWITCH_40] *****/ 
	                                break ;
	                            case KeyBuilder.TYPE_DSA_PUBLIC:
	                            case KeyBuilder.TYPE_DSA_PRIVATE:
	                                // DSA Encryption/Decryption is not supported by JavaCard !!
	                                ISOException.throwIt(SW_INVALID_PARAMETER);
	                                return;
	                            default:
	                                ISOException.throwIt(SW_INTERNAL_ERROR);
	                                return; // Compiler warning (ciph_alg_id unset)
	                        }
	                        Cipher ciph = getCipher(key_nb, ciph_alg_id);
	/***** BEGIN ORIGINAL [IF_SWITCH_42] *****
		                        if (size == (short) 0) {
		                            ciph.init(key, (ciph_dir == CD_ENCRYPT) ? Cipher.MODE_ENCRYPT : Cipher.MODE_DECRYPT);
		                        } else {
		                            ciph.init(key, (ciph_dir == CD_ENCRYPT) ? Cipher.MODE_ENCRYPT : Cipher.MODE_DECRYPT,
		                                    src_buff, (short) (src_base + 2), size);
		                        } 
	***** END ORIGINAL [IF_SWITCH_42] *****/ 
	/***** BEGIN REPLACE [IF_SWITCH_42] *****/ 
	IF_SWITCH_42: {
		switch (__getRandomBit()){
			case -1: 
				ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
			case 0:
				if ((size == (short) 0)) {
					{
						                            ciph.init(key, (ciph_dir == CD_ENCRYPT) ? Cipher.MODE_ENCRYPT : Cipher.MODE_DECRYPT);
						                        }
				} else {
					{
						                            ciph.init(key, (ciph_dir == CD_ENCRYPT) ? Cipher.MODE_ENCRYPT : Cipher.MODE_DECRYPT,
						                                    src_buff, (short) (src_base + 2), size);
						                        }
				}
	break IF_SWITCH_42;
			case 1:
				if (!(size == (short) 0)) {
					{
						                            ciph.init(key, (ciph_dir == CD_ENCRYPT) ? Cipher.MODE_ENCRYPT : Cipher.MODE_DECRYPT,
						                                    src_buff, (short) (src_base + 2), size);
						                        }
				} else {
					{
						                            ciph.init(key, (ciph_dir == CD_ENCRYPT) ? Cipher.MODE_ENCRYPT : Cipher.MODE_DECRYPT);
						                        }
				}
	break IF_SWITCH_42;
			default:
				ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
		}
	}
	/***** END REPLACE [IF_SWITCH_42] *****/ 
	                        ciph_dirs[key_nb] = ciph_dir;
	                        break ;
	                    default:
	                        ISOException.throwIt(SW_INVALID_PARAMETER);
	                }
	                break BLOCK_9;
	            case OP_PROCESS:
	            case OP_FINALIZE:
	                ciph_dir = ciph_dirs[key_nb];
	                switch (ciph_dir) {
	                    case CD_SIGN:
	                    case CD_VERIFY:
	                        Signature sign = signatures[key_nb];
	                        if (sign == null) /* Don't know what is incorrect: just say incorrect parameters
	                         *  we guess it was specified a wrong key number */ {
	                            ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
	                        }
	                        data_location = buffer[dataOffset];
	                        switch (data_location) {
	                            case DL_APDU:
	                                src_buff = mem.getBuffer();
	                                // Skip Data Location byte.
	                                src_base = (short) (dataOffset + 1);
	                                src_avail = (short) (bytesLeft - 1);
	                                break ;
	                            case DL_OBJECT:
	                                src_buff = mem.getBuffer();
	                                src_base = om.getBaseAddress(IN_OBJECT_CLA, IN_OBJECT_ID);
	                                if (src_base == MemoryManager.NULL_OFFSET) {
	                                    ISOException.throwIt(SW_OBJECT_NOT_FOUND);
	                                }
	                                src_avail = om.getSizeFromAddress(src_base);
	                                break ;
	                            default:
	                                ISOException.throwIt(SW_INVALID_PARAMETER);
	                                return;
	                        }
	                        if (src_avail < 2) {
	                            ISOException.throwIt(SW_INVALID_PARAMETER);
	                        }
	                        size = Util.getShort(src_buff, src_base);
	                        // IO objects are allowed to be larger than size of contained data
	                        if (src_avail < (short) (2 + size)) {
	                            ISOException.throwIt(SW_INVALID_PARAMETER);
	                        }
	/***** BEGIN ORIGINAL [IF_SWITCH_47] *****
		                        if (op == OP_PROCESS) {
		                            sign.update(src_buff, (short) (src_base + 2), size);
		                        } else {
		                            // OP_FINALIZE
		                            if (ciph_dir == CD_SIGN) {
		                                om.destroyObject(OUT_OBJECT_CLA, OUT_OBJECT_ID, true);
		                                short dst_base = om.createObject(OUT_OBJECT_CLA, OUT_OBJECT_ID,
		                                        (short) (sign.getLength() + 2),
		                                        getCurrentACL(), (short) 0);
		                                if (dst_base == MemoryManager.NULL_OFFSET) {
		                                    ISOException.throwIt(SW_NO_MEMORY_LEFT);
		                                }
		                                short sign_size = sign.sign(src_buff, (short) (src_base + 2),
		                                        size, mem.getBuffer(), (short) (dst_base + 2));
		                                if (sign_size > sign.getLength()) // We got a buffer overflow (unless we were in memory end and got an exception...)
		                                {
		                                    ISOException.throwIt(SW_INTERNAL_ERROR);
		                                }
		                                mem.setShort(dst_base, sign_size);
		                                // Actually send data back (and clear output buffer) only if location is APDU
		                                if (data_location == DL_APDU) {
		                                    sendData(apdu, mem.getBuffer(), dst_base, (short) (sign_size + 2));
		                                    om.destroyObject(OUT_OBJECT_CLA, OUT_OBJECT_ID, true);
		                                }
		                            } else { // ciph_dir == CD_VERIFY
		                                if (src_avail < (short) (2 + size + 2)) {
		                                    ISOException.throwIt(SW_INVALID_PARAMETER);
		                                }
		                                short sign_size = Util.getShort(src_buff, (short) (src_base + 2 + size));
		                                if (src_avail < (short) (2 + size + 2 + sign_size)) {
		                                    ISOException.throwIt(SW_INVALID_PARAMETER);
		                                }
		                                if (sign_size != sign.getLength()) {
		                                    ISOException.throwIt(SW_INVALID_PARAMETER);
		                                }
		                                if (!sign.verify(src_buff, (short) (src_base + 2), size,
		                                        src_buff, (short) (src_base + 2 + size + 2), sign_size)) {
		                                    ISOException.throwIt(SW_SIGNATURE_INVALID);
		                                }
		                            }
		                        } 
	***** END ORIGINAL [IF_SWITCH_47] *****/ 
	/***** BEGIN REPLACE [IF_SWITCH_47] *****/ 
	IF_SWITCH_47: {
		switch (__getRandomBit()){
			case -1: 
				ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
			case 0:
				if ((op == OP_PROCESS)) {
					{
						                            sign.update(src_buff, (short) (src_base + 2), size);
						                        }
				} else {
					{
						                            // OP_FINALIZE
						                            if (ciph_dir == CD_SIGN) {
						                                om.destroyObject(OUT_OBJECT_CLA, OUT_OBJECT_ID, true);
						                                short dst_base = om.createObject(OUT_OBJECT_CLA, OUT_OBJECT_ID,
						                                        (short) (sign.getLength() + 2),
						                                        getCurrentACL(), (short) 0);
						                                if (dst_base == MemoryManager.NULL_OFFSET) {
						                                    ISOException.throwIt(SW_NO_MEMORY_LEFT);
						                                }
						                                short sign_size = sign.sign(src_buff, (short) (src_base + 2),
						                                        size, mem.getBuffer(), (short) (dst_base + 2));
						                                if (sign_size > sign.getLength()) // We got a buffer overflow (unless we were in memory end and got an exception...)
						                                {
						                                    ISOException.throwIt(SW_INTERNAL_ERROR);
						                                }
						                                mem.setShort(dst_base, sign_size);
						                                // Actually send data back (and clear output buffer) only if location is APDU
						                                if (data_location == DL_APDU) {
						                                    sendData(apdu, mem.getBuffer(), dst_base, (short) (sign_size + 2));
						                                    om.destroyObject(OUT_OBJECT_CLA, OUT_OBJECT_ID, true);
						                                }
						                            } else { // ciph_dir == CD_VERIFY
						                                if (src_avail < (short) (2 + size + 2)) {
						                                    ISOException.throwIt(SW_INVALID_PARAMETER);
						                                }
						                                short sign_size = Util.getShort(src_buff, (short) (src_base + 2 + size));
						                                if (src_avail < (short) (2 + size + 2 + sign_size)) {
						                                    ISOException.throwIt(SW_INVALID_PARAMETER);
						                                }
						                                if (sign_size != sign.getLength()) {
						                                    ISOException.throwIt(SW_INVALID_PARAMETER);
						                                }
						                                if (!sign.verify(src_buff, (short) (src_base + 2), size,
						                                        src_buff, (short) (src_base + 2 + size + 2), sign_size)) {
						                                    ISOException.throwIt(SW_SIGNATURE_INVALID);
						                                }
						                            }
						                        }
				}
	break IF_SWITCH_47;
			case 1:
				if (!(op == OP_PROCESS)) {
					{
						                            // OP_FINALIZE
						                            if (ciph_dir == CD_SIGN) {
						                                om.destroyObject(OUT_OBJECT_CLA, OUT_OBJECT_ID, true);
						                                short dst_base = om.createObject(OUT_OBJECT_CLA, OUT_OBJECT_ID,
						                                        (short) (sign.getLength() + 2),
						                                        getCurrentACL(), (short) 0);
						                                if (dst_base == MemoryManager.NULL_OFFSET) {
						                                    ISOException.throwIt(SW_NO_MEMORY_LEFT);
						                                }
						                                short sign_size = sign.sign(src_buff, (short) (src_base + 2),
						                                        size, mem.getBuffer(), (short) (dst_base + 2));
						                                if (sign_size > sign.getLength()) // We got a buffer overflow (unless we were in memory end and got an exception...)
						                                {
						                                    ISOException.throwIt(SW_INTERNAL_ERROR);
						                                }
						                                mem.setShort(dst_base, sign_size);
						                                // Actually send data back (and clear output buffer) only if location is APDU
						                                if (data_location == DL_APDU) {
						                                    sendData(apdu, mem.getBuffer(), dst_base, (short) (sign_size + 2));
						                                    om.destroyObject(OUT_OBJECT_CLA, OUT_OBJECT_ID, true);
						                                }
						                            } else { // ciph_dir == CD_VERIFY
						                                if (src_avail < (short) (2 + size + 2)) {
						                                    ISOException.throwIt(SW_INVALID_PARAMETER);
						                                }
						                                short sign_size = Util.getShort(src_buff, (short) (src_base + 2 + size));
						                                if (src_avail < (short) (2 + size + 2 + sign_size)) {
						                                    ISOException.throwIt(SW_INVALID_PARAMETER);
						                                }
						                                if (sign_size != sign.getLength()) {
						                                    ISOException.throwIt(SW_INVALID_PARAMETER);
						                                }
						                                if (!sign.verify(src_buff, (short) (src_base + 2), size,
						                                        src_buff, (short) (src_base + 2 + size + 2), sign_size)) {
						                                    ISOException.throwIt(SW_SIGNATURE_INVALID);
						                                }
						                            }
						                        }
				} else {
					{
						                            sign.update(src_buff, (short) (src_base + 2), size);
						                        }
				}
	break IF_SWITCH_47;
			default:
				ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
		}
	}
	/***** END REPLACE [IF_SWITCH_47] *****/ 
	                        break ;
	                    case CD_ENCRYPT:
	                    case CD_DECRYPT:
	                        Cipher ciph = ciphers[key_nb];
	                        if (ciph == null) /* Don't know what is incorrect: just say incorrect parameters
	                         *  we guess it was specified a wrong key number */ {
	                            ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
	                        }
	                        data_location = buffer[dataOffset];
	                        switch (data_location) {
	                            case DL_APDU:
	                                src_buff = buffer;
	                                src_base = (short) (dataOffset + 1);
	                                src_avail = (short) (bytesLeft - 1);
	                                break ;
	                            case DL_OBJECT:
	                                src_buff = mem.getBuffer();
	                                src_base = om.getBaseAddress(IN_OBJECT_CLA, IN_OBJECT_ID);
	                                if (src_base == MemoryManager.NULL_OFFSET) {
	                                    ISOException.throwIt(SW_OBJECT_NOT_FOUND);
	                                }
	                                src_avail = om.getSizeFromAddress(src_base);
	                                break ;
	                            default:
	                                ISOException.throwIt(SW_INVALID_PARAMETER);
	                                return;
	                        }
	                        if (src_avail < 2) {
	                            ISOException.throwIt(SW_INVALID_PARAMETER);
	                        }
	                        size = Util.getShort(src_buff, src_base);
	                        if (src_avail < (short) (2 + size)) {
	                            ISOException.throwIt(SW_INVALID_PARAMETER);
	                        }
	                        // TODO: Don't destroy the out obj every time, but keep it
	                        om.destroyObject(OUT_OBJECT_CLA, OUT_OBJECT_ID, true);
	                        // Create object with 2 more bytes for DataChunk Size field
	                        short dst_base = om.createObject(OUT_OBJECT_CLA, OUT_OBJECT_ID,
	                                (short) (size + 2),
	                                getCurrentACL(), (short) 0);
	                        if (dst_base == MemoryManager.NULL_OFFSET) {
	                            ISOException.throwIt(SW_NO_MEMORY_LEFT);
	                        }
	                        mem.setShort(dst_base, size);
	/***** BEGIN ORIGINAL [IF_SWITCH_61] *****
		                        if (op == OP_PROCESS) {
		                            ciph.update(src_buff, (short) (src_base + 2),
		                                    size, mem.getBuffer(), (short) (dst_base + 2));
		                        } else  {
		                            ciph.doFinal(src_buff, (short) (src_base + 2),
		                                    size, mem.getBuffer(), (short) (dst_base + 2));
		                        } 
	***** END ORIGINAL [IF_SWITCH_61] *****/ 
	/***** BEGIN REPLACE [IF_SWITCH_61] *****/ 
	IF_SWITCH_61: {
		switch (__getRandomBit()){
			case -1: 
				ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
			case 0:
				if ((op == OP_PROCESS)) {
					{
						                            ciph.update(src_buff, (short) (src_base + 2),
						                                    size, mem.getBuffer(), (short) (dst_base + 2));
						                        }
				} else {
					{
						                            ciph.doFinal(src_buff, (short) (src_base + 2),
						                                    size, mem.getBuffer(), (short) (dst_base + 2));
						                        }
				}
	break IF_SWITCH_61;
			case 1:
				if (!(op == OP_PROCESS)) {
					{
						                            ciph.doFinal(src_buff, (short) (src_base + 2),
						                                    size, mem.getBuffer(), (short) (dst_base + 2));
						                        }
				} else {
					{
						                            ciph.update(src_buff, (short) (src_base + 2),
						                                    size, mem.getBuffer(), (short) (dst_base + 2));
						                        }
				}
	break IF_SWITCH_61;
			default:
				ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
		}
	}
	/***** END REPLACE [IF_SWITCH_61] *****/ 
	                        if (data_location == DL_APDU) {
	                            // Also copies the Short size information
	                            Util.arrayCopyNonAtomic(mem.getBuffer(), dst_base,
	                                    buffer, (short) 0, (short) (size + 2));
	                            om.destroyObject(OUT_OBJECT_CLA, OUT_OBJECT_ID, true);
	                            sendData(apdu, buffer, (short) 0, (short) (size + 2));
	                        }
	                        break ;
	                    default:
	                        // Internal error because it should have been checked on INIT
	                        ISOException.throwIt(SW_INTERNAL_ERROR);
	                }
	                break BLOCK_9;
	            default:
	                ISOException.throwIt(SW_INCORRECT_P2);
	        } }
	/***** END REPLACE [IDENTIFY_BLOCKS_9] *****/ 
    }

    private void GenerateKeyPair(APDU apdu, byte[] buffer) {
        short bytesLeft = Util.makeShort((byte) 0x00,
                buffer[ISO7816.OFFSET_LC]);
        if (bytesLeft != apdu.setIncomingAndReceive()) {
            ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
        }
        byte alg_id = buffer[OFFSET_GENKEY_ALG];
	/***** BEGIN ORIGINAL [IDENTIFY_BLOCKS_17] *****
	        switch (alg_id) {
	            case ALG_RSA:
	            case ALG_RSA_CRT:
	                GenerateKeyPairRSA(buffer);
	                break;
	            case ALG_DSA:
	                ISOException.throwIt(SW_UNSUPPORTED_FEATURE);
	                break;
	            
	            // JavaCard does not support on board
	            // symmetric key generation.
	            default:
	                ISOException.throwIt(SW_INCORRECT_ALG);
	        } 
	***** END ORIGINAL [IDENTIFY_BLOCKS_17] *****/ 
	/***** BEGIN REPLACE [IDENTIFY_BLOCKS_17] *****/ 
	BLOCK_17: { switch (alg_id) {
	            case ALG_RSA:
	            case ALG_RSA_CRT:
	                GenerateKeyPairRSA(buffer);
	                break BLOCK_17;
	            case ALG_DSA:
	                ISOException.throwIt(SW_UNSUPPORTED_FEATURE);
	                break BLOCK_17;
	            /*
	            case ALG_DES:
	            case ALG_3DES:
	            case ALG_3DES3:
	             */
	            // JavaCard does not support on board
	            // symmetric key generation.
	            default:
	                ISOException.throwIt(SW_INCORRECT_ALG);
	        } }
	/***** END REPLACE [IDENTIFY_BLOCKS_17] *****/ 
    }
    // Data has already been receive()ed

    private void GenerateKeyPairRSA(byte[] buffer) {
        byte prv_key_nb = buffer[ISO7816.OFFSET_P1];
        if ((prv_key_nb < 0) || (prv_key_nb >= MAX_NUM_KEYS)) {
            ISOException.throwIt(SW_INCORRECT_P1);
        }
        byte pub_key_nb = buffer[ISO7816.OFFSET_P2];
        if ((pub_key_nb < 0) || (pub_key_nb >= MAX_NUM_KEYS)) {
            ISOException.throwIt(SW_INCORRECT_P2);
        }
        if (pub_key_nb == prv_key_nb) {
            ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
        }
        byte alg_id = buffer[OFFSET_GENKEY_ALG];
        short key_size = Util.getShort(buffer, OFFSET_GENKEY_SIZE);
        byte options = buffer[OFFSET_GENKEY_OPTIONS];
        RSAPublicKey pub_key = (RSAPublicKey) getKey(pub_key_nb, KEY_RSA_PUBLIC, key_size);
        PrivateKey prv_key = (PrivateKey) getKey(
                prv_key_nb,
                alg_id == ALG_RSA ? KEY_RSA_PRIVATE : KEY_RSA_PRIVATE_CRT,
                key_size);
        /* If we're going to overwrite a keyPair's contents, check ACL */
        if (pub_key.isInitialized() && !authorizeKeyWrite(pub_key_nb)) {
            ISOException.throwIt(SW_UNAUTHORIZED);
        }
        if (prv_key.isInitialized() && !authorizeKeyWrite(prv_key_nb)) {
            ISOException.throwIt(SW_UNAUTHORIZED);
        }
        /* Store private key ACL */
        Util.arrayCopy(buffer, OFFSET_GENKEY_PRV_ACL, keyACLs,
                (short) (prv_key_nb * KEY_ACL_SIZE), KEY_ACL_SIZE);
        /* Store public key ACL */
        Util.arrayCopy(buffer, OFFSET_GENKEY_PUB_ACL, keyACLs,
                (short) (pub_key_nb * KEY_ACL_SIZE), KEY_ACL_SIZE);
	/***** BEGIN ORIGINAL [IDENTIFY_BLOCKS_18] *****
	        switch (options) {
	            case OPT_DEFAULT:
	                
	                if (pub_key.isInitialized()) {
	                    pub_key.clearKey();
	                }
	                break;
	            case OPT_RSA_PUB_EXP:
	                short exp_length =
	                        Util.getShort(buffer, OFFSET_GENKEY_RSA_PUB_EXP_LENGTH);
	                pub_key.setExponent(buffer,
	                        OFFSET_GENKEY_RSA_PUB_EXP_VALUE,
	                        exp_length);
	                break;
	            default:
	                ISOException.throwIt(SW_INVALID_PARAMETER);
	        } 
	***** END ORIGINAL [IDENTIFY_BLOCKS_18] *****/ 
	/***** BEGIN REPLACE [IDENTIFY_BLOCKS_18] *****/ 
	BLOCK_18: { switch (options) {
	            case OPT_DEFAULT:
	                /* As the default was specified, if public key already	*
	                 * exist we have to invalidate it, otherwise its parameters	*
	                 * would be used in place of the default ones		*/
	                if (pub_key.isInitialized()) {
	                    pub_key.clearKey();
	                }
	                break BLOCK_18;
	            case OPT_RSA_PUB_EXP:
	                short exp_length =
	                        Util.getShort(buffer, OFFSET_GENKEY_RSA_PUB_EXP_LENGTH);
	                pub_key.setExponent(buffer,
	                        OFFSET_GENKEY_RSA_PUB_EXP_VALUE,
	                        exp_length);
	                break BLOCK_18;
	            default:
	                ISOException.throwIt(SW_INVALID_PARAMETER);
	        } }
	/***** END REPLACE [IDENTIFY_BLOCKS_18] *****/ 
        /* TODO: Migrate checks on KeyPair on the top, so we
         *       avoid resource allocation on error conditions */
        /* If no keypair was previously used, ok.
         * If different keypairs were used, or for 1 key
         *   there is a keypair but the other key not, then error
         * If the same keypair object was used previously,
         *   check keypair size & type
         */
	/***** BEGIN ORIGINAL [IF_SWITCH_70] *****
	        if ((keyPairs[pub_key_nb] == null) && (keyPairs[prv_key_nb] == null)) {
	            keyPairs[pub_key_nb] = new KeyPair(pub_key, prv_key);
	            keyPairs[prv_key_nb] = keyPairs[pub_key_nb];
	        } else if (keyPairs[pub_key_nb] != keyPairs[prv_key_nb]) {
	            ISOException.throwIt(SW_OPERATION_NOT_ALLOWED);
	        } 
	***** END ORIGINAL [IF_SWITCH_70] *****/ 
	/***** BEGIN REPLACE [IF_SWITCH_70] *****/ 
	IF_SWITCH_70: {
		switch (__getRandomBit()){
			case -1: 
				ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
			case 0:
				if (((keyPairs[pub_key_nb] == null) && (keyPairs[prv_key_nb] == null))) {
					{
					            keyPairs[pub_key_nb] = new KeyPair(pub_key, prv_key);
					            keyPairs[prv_key_nb] = keyPairs[pub_key_nb];
					        }
				} else {
					if (keyPairs[pub_key_nb] != keyPairs[prv_key_nb]) {
					            ISOException.throwIt(SW_OPERATION_NOT_ALLOWED);
					        }
				}
	break IF_SWITCH_70;
			case 1:
				if (!((keyPairs[pub_key_nb] == null) && (keyPairs[prv_key_nb] == null))) {
					if (keyPairs[pub_key_nb] != keyPairs[prv_key_nb]) {
					            ISOException.throwIt(SW_OPERATION_NOT_ALLOWED);
					        }
				} else {
					{
					            keyPairs[pub_key_nb] = new KeyPair(pub_key, prv_key);
					            keyPairs[prv_key_nb] = keyPairs[pub_key_nb];
					        }
				}
	break IF_SWITCH_70;
			default:
				ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
		}
	}
	/***** END REPLACE [IF_SWITCH_70] *****/ 
        KeyPair kp = keyPairs[pub_key_nb];
        if ((kp.getPublic() != pub_key) || (kp.getPrivate() != prv_key)) // This should never happen according with this Applet policies
        {
            ISOException.throwIt(SW_INTERNAL_ERROR);
        }
        // We Rely on genKeyPair() to make all necessary checks about types
        kp.genKeyPair();
    }

    private void ImportKey(APDU apdu, byte[] buffer) {
        if (buffer[ISO7816.OFFSET_P2] != (byte) 0x00) {
            ISOException.throwIt(SW_INCORRECT_P2);
        }
        short bytesLeft = Util.makeShort((byte) 0x00,
                buffer[ISO7816.OFFSET_LC]);
        if (bytesLeft != apdu.setIncomingAndReceive()) {
            ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
        }
        byte key_nb = buffer[ISO7816.OFFSET_P1];
        if ((key_nb < 0) || (key_nb >= MAX_NUM_KEYS)) {
            ISOException.throwIt(SW_INCORRECT_P1);
        }
        /* If we're going to overwrite a key contents, check ACL */
        if ((keys[key_nb] != null)
                && keys[key_nb].isInitialized()
                && !authorizeKeyWrite(key_nb)) {
            ISOException.throwIt(SW_UNAUTHORIZED);
        }
        // Get memory base offset of the input buffer
        short base = om.getBaseAddress(IN_OBJECT_CLA, IN_OBJECT_ID);
        if (base == MemoryManager.NULL_OFFSET) {
            ISOException.throwIt(SW_OBJECT_NOT_FOUND);
        }
        short avail = om.getSizeFromAddress(base);
        /*** Start reading key blob ***/
        // Check entire blob header
        if (avail < 4) {
            ISOException.throwIt(SW_INVALID_PARAMETER);
        }
        // Check Blob Encoding
        if (mem.getByte(base) != BLOB_ENC_PLAIN) // TODO: Encrypted key blob ?
        {
            ISOException.throwIt(SW_UNSUPPORTED_FEATURE);
        }
        base++; // Skip Blob Encoding
        avail--;
        byte key_type = mem.getByte(base);
        base++; // Skip Key Type
        avail--;
        short key_size = mem.getShort(base);
        base += (short) 2; // Skip Key Size
        avail -= (short) 2;
        short size;
	/***** BEGIN ORIGINAL [IDENTIFY_BLOCKS_19] *****
	        switch (key_type) {
	            case KEY_RSA_PUBLIC:
	                RSAPublicKey rsa_pub_key =
	                        (RSAPublicKey) getKey(key_nb, key_type, key_size);
	                if (avail < 2) {
	                    ISOException.throwIt(SW_INVALID_PARAMETER);
	                }
	                size = mem.getShort(base);
	                base += (short) 2; // Skip Mod Size
	                avail -= (short) 2;
	                if (avail < (short) (size + 2)) {
	                    ISOException.throwIt(SW_INVALID_PARAMETER);
	                }
	                rsa_pub_key.setModulus(mem.getBuffer(), base, size);
	                base += size; // Skip Mod Value
	                avail -= size;
	                // avail already checked in previous if ()
	                size = mem.getShort(base);
	                base += (short) 2; // Skip Exp Size
	                avail -= (short) 2;
	                if (avail < size) {
	                    ISOException.throwIt(SW_INVALID_PARAMETER);
	                }
	                rsa_pub_key.setExponent(mem.getBuffer(), base, size);
	                base += size; // Skip Exp Value
	                avail -= size;
	                break;
	            case KEY_RSA_PRIVATE:
	                RSAPrivateKey rsa_prv_key =
	                        (RSAPrivateKey) getKey(key_nb, key_type, key_size);
	                if (avail < 2) {
	                    ISOException.throwIt(SW_INVALID_PARAMETER);
	                }
	                size = mem.getShort(base);
	                base += (short) 2; // Skip Mod Size
	                avail -= (short) 2;
	                if (avail < (short) (size + 2)) {
	                    ISOException.throwIt(SW_INVALID_PARAMETER);
	                }
	                rsa_prv_key.setModulus(mem.getBuffer(), base, size);
	                base += size; // Skip Mod Value
	                avail -= size;
	                // avail already checked in previous if ()
	                size = mem.getShort(base);
	                base += (short) 2; // Skip Exp Size
	                avail -= (short) 2;
	                if (avail < size) {
	                    ISOException.throwIt(SW_INVALID_PARAMETER);
	                }
	                rsa_prv_key.setExponent(mem.getBuffer(), base, size);
	                base += size; // Skip Exp Value
	                avail -= size;
	                break;
	            case KEY_RSA_PRIVATE_CRT:
	                RSAPrivateCrtKey rsa_prv_key_crt =
	                        (RSAPrivateCrtKey) getKey(key_nb, key_type, key_size);
	                if (avail < 2) {
	                    ISOException.throwIt(SW_INVALID_PARAMETER);
	                }
	                size = mem.getShort(base);
	                base += (short) 2; // Skip P Size
	                avail -= (short) 2;
	                if (avail < (short) (size + 2)) {
	                    ISOException.throwIt(SW_INVALID_PARAMETER);
	                }
	                rsa_prv_key_crt.setP(mem.getBuffer(), base, size);
	                base += size; // Skip P Value
	                avail -= size;
	                // avail ok...
	                size = mem.getShort(base);
	                base += (short) 2; // Skip Q Size
	                avail -= (short) 2;
	                if (avail < (short) (size + 2)) {
	                    ISOException.throwIt(SW_INVALID_PARAMETER);
	                }
	                rsa_prv_key_crt.setQ(mem.getBuffer(), base, size);
	                base += size; // Skip Q Value
	                avail -= size;
	                // avail ok...
	                size = mem.getShort(base);
	                base += (short) 2; // Skip PQ Size
	                avail -= (short) 2;
	                if (avail < (short) (size + 2)) {
	                    ISOException.throwIt(SW_INVALID_PARAMETER);
	                }
	                rsa_prv_key_crt.setPQ(mem.getBuffer(), base, size);
	                base += size; // Skip PQ Value
	                avail -= size;
	                // avail ok...
	                size = mem.getShort(base);
	                base += (short) 2; // Skip DP1 Size
	                avail -= (short) 2;
	                if (avail < (short) (size + 2)) {
	                    ISOException.throwIt(SW_INVALID_PARAMETER);
	                }
	                rsa_prv_key_crt.setDP1(mem.getBuffer(), base, size);
	                base += size; // Skip DP1 Value
	                avail -= size;
	                // avail ok...
	                size = mem.getShort(base);
	                base += (short) 2; // Skip DQ1 Size
	                avail -= (short) 2;
	                if (avail < size) {
	                    ISOException.throwIt(SW_INVALID_PARAMETER);
	                }
	                rsa_prv_key_crt.setDQ1(mem.getBuffer(), base, size);
	                base += size; // Skip DQ1 Value
	                avail -= size;
	                break;
	            case KEY_DSA_PRIVATE:
	            case KEY_DSA_PUBLIC:
	                ISOException.throwIt(SW_UNSUPPORTED_FEATURE);
	            case KEY_DES:
	                DESKey des_key = (DESKey) getKey(key_nb, key_type, key_size);
	                if (avail < 2) {
	                    ISOException.throwIt(SW_INVALID_PARAMETER);
	                }
	                size = mem.getShort(base);
	                base += (short) 2; // Skip Key Size
	                avail -= (short) 2;
	                if (avail < size) {
	                    ISOException.throwIt(SW_INVALID_PARAMETER);
	                }
	                des_key.setKey(mem.getBuffer(), base);
	                base += size; // Skip Key Value
	                avail -= size;
	                break;
	            case KEY_3DES:
	            case KEY_3DES3:
	                ISOException.throwIt(SW_UNSUPPORTED_FEATURE);
	            default:
	                ISOException.throwIt(SW_INCORRECT_ALG);
	        } 
	***** END ORIGINAL [IDENTIFY_BLOCKS_19] *****/ 
	/***** BEGIN REPLACE [IDENTIFY_BLOCKS_19] *****/ 
	BLOCK_19: { switch (key_type) {
	            case KEY_RSA_PUBLIC:
	                RSAPublicKey rsa_pub_key =
	                        (RSAPublicKey) getKey(key_nb, key_type, key_size);
	                if (avail < 2) {
	                    ISOException.throwIt(SW_INVALID_PARAMETER);
	                }
	                size = mem.getShort(base);
	                base += (short) 2; // Skip Mod Size
	                avail -= (short) 2;
	                if (avail < (short) (size + 2)) {
	                    ISOException.throwIt(SW_INVALID_PARAMETER);
	                }
	                rsa_pub_key.setModulus(mem.getBuffer(), base, size);
	                base += size; // Skip Mod Value
	                avail -= size;
	                // avail already checked in previous if ()
	                size = mem.getShort(base);
	                base += (short) 2; // Skip Exp Size
	                avail -= (short) 2;
	                if (avail < size) {
	                    ISOException.throwIt(SW_INVALID_PARAMETER);
	                }
	                rsa_pub_key.setExponent(mem.getBuffer(), base, size);
	                base += size; // Skip Exp Value
	                avail -= size;
	                break BLOCK_19;
	            case KEY_RSA_PRIVATE:
	                RSAPrivateKey rsa_prv_key =
	                        (RSAPrivateKey) getKey(key_nb, key_type, key_size);
	                if (avail < 2) {
	                    ISOException.throwIt(SW_INVALID_PARAMETER);
	                }
	                size = mem.getShort(base);
	                base += (short) 2; // Skip Mod Size
	                avail -= (short) 2;
	                if (avail < (short) (size + 2)) {
	                    ISOException.throwIt(SW_INVALID_PARAMETER);
	                }
	                rsa_prv_key.setModulus(mem.getBuffer(), base, size);
	                base += size; // Skip Mod Value
	                avail -= size;
	                // avail already checked in previous if ()
	                size = mem.getShort(base);
	                base += (short) 2; // Skip Exp Size
	                avail -= (short) 2;
	                if (avail < size) {
	                    ISOException.throwIt(SW_INVALID_PARAMETER);
	                }
	                rsa_prv_key.setExponent(mem.getBuffer(), base, size);
	                base += size; // Skip Exp Value
	                avail -= size;
	                break BLOCK_19;
	            case KEY_RSA_PRIVATE_CRT:
	                RSAPrivateCrtKey rsa_prv_key_crt =
	                        (RSAPrivateCrtKey) getKey(key_nb, key_type, key_size);
	                if (avail < 2) {
	                    ISOException.throwIt(SW_INVALID_PARAMETER);
	                }
	                size = mem.getShort(base);
	                base += (short) 2; // Skip P Size
	                avail -= (short) 2;
	                if (avail < (short) (size + 2)) {
	                    ISOException.throwIt(SW_INVALID_PARAMETER);
	                }
	                rsa_prv_key_crt.setP(mem.getBuffer(), base, size);
	                base += size; // Skip P Value
	                avail -= size;
	                // avail ok...
	                size = mem.getShort(base);
	                base += (short) 2; // Skip Q Size
	                avail -= (short) 2;
	                if (avail < (short) (size + 2)) {
	                    ISOException.throwIt(SW_INVALID_PARAMETER);
	                }
	                rsa_prv_key_crt.setQ(mem.getBuffer(), base, size);
	                base += size; // Skip Q Value
	                avail -= size;
	                // avail ok...
	                size = mem.getShort(base);
	                base += (short) 2; // Skip PQ Size
	                avail -= (short) 2;
	                if (avail < (short) (size + 2)) {
	                    ISOException.throwIt(SW_INVALID_PARAMETER);
	                }
	                rsa_prv_key_crt.setPQ(mem.getBuffer(), base, size);
	                base += size; // Skip PQ Value
	                avail -= size;
	                // avail ok...
	                size = mem.getShort(base);
	                base += (short) 2; // Skip DP1 Size
	                avail -= (short) 2;
	                if (avail < (short) (size + 2)) {
	                    ISOException.throwIt(SW_INVALID_PARAMETER);
	                }
	                rsa_prv_key_crt.setDP1(mem.getBuffer(), base, size);
	                base += size; // Skip DP1 Value
	                avail -= size;
	                // avail ok...
	                size = mem.getShort(base);
	                base += (short) 2; // Skip DQ1 Size
	                avail -= (short) 2;
	                if (avail < size) {
	                    ISOException.throwIt(SW_INVALID_PARAMETER);
	                }
	                rsa_prv_key_crt.setDQ1(mem.getBuffer(), base, size);
	                base += size; // Skip DQ1 Value
	                avail -= size;
	                break BLOCK_19;
	            case KEY_DSA_PRIVATE:
	            case KEY_DSA_PUBLIC:
	                ISOException.throwIt(SW_UNSUPPORTED_FEATURE);
	            case KEY_DES:
	                DESKey des_key = (DESKey) getKey(key_nb, key_type, key_size);
	                if (avail < 2) {
	                    ISOException.throwIt(SW_INVALID_PARAMETER);
	                }
	                size = mem.getShort(base);
	                base += (short) 2; // Skip Key Size
	                avail -= (short) 2;
	                if (avail < size) {
	                    ISOException.throwIt(SW_INVALID_PARAMETER);
	                }
	                des_key.setKey(mem.getBuffer(), base);
	                base += size; // Skip Key Value
	                avail -= size;
	                break BLOCK_19;
	            case KEY_3DES:
	            case KEY_3DES3:
	                ISOException.throwIt(SW_UNSUPPORTED_FEATURE);
	            default:
	                ISOException.throwIt(SW_INCORRECT_ALG);
	        } }
	/***** END REPLACE [IDENTIFY_BLOCKS_19] *****/ 
        // Zero and delete the import object
        om.destroyObject(IN_OBJECT_CLA, IN_OBJECT_ID, true);
    }

    private void ExportKey(APDU apdu, byte[] buffer) {
        if (buffer[ISO7816.OFFSET_P2] != (byte) 0x00) {
            ISOException.throwIt(SW_INCORRECT_P2);
        }
        short bytesLeft = Util.makeShort((byte) 0x00,
                buffer[ISO7816.OFFSET_LC]);
        if (bytesLeft != apdu.setIncomingAndReceive()) {
            ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
        }
        byte key_nb = buffer[ISO7816.OFFSET_P1];
        if ((key_nb < 0) || (key_nb >= MAX_NUM_KEYS)) {
            ISOException.throwIt(SW_INCORRECT_P1);
        }
        Key key = keys[key_nb];
        if ((key == null) || !key.isInitialized()) {
            ISOException.throwIt(SW_INCORRECT_P1);
        }
        // Enforce Access Control
        if (!authorizeKeyRead(key_nb)) {
            ISOException.throwIt(SW_UNAUTHORIZED);
        }
        // Destroy output object if already exists
        om.destroyObject(OUT_OBJECT_CLA, OUT_OBJECT_ID, true);
        // Automatically throws SW_NO_MEMORY_LEFT
        short base = om.createObjectMax(OUT_OBJECT_CLA, OUT_OBJECT_ID,
                getCurrentACL(), (short) 0);
        short buffer_size = om.getSizeFromAddress(base);
        short avail = buffer_size; /* Initially holds buffer size, after
         * is used to check buffer overflow	*/
        /*** Start reading key blob ***/
        // Check Blob Encoding
        if (buffer[ISO7816.OFFSET_CDATA] != BLOB_ENC_PLAIN) {
            ISOException.throwIt(SW_UNSUPPORTED_FEATURE);
        }
        // No need to check avail for all the key header
        if (avail < 4) {
            ThrowDeleteObjects(SW_NO_MEMORY_LEFT);
        }
        mem.setByte(base, BLOB_ENC_PLAIN);
        base++; // Skip Blob Encoding
        // avail advanced below
        byte key_type = key.getType();
        mem.setByte(base, getKeyType(key));
        base++;
        // avail advanced below
        short key_size = key.getSize();
        mem.setShort(base, key_size);
        base += (short) 2; // Skip Key Size
        // keeps into account all the key header
        avail -= (short) 4;
        short size;
        /* Maximum size of a BigNumber estimated to be equal to
         * the key size + 2 bytes for the bignum size itself.
         * TODO: Check if true for DSA		*/
        short bn_size = (short) (keys[key_nb].getSize() / 8 + 2);
	/***** BEGIN ORIGINAL [IDENTIFY_BLOCKS_20] *****
	        switch (key_type) {
	            case KeyBuilder.TYPE_RSA_PUBLIC:
	                RSAPublicKey pub_key = (RSAPublicKey) key;
	                if (avail < bn_size) {
	                    ThrowDeleteObjects(SW_NO_MEMORY_LEFT);
	                }
	                size = pub_key.getModulus(mem.getBuffer(), (short) (base + 2));
	                mem.setShort(base, size);
	                base += (short) (2 + size); // Skip Modulus Size & Value
	                avail -= (short) (2 + size);
	                if (avail < bn_size) {
	                    ThrowDeleteObjects(SW_NO_MEMORY_LEFT);
	                }
	                size = pub_key.getExponent(mem.getBuffer(), (short) (base + 2));
	                mem.setShort(base, size);
	                base += (short) (2 + size); // Skip Exponent Size & Value
	                avail -= (short) (2 + size);
	                break;
	            case KeyBuilder.TYPE_RSA_PRIVATE:
	                RSAPrivateKey prv_key = (RSAPrivateKey) key;
	                if (avail < bn_size) {
	                    ISOException.throwIt(SW_NO_MEMORY_LEFT);
	                }
	                size = prv_key.getModulus(mem.getBuffer(), (short) (base + 2));
	                mem.setShort(base, size);
	                base += (short) (2 + size); // Skip Modulus Size & Value
	                avail -= (short) (2 + size);
	                if (avail < bn_size) {
	                    ThrowDeleteObjects(SW_NO_MEMORY_LEFT);
	                }
	                size = prv_key.getExponent(mem.getBuffer(), (short) (base + 2));
	                mem.setShort(base, size);
	                base += (short) (2 + size); // Skip Exponent Size & Value
	                avail -= (short) (2 + size);
	                break;
	            case KeyBuilder.TYPE_RSA_CRT_PRIVATE:
	                RSAPrivateCrtKey prv_key_crt = (RSAPrivateCrtKey) key;
	                if (avail < bn_size) {
	                    ThrowDeleteObjects(SW_NO_MEMORY_LEFT);
	                }
	                size = prv_key_crt.getP(mem.getBuffer(), (short) (base + 2));
	                mem.setShort(base, size);
	                base += (short) (2 + size); // Skip P Size & Value
	                avail -= (short) (2 + size);
	                if (avail < bn_size) {
	                    ThrowDeleteObjects(SW_NO_MEMORY_LEFT);
	                }
	                size = prv_key_crt.getQ(mem.getBuffer(), (short) (base + 2));
	                mem.setShort(base, size);
	                base += (short) (2 + size); // Skip Q Size & Value
	                avail -= (short) (2 + size);
	                if (avail < bn_size) {
	                    ThrowDeleteObjects(SW_NO_MEMORY_LEFT);
	                }
	                size = prv_key_crt.getPQ(mem.getBuffer(), (short) (base + 2));
	                mem.setShort(base, size);
	                base += (short) (2 + size); // Skip PQ Size & Value
	                avail -= (short) (2 + size);
	                if (avail < bn_size) {
	                    ThrowDeleteObjects(SW_NO_MEMORY_LEFT);
	                }
	                size = prv_key_crt.getDP1(mem.getBuffer(), (short) (base + 2));
	                mem.setShort(base, size);
	                base += (short) (2 + size); // Skip DP1 Size & Value
	                avail -= (short) (2 + size);
	                if (avail < bn_size) {
	                    ThrowDeleteObjects(SW_NO_MEMORY_LEFT);
	                }
	                size = prv_key_crt.getDQ1(mem.getBuffer(), (short) (base + 2));
	                mem.setShort(base, size);
	                base += (short) (2 + size); // Skip DQ1 Size & Value
	                avail -= (short) (2 + size);
	                break;
	            case KeyBuilder.TYPE_DES:
	                DESKey des_key = (DESKey) key;
	                
	                if (avail < bn_size) {
	                    ThrowDeleteObjects(SW_NO_MEMORY_LEFT);
	                }
	                size = des_key.getKey(mem.getBuffer(), (short) (base + 2));
	                mem.setShort(base, size);
	                base += (short) (2 + size); // Skip P Size & Value
	                avail -= (short) (2 + size);
	                break;
	            
	            default:
	                ISOException.throwIt(SW_INVALID_PARAMETER);
	        } 
	***** END ORIGINAL [IDENTIFY_BLOCKS_20] *****/ 
	/***** BEGIN REPLACE [IDENTIFY_BLOCKS_20] *****/ 
	BLOCK_20: { switch (key_type) {
	            case KeyBuilder.TYPE_RSA_PUBLIC:
	                RSAPublicKey pub_key = (RSAPublicKey) key;
	                if (avail < bn_size) {
	                    ThrowDeleteObjects(SW_NO_MEMORY_LEFT);
	                }
	                size = pub_key.getModulus(mem.getBuffer(), (short) (base + 2));
	                mem.setShort(base, size);
	                base += (short) (2 + size); // Skip Modulus Size & Value
	                avail -= (short) (2 + size);
	                if (avail < bn_size) {
	                    ThrowDeleteObjects(SW_NO_MEMORY_LEFT);
	                }
	                size = pub_key.getExponent(mem.getBuffer(), (short) (base + 2));
	                mem.setShort(base, size);
	                base += (short) (2 + size); // Skip Exponent Size & Value
	                avail -= (short) (2 + size);
	                break BLOCK_20;
	            case KeyBuilder.TYPE_RSA_PRIVATE:
	                RSAPrivateKey prv_key = (RSAPrivateKey) key;
	                if (avail < bn_size) {
	                    ISOException.throwIt(SW_NO_MEMORY_LEFT);
	                }
	                size = prv_key.getModulus(mem.getBuffer(), (short) (base + 2));
	                mem.setShort(base, size);
	                base += (short) (2 + size); // Skip Modulus Size & Value
	                avail -= (short) (2 + size);
	                if (avail < bn_size) {
	                    ThrowDeleteObjects(SW_NO_MEMORY_LEFT);
	                }
	                size = prv_key.getExponent(mem.getBuffer(), (short) (base + 2));
	                mem.setShort(base, size);
	                base += (short) (2 + size); // Skip Exponent Size & Value
	                avail -= (short) (2 + size);
	                break BLOCK_20;
	            case KeyBuilder.TYPE_RSA_CRT_PRIVATE:
	                RSAPrivateCrtKey prv_key_crt = (RSAPrivateCrtKey) key;
	                if (avail < bn_size) {
	                    ThrowDeleteObjects(SW_NO_MEMORY_LEFT);
	                }
	                size = prv_key_crt.getP(mem.getBuffer(), (short) (base + 2));
	                mem.setShort(base, size);
	                base += (short) (2 + size); // Skip P Size & Value
	                avail -= (short) (2 + size);
	                if (avail < bn_size) {
	                    ThrowDeleteObjects(SW_NO_MEMORY_LEFT);
	                }
	                size = prv_key_crt.getQ(mem.getBuffer(), (short) (base + 2));
	                mem.setShort(base, size);
	                base += (short) (2 + size); // Skip Q Size & Value
	                avail -= (short) (2 + size);
	                if (avail < bn_size) {
	                    ThrowDeleteObjects(SW_NO_MEMORY_LEFT);
	                }
	                size = prv_key_crt.getPQ(mem.getBuffer(), (short) (base + 2));
	                mem.setShort(base, size);
	                base += (short) (2 + size); // Skip PQ Size & Value
	                avail -= (short) (2 + size);
	                if (avail < bn_size) {
	                    ThrowDeleteObjects(SW_NO_MEMORY_LEFT);
	                }
	                size = prv_key_crt.getDP1(mem.getBuffer(), (short) (base + 2));
	                mem.setShort(base, size);
	                base += (short) (2 + size); // Skip DP1 Size & Value
	                avail -= (short) (2 + size);
	                if (avail < bn_size) {
	                    ThrowDeleteObjects(SW_NO_MEMORY_LEFT);
	                }
	                size = prv_key_crt.getDQ1(mem.getBuffer(), (short) (base + 2));
	                mem.setShort(base, size);
	                base += (short) (2 + size); // Skip DQ1 Size & Value
	                avail -= (short) (2 + size);
	                break BLOCK_20;
	            case KeyBuilder.TYPE_DES:
	                DESKey des_key = (DESKey) key;
	                /* For a DES Key, bn_size contains the exact key length + 2 */
	                if (avail < bn_size) {
	                    ThrowDeleteObjects(SW_NO_MEMORY_LEFT);
	                }
	                size = des_key.getKey(mem.getBuffer(), (short) (base + 2));
	                mem.setShort(base, size);
	                base += (short) (2 + size); // Skip P Size & Value
	                avail -= (short) (2 + size);
	                break BLOCK_20;
	            /* Nothing here. If DSA is disabled, you cannot ever have DSA keys to export */
	            default:
	                ISOException.throwIt(SW_INVALID_PARAMETER);
	        } }
	/***** END REPLACE [IDENTIFY_BLOCKS_20] *****/ 
        // Eventually clamp buffer to make the export object the exact
        // size of the exported key blob
        om.clampObject(OUT_OBJECT_CLA, OUT_OBJECT_ID, (short) (buffer_size - avail));
    }

    private void CreatePIN(APDU apdu, byte[] buffer) {
        byte pin_nb = buffer[ISO7816.OFFSET_P1];
        byte num_tries = buffer[ISO7816.OFFSET_P2];
        /* Check that Identity n.0 is logged */
        if ((__get_byte_g(create_pin_ACL,(short)3) == (byte) 0xFF) || (((__get_short_g(logged_ids,(short)0) & __get_byte_g(create_pin_ACL,(short)3)) == (short) 0x0000)
                && (__get_byte_g(create_pin_ACL,(short)3) != (byte) 0x00))) {
            ISOException.throwIt(SW_UNAUTHORIZED);
        }
        if ((pin_nb < 0) || (pin_nb >= MAX_NUM_PINS)
                || (pins[pin_nb] != null)) {
            ISOException.throwIt(SW_INCORRECT_P1);
        }
        /* Allow pin lengths > 127 (useful at all ?) */
        short avail = Util.makeShort((byte) 0x00,
                buffer[ISO7816.OFFSET_LC]);
        if (apdu.setIncomingAndReceive() != avail) {
            ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
        }
        // At least 1 character for PIN and 1 for unblock code (+ lengths)
        if (avail < 4) {
            ISOException.throwIt(SW_INVALID_PARAMETER);
        }
        byte pin_size = buffer[ISO7816.OFFSET_CDATA];
        if (avail < (short) (1 + pin_size + 1)) {
            ISOException.throwIt(SW_INVALID_PARAMETER);
        }
        if (!CheckPINPolicy(buffer, (short) (ISO7816.OFFSET_CDATA + 1), pin_size)) {
            ISOException.throwIt(SW_INVALID_PARAMETER);
        }
        byte ucode_size = buffer[(short) (ISO7816.OFFSET_CDATA + 1 + pin_size)];
        if (avail != (short) (1 + pin_size + 1 + ucode_size)) {
            ISOException.throwIt(SW_INVALID_PARAMETER);
        }
        if (!CheckPINPolicy(buffer, (short) (ISO7816.OFFSET_CDATA + 1 + pin_size + 1), ucode_size)) {
            ISOException.throwIt(SW_INVALID_PARAMETER);
        }
        pins[pin_nb] = new OwnerPIN(num_tries, pinMaxSize);
        pins[pin_nb].update(buffer, (short) (ISO7816.OFFSET_CDATA + 1), pin_size);
        ublk_pins[pin_nb] = new OwnerPIN((byte) 3, pinMaxSize);
        // Recycle variable pin_size
        pin_size = (byte) (ISO7816.OFFSET_CDATA + 1 + pin_size + 1);
        ublk_pins[pin_nb].update(buffer, pin_size, ucode_size);
    }

    private void VerifyPIN(APDU apdu, byte[] buffer) {
        byte pin_nb = buffer[ISO7816.OFFSET_P1];
        if ((pin_nb < 0) || (pin_nb >= MAX_NUM_PINS)) {
            ISOException.throwIt(SW_INCORRECT_P1);
        }
        OwnerPIN pin = pins[pin_nb];
        if (pin == null) {
            ISOException.throwIt(SW_INCORRECT_P1);
        }
        if (buffer[ISO7816.OFFSET_P2] != 0x00) {
            ISOException.throwIt(SW_INCORRECT_P2);
        }
        short numBytes = Util.makeShort((byte) 0x00,
                buffer[ISO7816.OFFSET_LC]);
        /* Here I suppose the PIN code is small enough
        to enter in the buffer
        TODO: Verify the assumption and eventually
        adjust code to support reading PIN in
        multiple read()s */
        if (numBytes != apdu.setIncomingAndReceive()) {
            ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
        }
        if (!CheckPINPolicy(buffer, ISO7816.OFFSET_CDATA, (byte) numBytes)) {
            ISOException.throwIt(SW_INVALID_PARAMETER);
        }
        if (pin.getTriesRemaining() == (byte) 0x00) {
            ISOException.throwIt(SW_IDENTITY_BLOCKED);
        }
        if (!pin.check(buffer, (short) ISO7816.OFFSET_CDATA, (byte) numBytes)) {
            LogoutIdentity(pin_nb);
            ISOException.throwIt(SW_AUTH_FAILED);
        }
        // Actually register that PIN has been successfully verified.
        logged_ids=__set_short_g((short)(__get_short_g(logged_ids,(short)0)|(short) (0x0001 << pin_nb)),(short)0);
    }

    private void ChangePIN(APDU apdu, byte[] buffer) {
        /* Here I suppose the PIN code is small enough
        that 2 of them enter in the buffer
        TODO: Verify the assumption and eventually
        adjust code to support reading PINs in
        multiple read()s */
        byte pin_nb = buffer[ISO7816.OFFSET_P1];
        if ((pin_nb < 0) || (pin_nb >= MAX_NUM_PINS)) {
            ISOException.throwIt(SW_INCORRECT_P1);
        }
        OwnerPIN pin = pins[pin_nb];
        if (pin == null) {
            ISOException.throwIt(SW_INCORRECT_P1);
        }
        if (buffer[ISO7816.OFFSET_P2] != (byte) 0x00) {
            ISOException.throwIt(SW_INCORRECT_P2);
        }
        short avail = Util.makeShort((byte) 0x00,
                buffer[ISO7816.OFFSET_LC]);
        if (apdu.setIncomingAndReceive() != avail) {
            ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
        }
        // At least 1 charachter for each PIN code
        if (avail < 4) {
            ISOException.throwIt(SW_INVALID_PARAMETER);
        }
        byte pin_size = buffer[ISO7816.OFFSET_CDATA];
        if (avail < (short) (1 + pin_size + 1)) {
            ISOException.throwIt(SW_INVALID_PARAMETER);
        }
        if (!CheckPINPolicy(buffer, (short) (ISO7816.OFFSET_CDATA + 1), pin_size)) {
            ISOException.throwIt(SW_INVALID_PARAMETER);
        }
        byte new_pin_size = buffer[(short) (ISO7816.OFFSET_CDATA + 1 + pin_size)];
        if (avail < (short) (1 + pin_size + 1 + new_pin_size)) {
            ISOException.throwIt(SW_INVALID_PARAMETER);
        }
        if (!CheckPINPolicy(buffer, (short) (ISO7816.OFFSET_CDATA + 1 + pin_size + 1), new_pin_size)) {
            ISOException.throwIt(SW_INVALID_PARAMETER);
        }
        if (pin.getTriesRemaining() == (byte) 0x00) {
            ISOException.throwIt(SW_IDENTITY_BLOCKED);
        }
        if (!pin.check(buffer, (short) (ISO7816.OFFSET_CDATA + 1), pin_size)) {
            LogoutIdentity(pin_nb);
            ISOException.throwIt(SW_AUTH_FAILED);
        }
        pin.update(buffer, (short) (ISO7816.OFFSET_CDATA + 1 + pin_size + 1), new_pin_size);
        // JC specifies this resets the validated flag. So we do.
        logged_ids=__set_short_g((short)(__get_short_g(logged_ids,(short)0)&(short) ((short) 0xFFFF ^ (0x01 << pin_nb))),(short)0);
    }

    private void UnblockPIN(APDU apdu, byte[] buffer) {
        byte pin_nb = buffer[ISO7816.OFFSET_P1];
        if ((pin_nb < 0) || (pin_nb >= MAX_NUM_PINS)) {
            ISOException.throwIt(SW_INCORRECT_P1);
        }
        OwnerPIN pin = pins[pin_nb];
        OwnerPIN ublk_pin = ublk_pins[pin_nb];
        if (pin == null) {
            ISOException.throwIt(SW_INCORRECT_P1);
        }
        if (ublk_pin == null) {
            ISOException.throwIt(SW_INTERNAL_ERROR);
        }
        // If the PIN is not blocked, the call is inconsistent
        if (pin.getTriesRemaining() != 0) {
            ISOException.throwIt(SW_OPERATION_NOT_ALLOWED);
        }
        if (buffer[ISO7816.OFFSET_P2] != 0x00) {
            ISOException.throwIt(SW_INCORRECT_P2);
        }
        short numBytes = Util.makeShort((byte) 0x00,
                buffer[ISO7816.OFFSET_LC]);
        /* Here I suppose the PIN code is small enough
        to fit into the buffer
        TODO: Verify the assumption and eventually
        adjust code to support reading PIN in
        multiple read()s */
        if (numBytes != apdu.setIncomingAndReceive()) {
            ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
        }
        if (!CheckPINPolicy(buffer, ISO7816.OFFSET_CDATA, (byte) numBytes)) {
            ISOException.throwIt(SW_INVALID_PARAMETER);
        }
        if (!ublk_pin.check(buffer, ISO7816.OFFSET_CDATA, (byte) numBytes)) {
            ISOException.throwIt(SW_AUTH_FAILED);
        }
        pin.resetAndUnblock();
    }

    private void CreateObject(APDU apdu, byte[] buffer) {
        short bytesLeft = Util.makeShort((byte) 0x00,
                buffer[ISO7816.OFFSET_LC]);
        if (bytesLeft != apdu.setIncomingAndReceive()) {
            ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
        }
        if ((__get_byte_g(create_object_ACL,(short)1) == (byte) 0xFF) || (((__get_short_g(logged_ids,(short)0) & __get_byte_g(create_object_ACL,(short)1)) == (short) 0x0000)
                && (__get_byte_g(create_object_ACL,(short)1) != (byte) 0x00))) {
            ISOException.throwIt(SW_UNAUTHORIZED);
        }
        // ID + Size + ACL = 14 bytes
        if (bytesLeft != (short) (4 + 4 + ObjectManager.OBJ_ACL_SIZE)) {
            ISOException.throwIt(SW_INVALID_PARAMETER);
        }
        if (buffer[ISO7816.OFFSET_P1] != 0x00) {
            ISOException.throwIt(SW_INCORRECT_P1);
        }
        if (buffer[ISO7816.OFFSET_P2] != 0x00) {
            ISOException.throwIt(SW_INCORRECT_P2);
        }
        // Retrieve Object ID.
        short obj_class = Util.getShort(buffer, ISO7816.OFFSET_CDATA);
        short obj_id = Util.getShort(buffer, (short) (ISO7816.OFFSET_CDATA + (short) 2));
        // Check if object exists
        if (om.exists(obj_class, obj_id)) {
            ISOException.throwIt(SW_OBJECT_EXISTS);
        }
        // Check if object size in supported range: M.S.Word must be 0x0000 AND M.S.Bit of L.S.Word must be 0
        if ((Util.getShort(buffer, (short) (ISO7816.OFFSET_CDATA + 4)) != 0x0000)
                || (buffer[(short) (ISO7816.OFFSET_CDATA + 6)] < 0)) {
            ISOException.throwIt(SW_NO_MEMORY_LEFT);
        }
        // Check for zero size
        if (Util.getShort(buffer, (short) (ISO7816.OFFSET_CDATA + 6)) == 0x0000) {
            ISOException.throwIt(SW_INVALID_PARAMETER);
        }
        // Actually create object
        om.createObject(obj_class, obj_id,
                // Skip 2 M.S.Bytes of Size (only handle short sizes)
                Util.getShort(buffer, (short) (ISO7816.OFFSET_CDATA + 6)),
                buffer, (short) (ISO7816.OFFSET_CDATA + 8));
    }

    private void DeleteObject(APDU apdu, byte[] buffer) {
        if (buffer[ISO7816.OFFSET_P1] != (byte) 0x00) {
            ISOException.throwIt(SW_INCORRECT_P1);
        }
        if ((buffer[ISO7816.OFFSET_P2] != (byte) 0x00)
                && (buffer[ISO7816.OFFSET_P2] != (byte) 0x01)) {
            ISOException.throwIt(SW_INCORRECT_P2);
        }
        short bytesLeft = Util.makeShort((byte) 0x00,
                buffer[ISO7816.OFFSET_LC]);
        if (bytesLeft != apdu.setIncomingAndReceive()) {
            ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
        }
        if (bytesLeft != (short) 0x04) {
            ISOException.throwIt(SW_INVALID_PARAMETER);
        }
        short obj_class = Util.getShort(buffer, ISO7816.OFFSET_CDATA);
        short obj_id = Util.getShort(buffer, (short) (ISO7816.OFFSET_CDATA + (short) 2));
        // TODO: Here there are 2 object lookups. Optimize, please !
        //       (single destroy function with logged_ids param)
        short base = om.getBaseAddress(obj_class, obj_id);
        // Verify that object exists
        if (base == MemoryManager.NULL_OFFSET) {
            ISOException.throwIt(SW_OBJECT_NOT_FOUND);
        }
        // Enforce Access Control
        if (!om.authorizeDeleteFromAddress(base, __get_short_g(logged_ids,(short)0))) {
            ISOException.throwIt(SW_UNAUTHORIZED);
        }
        // Actually delete the object
        om.destroyObject(obj_class, obj_id,
                buffer[ISO7816.OFFSET_P2] == 0x01);
    }

    private void ReadObject(APDU apdu, byte[] buffer) {
        // Checking P1 & P2
        if (buffer[ISO7816.OFFSET_P1] != (byte) 0x00) {
            ISOException.throwIt(SW_INCORRECT_P1);
        }
        if (buffer[ISO7816.OFFSET_P2] != (byte) 0x00) {
            ISOException.throwIt(SW_INCORRECT_P2);
        }
        short bytesLeft = Util.makeShort((byte) 0x00,
                buffer[ISO7816.OFFSET_LC]);
        if (bytesLeft != apdu.setIncomingAndReceive()) {
            ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
        }
        if (bytesLeft != (short) 9) {
            ISOException.throwIt(SW_INVALID_PARAMETER);
        }
        short obj_class = Util.getShort(buffer, ISO7816.OFFSET_CDATA);
        short obj_id = Util.getShort(buffer, (short) (ISO7816.OFFSET_CDATA + (short) 2));
        // Skip 2 M.S.Bytes of the offset
        short offset = Util.getShort(buffer, (short) (ISO7816.OFFSET_CDATA + (short) 6));
        short size = Util.makeShort((byte) 0x00, buffer[(short) ISO7816.OFFSET_CDATA + (short) 8]);
        short base = om.getBaseAddress(obj_class, obj_id);
        // Verify that object exists
        if (base == MemoryManager.NULL_OFFSET) {
            ISOException.throwIt(SW_INVALID_PARAMETER);
        }
        // Enforce Access Control
        if (!om.authorizeReadFromAddress(base, __get_short_g(logged_ids,(short)0))) {
            ISOException.throwIt(SW_UNAUTHORIZED);
        }
        /* Additional checks: buffer overflow protection
         *   (prevents reading memory contents following the object)
         */
        if ((short) (offset + size) > om.getSizeFromAddress(base)) {
            ISOException.throwIt(SW_INVALID_PARAMETER);
        }
        // Sending data
        sendData(apdu, mem.getBuffer(), (short) (base + offset), size);
    }

    private void WriteObject(APDU apdu, byte[] buffer) {
        // Checking P1 & P2
        if (buffer[ISO7816.OFFSET_P1] != (byte) 0x00) {
            ISOException.throwIt(SW_INCORRECT_P1);
        }
        if (buffer[ISO7816.OFFSET_P2] != (byte) 0x00) {
            ISOException.throwIt(SW_INCORRECT_P2);
        }
        short bytesLeft = Util.makeShort((byte) 0x00,
                buffer[ISO7816.OFFSET_LC]);
        if (bytesLeft != apdu.setIncomingAndReceive()) {
            ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
        }
        short obj_class = Util.getShort(buffer, ISO7816.OFFSET_CDATA);
        short obj_id = Util.getShort(buffer, (short) (ISO7816.OFFSET_CDATA + 2));
        // Skip 2 M.S.Bytes of the offset
        short offset = Util.getShort(buffer, (short) (ISO7816.OFFSET_CDATA + 6));
        short size = Util.makeShort((byte) 0x00, buffer[(short) (ISO7816.OFFSET_CDATA + 8)]);
        short base = om.getBaseAddress(obj_class, obj_id);
        // Verify that object exists
        if (base == MemoryManager.NULL_OFFSET) {
            ISOException.throwIt(SW_INVALID_PARAMETER);
        }
        // Enforce Access Control
        if (!om.authorizeWriteFromAddress(base, __get_short_g(logged_ids,(short)0))) {
            ISOException.throwIt(SW_UNAUTHORIZED);
        }
        /* Additional checks: buffer overflow protection
         *   (prevents writing memory contents following the object)
         */
        if ((short) (offset + size) > om.getSizeFromAddress(base)) {
            ISOException.throwIt(SW_INVALID_PARAMETER);
        }
        // Update object data
        mem.setBytes(base, offset, buffer,
                (short) (ISO7816.OFFSET_CDATA + 9),
                size);
    }

    private void LogOutAll() {
        logged_ids=__set_short_g((short)((short) 0x0000),(short)0); // Nobody is logged in
        byte i;
        for (i = (byte) 0; i < MAX_NUM_PINS; i++) {
            if (pins[i] != null) {
                pins[i].reset();
            }
        }
    }

    private void ListPINs(APDU apdu, byte[] buffer) {
        // Checking P1 & P2
        if (buffer[ISO7816.OFFSET_P1] != (byte) 0x00) {
            ISOException.throwIt(SW_INCORRECT_P1);
        }
        if (buffer[ISO7816.OFFSET_P2] != (byte) 0x00) {
            ISOException.throwIt(SW_INCORRECT_P2);
        }
        byte expectedBytes =
                (byte) (buffer[ISO7816.OFFSET_LC]);
        if (expectedBytes != (short) 2) {
            ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
        }
        // Build the PIN bit mask
        short mask = (short) 0x00;
        short b;
        for (b = (short) 0; b < MAX_NUM_PINS; b++) {
            if (pins[b] != null) {
                mask |= (short) (((short) 0x01) << b);
            }
        }
        // Fill the buffer
        Util.setShort(buffer, (short) 0, mask);
        // Send response
        apdu.setOutgoingAndSend((short) 0, (short) 2);
    }

    private void ListObjects(APDU apdu, byte[] buffer) {
        // Checking P1 & P2
        if (buffer[ISO7816.OFFSET_P2] != (byte) 0x00) {
            ISOException.throwIt(SW_INCORRECT_P2);
        }
        byte expectedBytes =
                (byte) (buffer[ISO7816.OFFSET_LC]);
        if (expectedBytes < ObjectManager.RECORD_SIZE) {
            ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
        }
        boolean found = false; // Suppress compiler warning
	/***** BEGIN ORIGINAL [IF_SWITCH_179] *****
	        if (buffer[ISO7816.OFFSET_P1] == LIST_OPT_RESET) {
	            found = om.getFirstRecord(buffer, (short) 0);
	        } else if (buffer[ISO7816.OFFSET_P1] != LIST_OPT_NEXT) {
	            ISOException.throwIt(SW_INCORRECT_P1);
	        } else {
	            found = om.getNextRecord(buffer, (short) 0);
	        } 
	***** END ORIGINAL [IF_SWITCH_179] *****/ 
	/***** BEGIN REPLACE [IF_SWITCH_179] *****/ 
	IF_SWITCH_179: {
		switch (__getRandomBit()){
			case -1: 
				ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
			case 0:
				if ((buffer[ISO7816.OFFSET_P1] == LIST_OPT_RESET)) {
					{
					            found = om.getFirstRecord(buffer, (short) 0);
					        }
				} else {
					if (buffer[ISO7816.OFFSET_P1] != LIST_OPT_NEXT) {
					            ISOException.throwIt(SW_INCORRECT_P1);
					        } else {
					            found = om.getNextRecord(buffer, (short) 0);
					        }
				}
	break IF_SWITCH_179;
			case 1:
				if (!(buffer[ISO7816.OFFSET_P1] == LIST_OPT_RESET)) {
					if (buffer[ISO7816.OFFSET_P1] != LIST_OPT_NEXT) {
					            ISOException.throwIt(SW_INCORRECT_P1);
					        } else {
					            found = om.getNextRecord(buffer, (short) 0);
					        }
				} else {
					{
					            found = om.getFirstRecord(buffer, (short) 0);
					        }
				}
	break IF_SWITCH_179;
			default:
				ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
		}
	}
	/***** END REPLACE [IF_SWITCH_179] *****/ 
	/***** BEGIN ORIGINAL [IF_SWITCH_181] *****
	        if (found) {
	            apdu.setOutgoingAndSend((short) 0, (short) ObjectManager.RECORD_SIZE);
	        } else {
	            ISOException.throwIt(SW_SEQUENCE_END);
	        } 
	***** END ORIGINAL [IF_SWITCH_181] *****/ 
	/***** BEGIN REPLACE [IF_SWITCH_181] *****/ 
	IF_SWITCH_181: {
		switch (__getRandomBit()){
			case -1: 
				ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
			case 0:
				if ((found)) {
					{
					            apdu.setOutgoingAndSend((short) 0, (short) ObjectManager.RECORD_SIZE);
					        }
				} else {
					{
					            ISOException.throwIt(SW_SEQUENCE_END);
					        }
				}
	break IF_SWITCH_181;
			case 1:
				if (!(found)) {
					{
					            ISOException.throwIt(SW_SEQUENCE_END);
					        }
				} else {
					{
					            apdu.setOutgoingAndSend((short) 0, (short) ObjectManager.RECORD_SIZE);
					        }
				}
	break IF_SWITCH_181;
			default:
				ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
		}
	}
	/***** END REPLACE [IF_SWITCH_181] *****/ 
    }

    private void ListKeys(APDU apdu, byte[] buffer) {
        // Checking P2
        if (buffer[ISO7816.OFFSET_P2] != (byte) 0x00) {
            ISOException.throwIt(SW_INCORRECT_P2);
        }
        short expectedBytes = Util.makeShort((byte) 0x00,
                buffer[ISO7816.OFFSET_LC]);
        if (expectedBytes != (short) 0x0B) {
            ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
        }
	/***** BEGIN ORIGINAL [IF_SWITCH_184] *****
	        if (buffer[ISO7816.OFFSET_P1] == LIST_OPT_RESET) {
	            key_it=__set_byte_g((byte)((byte) 0),(short)0);
	        } else if (buffer[ISO7816.OFFSET_P1] != LIST_OPT_NEXT) {
	            ISOException.throwIt(SW_INCORRECT_P1);
	        } 
	***** END ORIGINAL [IF_SWITCH_184] *****/ 
	/***** BEGIN REPLACE [IF_SWITCH_184] *****/ 
	IF_SWITCH_184: {
		switch (__getRandomBit()){
			case -1: 
				ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
			case 0:
				if ((buffer[ISO7816.OFFSET_P1] == LIST_OPT_RESET)) {
					{
					            key_it=__set_byte_g((byte)((byte) 0),(short)0);
					        }
				} else {
					if (buffer[ISO7816.OFFSET_P1] != LIST_OPT_NEXT) {
					            ISOException.throwIt(SW_INCORRECT_P1);
					        }
				}
	break IF_SWITCH_184;
			case 1:
				if (!(buffer[ISO7816.OFFSET_P1] == LIST_OPT_RESET)) {
					if (buffer[ISO7816.OFFSET_P1] != LIST_OPT_NEXT) {
					            ISOException.throwIt(SW_INCORRECT_P1);
					        }
				} else {
					{
					            key_it=__set_byte_g((byte)((byte) 0),(short)0);
					        }
				}
	break IF_SWITCH_184;
			default:
				ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
		}
	}
	/***** END REPLACE [IF_SWITCH_184] *****/ 
        while ((__get_byte_g(key_it,(short)0) < MAX_NUM_KEYS)
                && ((keys[__get_byte_g(key_it,(short)0)] == null)
                || !keys[__get_byte_g(key_it,(short)0)].isInitialized())) {
            key_it=__set_byte_g((byte)(__get_byte_g(key_it,(short)0)+1),(short)0);
        }
        if (__get_byte_g(key_it,(short)0) < MAX_NUM_KEYS) {
            Key key = keys[__get_byte_g(key_it,(short)0)];
            buffer[(short) 0] = __get_byte_g(key_it,(short)0);
            buffer[(short) 1] = getKeyType(key);
            buffer[(short) 2] = (byte) 0xFF; // No partner information available
            Util.setShort(buffer, (short) 3, key.getSize());
            Util.arrayCopyNonAtomic(keyACLs, (short) (__get_byte_g(key_it,(short)0) * KEY_ACL_SIZE),
                    buffer, (short) 5, KEY_ACL_SIZE);
            // Advance iterator
            key_it=__set_byte_g((byte)(__get_byte_g(key_it,(short)0)+1),(short)0);
            apdu.setOutgoingAndSend((short) 0, (short) (5 + KEY_ACL_SIZE));
        }
    }

    private void GetChallenge(APDU apdu, byte[] buffer) {
        if (buffer[ISO7816.OFFSET_P1] != (byte) 0x00) {
            ISOException.throwIt(SW_INCORRECT_P1);
        }
        short bytesLeft = Util.makeShort((byte) 0x00,
                buffer[ISO7816.OFFSET_LC]);
        if (bytesLeft != apdu.setIncomingAndReceive()) {
            ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
        }
        if (bytesLeft < 4) {
            ISOException.throwIt(SW_INVALID_PARAMETER);
        }
        short size = Util.getShort(buffer, ISO7816.OFFSET_CDATA);
        short seed_size = Util.getShort(buffer, (short) (ISO7816.OFFSET_CDATA + 2));
        if (bytesLeft != (short) (seed_size + 4)) {
            ISOException.throwIt(SW_INVALID_PARAMETER);
        }
        byte data_loc = buffer[ISO7816.OFFSET_P2];
        if ((data_loc != DL_APDU) && (data_loc != DL_OBJECT)) {
            ISOException.throwIt(SW_INVALID_PARAMETER);
        }
        if (randomData == null) {
            randomData = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM);
        }
        if (seed_size != (short) 0x0000) {
            randomData.setSeed(buffer, (short) (ISO7816.OFFSET_CDATA + 4), seed_size);
        }
        // Allow size = 0 for only seeding purposes
        if (size != (short) 0x0000) {
            // Automatically throws exception if no memory
            short base = om.createObject(OUT_OBJECT_CLA, OUT_OBJECT_ID, (short) (size + 2), getRestrictedACL(), (short) 0);
            mem.setShort(base, size);
            randomData.generateData(mem.getBuffer(), (short) (base + 2), size);
            /* Remember that out object contains getChallenge data (to avoid attacks
             * pretending to write the out object before extAuth) */
            getChallengeDone=__set_boolean_g((boolean)(true),(short)0);
            // Actually return data in APDU only if DL_APDU specified.
            if (data_loc == DL_APDU) {
                sendData(apdu, mem.getBuffer(), base, (short) (size + 2));
                /* Don't destroy out object ! Generated data is needed in ExtAuth ! */
                /* Not if running without external authentication */
                om.destroyObject(OUT_OBJECT_CLA, OUT_OBJECT_ID, true);
            }
        }
    }

    private void GetStatus(APDU apdu, byte[] buffer) {
        if (buffer[ISO7816.OFFSET_P1] != (byte) 0x00) {
            ISOException.throwIt(SW_INCORRECT_P1);
        }
        if (buffer[ISO7816.OFFSET_P2] != (byte) 0x00) {
            ISOException.throwIt(SW_INCORRECT_P2);
        }
        short pos = (short) 0;
        buffer[pos++] = (byte) 1; // Major Card Edge Protocol version n.
        buffer[pos++] = (byte) 3; // Minor Card Edge Protocol version n.
        buffer[pos++] = (byte) 0; // Major Applet version n.
        buffer[pos++] = (byte) 9; // Minor Applet version n.
        Util.setShort(buffer, pos, (short) 0x00); // Total mem M.S.
        pos += (short) 2;
        Util.setShort(buffer, pos, (short) mem.getBuffer().length); // Total mem L.S.
        pos += (short) 2;
        Util.setShort(buffer, pos, (short) 0x00); // Free mem M.S.
        pos += (short) 2;
        Util.setShort(buffer, pos, mem.freemem()); // Free mem L.S.
        pos += (short) 2;
        byte cnt = (byte) 0;
        for (short i = 0; i < pins.length; i++) {
            if (pins[i] != null) {
                cnt++;
            }
        }
        buffer[pos++] = cnt; // Number of used PINs
        cnt = (byte) 0;
        for (short i = 0; i < keys.length; i++) {
            if (keys[i] != null) {
                cnt++;
            }
        }
        buffer[pos++] = cnt; // Number of used Keys
        Util.setShort(buffer, pos, __get_short_g(logged_ids,(short)0)); // Logged ids
        pos += (short) 2;
        apdu.setOutgoingAndSend((short) 0, pos);
    }
 
	/**
	 *	This method will compare negation of input value with stored negation. 
	 * 	In case of error an exception will be thrown. 
	 *@param value value, which is expected
	 *@param id id of variable in array of shadow copies
	 *@throw Exception if value is corrupted
	 */
	private short __get_short_g(short value, short id){
		if (fault_resistant_short_g[id] != (short)(value ^ (short)((1<<15)-1))) 
			ISOException.throwIt(ISO7816.SW_DATA_INVALID);
		return value;
	}	/**
	 *	This method will set value in help array to negation of original value.
	 *@param value value to set
	 *@param id id of variable in array of shadow copies
	 *@return value value, which was set
	 */
	private short __set_short_g(short value, short id){
		fault_resistant_short_g[id] = (short)(value ^ (short)((1<<15)-1));
		return value;
	} 
	/**
	 *	This method will compare negation of input value with stored negation. 
	 * 	In case of error an exception will be thrown. 
	 *@param value value, which is expected
	 *@param id id of variable in array of shadow copies
	 *@throw Exception if value is corrupted
	 */
	private boolean __get_boolean_g(boolean value, short id){
		if (fault_resistant_boolean_g[id] != (boolean)(!value)) 
			ISOException.throwIt(ISO7816.SW_DATA_INVALID);
		return value;
	}	/**
	 *	This method will set value in help array to negation of original value.
	 *@param value value to set
	 *@param id id of variable in array of shadow copies
	 *@return value value, which was set
	 */
	private boolean __set_boolean_g(boolean value, short id){
		fault_resistant_boolean_g[id] = (boolean)(!value);
		return value;
	} 
	/**
	 *	This method will compare negation of input value with stored negation. 
	 * 	In case of error an exception will be thrown. 
	 *@param value value, which is expected
	 *@param id id of variable in array of shadow copies
	 *@throw Exception if value is corrupted
	 */
	private byte __get_byte_g(byte value, short id){
		if (fault_resistant_byte_g[id] != (byte)(value ^ (byte)((1<<7)-1))) 
			ISOException.throwIt(ISO7816.SW_DATA_INVALID);
		return value;
	}	/**
	 *	This method will set value in help array to negation of original value.
	 *@param value value to set
	 *@param id id of variable in array of shadow copies
	 *@return value value, which was set
	 */
	private byte __set_byte_g(byte value, short id){
		fault_resistant_byte_g[id] = (byte)(value ^ (byte)((1<<7)-1));
		return value;
	}	/** Random number generator for IF_SWITCH transformation */
	private static RandomData __random = null;
	/** Helper array for random number generator */
	private static byte __randomData[] = null;
	/**
	 *	Returns random bit 0 or 1
	 */
	private static short __getRandomBit(){
		try {
			if (__random == null) __random = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM);
			if (__randomData == null) __randomData = JCSystem.makeTransientByteArray((short)1, JCSystem.CLEAR_ON_DESELECT);
			__random.generateData(__randomData, (short)0, (short)1);
			return (byte)((__randomData[0] & 0xFF)%2);
		} catch (CryptoException ex) {
			// JC simulator sometimes doesn't allow to get instance of RandomData.. 
			// this fallback will return always 0 in case that random number couldn't be fetched 
			// and program will continue normally
			return (byte)0;
		}
	}} // end of class JAVA_APPLET

