Program used for Java Card Applet Simulation



Be carefull, this program does not run properly with the JDK virtual machine!
Indeed, we suppose that there is no interleaving between threads (master/slave mode).
So the current executing thread stays executable until it becomes dead (no more instruction) or suspended (it has called the method suspend())

If you want to know more about our development environment for Java Card, please click here
/*********************************************************************
 *                         Root Class
 *********************************************************************/
public class Root {
   /*********************** Public Method **************************/
   public static void main(String[] args) {
      User user = new User();
      JCRE jcre = new JCRE();
      CAD cad = new CAD(jcre, user);

      jcre.setCAD(cad);
      user.setCAD(cad);
      user.resume();
   }
}
 
/*********************************************************************
 *                         User Class
 *********************************************************************/
class User extends Thread {
   private CAD cad;

   /*************************** Constructor *************************/
   protected User() {
      start();
      suspend();
   }

   /*********************** Public Methods *************************/
   public void run() {
      cad.installAppli(PurseFr.AID, 1000);
      cad.sendAction(PurseFr.AID, PurseFr.PURSE_FR_WITHDRAW, 100);
      cad.installAppli(FrequentFlyer.AID, 0);   
      cad.sendAction(FrequentFlyer.AID, FrequentFlyer.GAIN_MILES, 5000);
     // cad.sendAction(PurseFr.AID, PurseFr.PURSE_FR_DEPOSIT, 20);
   }

   /*********************** Protected Methods **********************/
   protected void setCAD(CAD cad1) {
      cad = cad1;
   }
}

/*********************************************************************
 *                         CAD Class
 *********************************************************************/
class CAD extends Thread {
   private static byte PROCESS = 1;
   private static byte SELECT = 2;

   private static byte NOTHING = 0;
   protected APDU apdu;

   private User user;
   private JCRE jcre;

   private byte currentAID;
   private byte ins;
   private byte AID;
   private int commandData;
   private byte action;

   /*************************** Constructor *************************/
   protected CAD(JCRE jcre1, User user1) {
      apdu = new APDU();
      jcre = jcre1;
      user = user1;
      currentAID = JCRE.NO_SELECTED_APPLET;
      start();
      suspend();
   }

   /*********************** Public Methods *************************/
   public void run() {
      while (true) {
         sendActionToCard();
         if (action == SELECT) {
            if (apdu.sw1 == JCRE.OK) {
               currentAID = AID;
               apdu.setCommandApdu(JCRE.PROCESS_CLA, ins, NOTHING, commandData);
               sendActionToCard();
            }
            else 
               currentAID = JCRE.NO_SELECTED_APPLET;
         }
         user.resume();
         suspend();
      }
   }

   public void installAppli(byte AID, int data) {
      apdu.setCommandApdu(NOTHING, NOTHING,NOTHING, data);
      if (AID == PurseFr.AID)
         jcre.install(PurseFr.install(apdu), PurseFr.AID);
      else
         jcre.install(FrequentFlyer.install(apdu), FrequentFlyer.AID);
   }

   public void sendAction(byte AID1, byte ins1, int data) {
      if ((currentAID == JCRE.NO_SELECTED_APPLET) || (currentAID != AID1)) {
         action = SELECT;
         ins = ins1;
         commandData = data;
         AID = AID1;
         apdu.setCommandApdu(JCRE.SELECT_CLA, JCRE.SELECT_INS, AID1, NOTHING);
      }
      else {
         action = PROCESS;
         apdu.setCommandApdu(JCRE.PROCESS_CLA, ins1, NOTHING, data);
      }
      resume();
      user.suspend();
   }

   /*********************** Private Methods ************************/
   private void sendActionToCard() {
      jcre.apdu.copyCommandApdu(apdu);
      jcre.resume();
      suspend(); // wait the card reply
   }
}

/*********************************************************************
 *                         JCRE Class
 *********************************************************************/
class JCRE extends Thread {
   protected final static byte SELECT_CLA = 0;
   protected final static byte PROCESS_CLA = 120;
   protected final static byte SELECT_INS = 82;
   /*//0xA4 ie -82*/
   protected final static byte NO_SELECTED_APPLET = 0;
   /*//Possible commands on the applet*/
   protected final static byte NO_CODE = 0;
   protected final static byte SELECT = 1;
   protected final static byte DESELECT = 2;
   protected final static byte PROCESS = 3;
   protected final static byte OK = 127;
   /*//sw1 = 0x90 = -127*/
   protected final static byte IMPOSSIBLE = 0;
   static JCRE self;
   /*//Applets on the Card*/
   private final static byte NB_MAX_APPLI_ON_CARD = 5;
   private int nbAppletsOnCard;
   private Applet appli1, appli2, appli3, appli4, appli5;
   private byte appli1AID, appli2AID, appli3AID, appli4AID, appli5AID;
   protected byte commandCode;
   private byte selectedAppliAID;
   private Applet selectedAppli;
   protected APDU apdu;
   CAD cad;


   /*************************** Constructor *************************/
   protected JCRE() {
      nbAppletsOnCard = 0;
      apdu = new APDU();
      commandCode = NO_CODE;
      selectedAppliAID = NO_SELECTED_APPLET;
      self = this;
      start();
      suspend();
   }

   /*********************** Public Methods *************************/
   public void run() {
      while (true) {
         /*// Select APDU*/
         if (apdu.cla == SELECT_CLA && apdu.ins == SELECT_INS) {
            if (selectedAppliAID != NO_SELECTED_APPLET) {
               sendCommandToAppletAndWaitResponse(DESELECT, selectedAppliAID);
               selectedAppliAID = NO_SELECTED_APPLET;
            }
            sendCommandToAppletAndWaitResponse(SELECT, apdu.p1);
            if (apdu.sw1 == OK)
               selectedAppliAID = apdu.p1;
         } else {
            /*// Process APDU*/
            if (selectedAppliAID != NO_SELECTED_APPLET)
               sendCommandToAppletAndWaitResponse(PROCESS, selectedAppliAID);
            else
               apdu.sw1 = IMPOSSIBLE;
         }
         cad.apdu.copyResponseApdu(apdu);
         cad.resume();
         /*Wait a CAD action
         */
         suspend();
      }
   }

   /*********************** Protected Methods **********************/
   protected void install(Applet appli, byte appletAID) {
      /*//A mettre un switch qd fait en sem*/
      if (nbAppletsOnCard < NB_MAX_APPLI_ON_CARD) {
         if (nbAppletsOnCard == 0) {
            appli1 = appli;
            appli1AID = appletAID;
         } else
            if (nbAppletsOnCard == 1) {
               appli2 = appli;
               appli2AID = appletAID;
            } else
               if (nbAppletsOnCard == 2) {
                  appli3 = appli;
                  appli3AID = appletAID;
               } else
                  if (nbAppletsOnCard == 3) {
                     appli4 = appli;
                     appli4AID = appletAID;
                  } else
                     if (nbAppletsOnCard == 4) {
                        appli5 = appli;
                        appli5AID = appletAID;
                     }
         nbAppletsOnCard = nbAppletsOnCard + 1;
         appli.start();
         appli.suspend();
      }
   }
   protected void setCAD(CAD cad1) {
      cad = cad1;
   }

   /*********************** Private Methods ************************/
   private void sendCommandToAppletAndWaitResponse(byte command, byte appletAID)
     {
      commandCode = command;
      if (command == SELECT) {
         selectedAppli = searchApplet(appletAID);
      }
      selectedAppli.resume();
      suspend();
   }
   private Applet searchApplet(byte AID) {
      if (AID == appli1AID)
         return appli1;
      else
         if (AID == appli2AID)
            return appli2;
         else
            if (AID == appli3AID)
               return appli3;
            else
               if (AID == appli4AID)
                  return appli4;
               else
                  /*//if (AID == appli5AID)*/
                  return appli5;
   }
}

/*********************************************************************
 *                         PurseFr Class
 *********************************************************************/
class PurseFr extends Applet {
   protected final static byte AID = 126;
   /*//Possible Instructions*/
   protected final static byte PURSE_FR_DEPOSIT = 1;
   protected final static byte PURSE_FR_WITHDRAW = 2;
   protected final static byte PURSE_FR_BALANCE_CHECK = 3;
   private final static int PURSE_FR_MAX = 3000;
   private int purseFrTotal;

   /*********************** Constructor ********************************/
   private PurseFr(APDU apdu) {
      purseFrTotal = apdu.commandData;
   }

   /*********************** Public Methods *****************************/
   /**
   * Performs only one time at the applet installation
   * on the smart card
   * A revoir car mauvais renvoi et peut envoyer une exception
   */
   public static PurseFr install(APDU apdu) {
      return new PurseFr(apdu);
   }
   /**
   * Performs the session ie deal with the APDUs sent by the CAD
   */
   public void process(APDU apdu) {
      if (apdu.ins == PURSE_FR_DEPOSIT)
         purseDeposit(apdu);
      else
         if (apdu.ins == PURSE_FR_WITHDRAW)
            purseWithdraw(apdu);
         else
            if (apdu.ins == PURSE_FR_BALANCE_CHECK)
               purseBalanceCheck(apdu);
   }

   /*********************** Private Methods ****************************/
   private void purseDeposit(APDU apdu) {
      int amountOfDeposit = apdu.commandData;
      int temporaryPurseFrTotal = amountOfDeposit + purseFrTotal;
      if (amountOfDeposit > 0 && temporaryPurseFrTotal <= PURSE_FR_MAX) {
         purseFrTotal = temporaryPurseFrTotal;
         apdu.setResponseApdu(purseFrTotal, JCRE.OK);
      } else
         apdu.setResponseApdu(purseFrTotal, JCRE.IMPOSSIBLE);
   }
   private void purseWithdraw(APDU apdu) {
      int amountOfWithdraw = apdu.commandData;
      int temporaryPurseFrTotal = purseFrTotal - amountOfWithdraw;
      if (amountOfWithdraw > 0 && temporaryPurseFrTotal >= 0) {
         purseFrTotal = temporaryPurseFrTotal;
         apdu.responseData = purseFrTotal;
      } else
         apdu.setResponseApdu(purseFrTotal, JCRE.IMPOSSIBLE);
   }
   private void purseBalanceCheck(APDU apdu) {
      apdu.setResponseApdu(purseFrTotal, JCRE.IMPOSSIBLE);
   }
}

/*********************************************************************
 *                         Frequent Flyer Class
 *********************************************************************/
class FrequentFlyer extends Applet {
   protected final static byte AID = 124;
   /*//Possible Instructions*/
   protected final static byte GAIN_MILES = 1;
   protected final static byte REDEEM_MILES = 2;
   protected final static byte CHECK_MILES = 3;
   private final static int MILES_MAX = 100000;
   private int milesTotal;

   /************************** Constructor ****************************/
   private FrequentFlyer(APDU apdu) {
      milesTotal = apdu.commandData;
   }

   /*********************** Public Methods ****************************/
   /**
   * Performs only one time at the applet installation
   * on the smart card
   * A revoir car mauvais renvoi et peut envoyer une exception
   */
   public static FrequentFlyer install(APDU apdu) {
      return new FrequentFlyer(apdu);
   }
   /**
   * Performs the session ie deal with the APDUs sent by the CAD
   */
   public void process(APDU apdu) {
      if (apdu.ins == GAIN_MILES)
         milesGain(apdu);
      else
         if (apdu.ins == REDEEM_MILES)
            milesRedeem(apdu);
         else
            if (apdu.ins == CHECK_MILES)
               milesCheck(apdu);
   }

   /*********************** Private Methods ****************************/
   private void milesGain(APDU apdu) {
      int nbOfMilesGained = apdu.commandData;
      int temporaryMilesTotalTempory = nbOfMilesGained + milesTotal;
      if (nbOfMilesGained > 0 && temporaryMilesTotalTempory <= MILES_MAX) {
         milesTotal = temporaryMilesTotalTempory;
         apdu.setResponseApdu(milesTotal, JCRE.OK);
      } else
         apdu.setResponseApdu(milesTotal, JCRE.IMPOSSIBLE);
   }
   private void milesRedeem(APDU apdu) {
      int nbOfMilesRedeemed = apdu.commandData;
      int temporaryMilesTotal = milesTotal - nbOfMilesRedeemed;
      if (nbOfMilesRedeemed > 0 && temporaryMilesTotal >= 0) {
         milesTotal = temporaryMilesTotal;
         apdu.responseData = milesTotal;
      } else
         apdu.setResponseApdu(milesTotal, JCRE.IMPOSSIBLE);
   }
   private void milesCheck(APDU apdu) {
      apdu.setResponseApdu(milesTotal, JCRE.IMPOSSIBLE);
   }
}

/*********************************************************************
 *                         APDU Class
 *********************************************************************/
final class APDU extends Object {
   /***************** Command APDU Fields*****************************/
   // Mandatory Header
   byte cla; // Class Byte - Often used to identify an application
   byte ins; // Instruction byte - Indicate the instruction code
   byte p1;  // Parameter bytes 

   // Conditionnal Body
   int commandData; //Data Field

   /***************** Response APDU Fields ***************************/
   // Conditional 
   int responseData; //Data field in response

   // Mandatory Trailer
   byte sw1; // private byte SW1, SW2; command APDU status

   /************************** Constructor ***************************/
   public APDU() {
   }

   /************************** Public Methods ************************/
   public void setCommandApdu(byte cla1, byte ins1, byte p, int commandData1) {
      cla = cla1;
      ins = ins1;
      p1 = p;
      commandData = commandData1;
   }
   public void setResponseApdu(int responseData1, byte sw) {
      responseData = responseData1;
      sw1 = sw;
   }
   public void copyCommandApdu(APDU commandApdu) {
      cla = commandApdu.cla;
      ins = commandApdu.ins;
      p1 = commandApdu.p1;
      commandData = commandApdu.commandData;
   }
   public void copyResponseApdu(APDU responseApdu) {
      responseData = responseApdu.responseData;
      sw1 = responseApdu.sw1;
   }
}

/*********************************************************************
 *                         Applet Class
 *********************************************************************/
class Applet extends Thread {
   /*//Java Card Applet*/
   /************************** Constructor ***************************/
   Applet() {
   }

   /************************** Public Methods ************************/
   public void run() {
      byte commandCode;
      boolean selectOk;
      while (true) {
         commandCode = JCRE.self.commandCode;
         if (commandCode == JCRE.SELECT) {
            selectOk = select();
            if (selectOk == true)
               JCRE.self.apdu.sw1 = JCRE.OK;
            else
               JCRE.self.apdu.sw1 = JCRE.IMPOSSIBLE;
         } else
            if (commandCode == JCRE.DESELECT)
               deselect();
            else
               if (commandCode == JCRE.PROCESS) {
                  process(JCRE.self.apdu);
               }
         JCRE.self.resume();
         suspend();
      }
   }
   public void process(APDU apdu) {
   }
   /**
   * Make this applet selected - a new session has begun
   */
   public boolean select() {
      return true;
   }
   /**
   * Performs the session finalization - current session end
   */
   public void deselect() {
   }
}

public class Object {
   Object() {
   }
}

public class Thread {
   Thread() {
   }
   void start() {
   }
}