/* 
 * File:   EszkozApplet.java
 * Szerzo: Ondi Attila (att@ludens.elte.hu)
 * Leiras: Az elektronikus-penztarca alkalmazas
 *         amely a gyurun fut
 */

import javacard.framework.*;

/* 
 * a gyurun futo alkalmazas 
 */
public class EszkozApplet extends Applet
{
   /* Az alabbi "private" valtozok az eszkoz belso valtozoi */

   private int Egyenleg = 300; /* a kezdo egyenleg (a valos eletben 0 kell) */
   private int Limit = 50; /* a kezdo limit (a vlaos eletben 0 kell) */
   private byte[] BankJelszo = {'1','2','3','4'}; /* a gyuruhoz tartozo banki jelszo */
   private byte[] PIN = {'H','e','l','l','o'}; /* a felhasznaloi jelszo */
   private short bankJelszoProbalkozas = (short)0; /* hanyszor rontottak el a banki jelszot */
   private short userJelszoProbalkozas = (short)0; /* hanyszor rontottak el a felhasznaloi jelszot */
   private final short bankPROBALKOZAS = (short)1; /* hanyszor ronthato el a banki jelszo (egyel tobb) */
   private final short userPROBALKOZAS = (short)3; /* hanyszor ronthato el a felhasznaloi jelszo (egyel tobb) */
   
   private APDU[] Tranzakcio = new APDU[5]; /* a legutolso lenyeges tranzakciok */
   private byte utolsoTranzakcio = (byte)0; /* az utolso lenyeges tranzakcio szama az 5-bol */

   /* Az alabbi konstansok az eszkozzel valo kommunikaciohoz kellenek */

   final static byte EA_CLA               = (byte) 0x81;
   final static byte EA_EgyenlegKerdez    = (byte) 0x01;
   final static byte EA_EgyenlegModosit   = (byte) 0x03;
   final static byte EA_LimitKerdez       = (byte) 0x04;
   final static byte EA_LimitModosit      = (byte) 0x05;
   final static byte EA_JelszoHasonlit    = (byte) 0x06;
   final static byte EA_JelszoModosit     = (byte) 0x07;
   final static byte EA_TranzakcioKerdez  = (byte) 0x08;
   final static byte SELECT_CLA           = (byte) 0x00;
   final static byte SELECT_INS           = (byte) 0xA4;

   /*
    * Konstruktor.
    */
   EszkozApplet()
   {
      register();
   }
  
   /*
    * Installalja az 'EszkozApplet' alkalmazast a gyurure.
    */
   public static void install(APDU apdu)
   {
      new EszkozApplet();
   }
  
   /*
    * a megadottappletet allitja aktualisan kivalasztottnak a gyurun.
    */
   public boolean select(APDU apdu)
   {
      return true;
   }
  
   /* 
    * vegrehajtja a megadott APDU-t
    */
   public void process(APDU apdu) throws ISOException 
   {
      byte[] buffer = apdu.getBuffer();
    
      if ((buffer[ISO.OFFSET_CLA] == SELECT_CLA)&& 
          (buffer[ISO.OFFSET_INS] == SELECT_INS))
      {
         return;
      }
      if (buffer[ISO.OFFSET_CLA] != EA_CLA)
      {
         throw new ISOException(ISO.SW_CLA_NOT_SUPPORTED);
      }
      else 
      {
         switch (buffer[ISO.OFFSET_INS])
         {
            case EA_EgyenlegKerdez: 
            {
               EgyenlegKerdez(apdu);
               break;
            }
            case EA_EgyenlegModosit:
            {
               EgyenlegModosit(apdu);
               break;
            }
            case EA_LimitKerdez:
            {
               LimitKerdez(apdu);
               break;
            }
            case EA_LimitModosit:
            {
               LimitModosit(apdu);
               break;
            }
            case EA_JelszoHasonlit:
            {
               JelszoHasonlit(apdu);
               break;
            }
            case EA_JelszoModosit:
            {
               JelszoModosit(apdu);
               break;
            }
            case EA_TranzakcioKerdez:
            {
               TranzakcioKerdez(apdu);
               break;
            }
            default: 
            {
               throw new ISOException(ISO.SW_INS_NOT_SUPPORTED);
            }
         }
      }
   }
   
   /*
    * Integer-t byte[] -ra alakit
    */
   protected byte[] IntegerToBytes(int szam)
   {
      byte[] b = new byte[4];

      for(byte i = 3;i>=0;i--)
      {
         b[i]=(byte)szam;
         szam>>>=8;
      }
      return b;
   }

   /* 
    * byte[] -ot Integer-re alakit
    */
   protected int BytesToInteger(byte[] b)
   {
      int szam = 0;

      szam+=(int)b[0];
      for(byte i = 1;i<4;i++)
      {
         szam<<=8;
         if((int)b[i]<0)
         {
            szam+=(int)b[i]+256;
         }
         else
         {
            szam+=(int)b[i];
         }
      }
      return szam;
   }

   /*
    * ket byte[] osszehasonlitasa (jelszavak osszehasonlitasahoz)
    */
   protected boolean egyezik(byte[] egyik, byte[] masik)
   {
      if(egyik.length!=masik.length)
      {
         return false;
      }
      else
      {
         for(byte i = 0; i<egyik.length; i++)
         {
            if(egyik[i]!=masik[i])
            {
               return false;
            }
         }
         return true;
      }
   }

   /*
    * az egyenleget kerdezik
    */
   protected void EgyenlegKerdez(APDU apdu) 
   {
      byte[] b = IntegerToBytes(Egyenleg); 
    
      apdu.setOutgoing();
      apdu.sendBytesLong(b, (short) 0, (short) 4);
   }

   /* 
    * egyenleget szeretnenek modositani 
    */
   protected void EgyenlegModosit(APDU apdu) 
   {
      byte[] buffer = apdu.getBuffer();
      int mennyivel2;
      byte[] b = new byte[1];
      byte[] hossz = new byte[1];
      byte[] bank = new byte[1];
      byte[] jelszo;
      byte[] mennyivel = new byte[4];

      apdu.setIncomingAndReceive();

      Util.arrayCopy(buffer, ISO.OFFSET_CDATA, bank, (short)0, (short)1);
      Util.arrayCopy(buffer, (short)(ISO.OFFSET_CDATA+1), hossz, (short)0, 
                     (short)1);
      jelszo = new byte[hossz[0]];
      Util.arrayCopy(buffer, (short)(ISO.OFFSET_CDATA+2), jelszo, (short)0, 
                     (short)hossz[0]);
      Util.arrayCopy(buffer, (short)(ISO.OFFSET_CDATA+2+jelszo.length), 
                     mennyivel, (short)0, (short)4);
      mennyivel2 = BytesToInteger(mennyivel);
      
      /* bani modositas */
      if(bank[0]==(byte)1)
      {
         if(egyezik(bankDekodol(jelszo),BankJelszo)&&
            (bankJelszoProbalkozas<bankPROBALKOZAS))
         {
         }
         else
         {
            bankJelszoProbalkozas += (short)1;
         }
      }
      else /* felhasznaloi modositas */
      {
         if((egyezik(userDekodol(jelszo),PIN)||
             (-mennyivel2<=Limit))&&
            (userJelszoProbalkozas<userPROBALKOZAS))
         {
         }
         else
         {
            userJelszoProbalkozas += (short)1;
         }
      }
      /* ha a modositas a megfelelo kereteken belul van */
      if((egyezik(bankDekodol(jelszo),BankJelszo)||
          (egyezik(userDekodol(jelszo),PIN)&&(mennyivel2<=0)))||
          ((mennyivel2<=0)&&(-mennyivel2<=Limit))&&
         (Egyenleg+mennyivel2>=0))
      {
         Egyenleg += mennyivel2;
         b[0] = 1;
         hozzaadTranzakcio(apdu);
      }
      else
      {
         b[0] = 0;
      }
      apdu.setOutgoing();
      apdu.sendBytesLong(b, (short) 0, (short) 1);
   }

   /* 
    * a limitet kerdezik 
    */
   protected void LimitKerdez(APDU apdu) 
   {
      byte[] b = IntegerToBytes(Limit); 

      apdu.setOutgoing();
      apdu.sendBytesLong(b, (short) 0, (short) 4);
   }

   /* 
    * limitet szeretnenek modositani 
    */
   protected void LimitModosit(APDU apdu) 
   {
      byte[] buffer = apdu.getBuffer();
      byte[] b = new byte[1];
      byte[] hossz = new byte[1];
      byte[] bankjelszo;
      byte[] mennyire = new byte[4];

      apdu.setIncomingAndReceive();

      Util.arrayCopy(buffer, (short)(ISO.OFFSET_CDATA+1), hossz, (short)0, 
                     (short)1);
      bankjelszo = new byte[hossz[0]];
      Util.arrayCopy(buffer, (short)(ISO.OFFSET_CDATA+2), bankjelszo, (short)0,
                     (short)hossz[0]);
      Util.arrayCopy(buffer, (short)(ISO.OFFSET_CDATA+2+bankjelszo.length), 
                     mennyire, (short)0, (short)4);
      
      /* a limitet csak a Bank modosithatja */
      if(egyezik(bankDekodol(bankjelszo),BankJelszo)&&
         (bankJelszoProbalkozas<bankPROBALKOZAS))
      {
         if(BytesToInteger(mennyire)>=0)
         {
            Limit = BytesToInteger(mennyire);
            b[0] = 1;
            hozzaadTranzakcio(apdu);
         }
         else
         {
            b[0] = 0;
         }
      }
      else
      {
         bankJelszoProbalkozas += (short)1;
         b[0] = 0;
      }
      apdu.setOutgoing();
      apdu.sendBytesLong(b, (short) 0, (short) 1);
   }

   /* 
    * a kuldott jelszo osszehasonlitasa a felhasznaloi jelszoval
    */
   protected void JelszoHasonlit(APDU apdu) 
   {
      byte[] buffer = apdu.getBuffer();
      byte[] b = new byte[1];
      byte[] hossz = new byte[1];
      byte[] jelszo;

      apdu.setIncomingAndReceive();

      Util.arrayCopy(buffer, ISO.OFFSET_CDATA, hossz, (short) 0, (short)1);
      jelszo = new byte[hossz[0]];
      Util.arrayCopy(buffer, (short)(ISO.OFFSET_CDATA+1), jelszo, (short)0, 
                     (short)hossz[0]);
      if(egyezik(userDekodol(jelszo),PIN)&&
         (userJelszoProbalkozas<userPROBALKOZAS))
      {
         userJelszoProbalkozas = 0;
         b[0] = 1;
      }
      else
      {
         userJelszoProbalkozas += (short)1;
         b[0] = 0;
      }
      apdu.setOutgoing();
      apdu.sendBytesLong(b, (short) 0, (short) 1);
   }

   /* 
    * jelszo modositasa 
    */
   protected void JelszoModosit(APDU apdu) 
   {
      byte[] buffer = apdu.getBuffer();
      byte[] b = new byte[1];
      byte[] hossz = new byte[1];
      byte[] bankjelszo;
      byte[] ujjelszo;

      apdu.setIncomingAndReceive();

      Util.arrayCopy(buffer, ISO.OFFSET_CDATA, hossz, (short) 0, (short)1);
      bankjelszo = new byte[hossz[0]];
      Util.arrayCopy(buffer, (short)(ISO.OFFSET_CDATA+1), bankjelszo, (short)0,
                     (short)hossz[0]);
      Util.arrayCopy(buffer, (short)(ISO.OFFSET_CDATA+1+bankjelszo.length), 
                     hossz, (short)0, (short)1);
      ujjelszo = new byte[hossz[0]];
      Util.arrayCopy(buffer, (short)(ISO.OFFSET_CDATA+2+bankjelszo.length), 
                     ujjelszo, (short)0, (short)hossz[0]);
      
      /* 
       * jelszot csak a Bank modosithat (a felhszanalo is csak azon keresztul)
       */
      if(egyezik(bankDekodol(bankjelszo),BankJelszo)&&
         (bankJelszoProbalkozas<bankPROBALKOZAS))
      {
         userJelszoProbalkozas = 0;
         ujjelszo = userDekodol(ujjelszo);
         PIN = ujjelszo;
         b[0] = 1;
         hozzaadTranzakcio(apdu);
      }
      else
      {
         bankJelszoProbalkozas += (short)1;
         b[0] = 0;
      }
      apdu.setOutgoing();
      apdu.sendBytesLong(b, (short) 0, (short) 1);
   }
   
   /* 
    * egy eltarolt tranzakcio lekerdezese 
    */
   protected void TranzakcioKerdez(APDU apdu)
   {
      byte[] buffer = apdu.getBuffer();
      byte[] hossz = new byte[1];
      byte[] bankjelszo;
      byte[] hanyadik = new byte[1];
      byte[] apdubytes;

      apdu.setIncomingAndReceive();

      Util.arrayCopy(buffer, ISO.OFFSET_CDATA, hossz, (short) 0, (short)1);
      bankjelszo = new byte[hossz[0]];
      Util.arrayCopy(buffer, (short)(ISO.OFFSET_CDATA+1), bankjelszo, (short)0,
                     (short)hossz[0]);
      Util.arrayCopy(buffer, (short)(ISO.OFFSET_CDATA+1+bankjelszo.length), 
                     hanyadik, (short)0, (short)1);
      
      if(egyezik(bankDekodol(bankjelszo),BankJelszo)&&
         (bankJelszoProbalkozas<bankPROBALKOZAS))
      {
         userJelszoProbalkozas = 0;
         apdubytes = Tranzakcio[hanyadik[0]].getBuffer();
      }
      else
      {
         bankJelszoProbalkozas += (short)1;
         apdubytes = new byte[1];
         apdubytes[0] = (byte)0;
      }
      apdu.setOutgoing();
      apdu.sendBytesLong(apdubytes, (short) 0, (short) apdubytes.length);
   }
   
   /* felhasznaloi jelszo visszafejtese */
   private byte[] userDekodol(byte[] kodszo)
   {
      byte[] dekodolt = new byte[kodszo.length];
      
      for(int i = 0;i<kodszo.length;i++)
      {
         if(kodszo[i]!=0)
         {
            dekodolt[i] = (byte)(kodszo[i]-(byte)1);
         }
         else
         {
            dekodolt[i] = (byte)255;
         }
      }
      return dekodolt;
   }
   
   /* banki jelszo visszafejtese */
   protected byte[] bankDekodol(byte[] kodszo)
   {
      byte[] dekodolt = new byte[kodszo.length];
      
      for(int i = 0;i<kodszo.length;i++)
      {
         if(kodszo[i]!=255)
         {
            dekodolt[i] = (byte)(kodszo[i]+(byte)1);
         }
         else
         {
            dekodolt[i] = (byte)0;
         }
      }
      return dekodolt;
   }
   
   /* 
    * uj tranzakcio felvetele 
    */
   protected void hozzaadTranzakcio(APDU apdu)
   {
      switch(utolsoTranzakcio)
      {
         case (byte)0:
         case (byte)1:
         case (byte)2:
         case (byte)3:
         {
            Tranzakcio[utolsoTranzakcio] = apdu;
            utolsoTranzakcio += (byte)1;
            break;
         }
         case (byte)4:
         {
            Tranzakcio[0] = Tranzakcio[1];
            Tranzakcio[1] = Tranzakcio[2];
            Tranzakcio[2] = Tranzakcio[3];
            Tranzakcio[3] = Tranzakcio[4];
            Tranzakcio[4] = apdu;
            break;
         }
         default:
         {
            break;
         }
      }
   }   
}