Package

Source Code of TopUpTerminal

/**
* TOP-UP Terminal - HW Security Course
*   @author Rafael Boix & Eduardo Novella
*/

//Java SDK imports
import java.util.Arrays;
import java.util.Hashtable;
import java.util.List;
//JCardSim imports -- based on bouncy castle -- for signature generation/verification a-la-JCOP
import javacard.framework.security.DESKey;
import javacard.framework.security.KeyBuilder;
import javacard.framework.security.Signature;
//Javacard IO imports
import javax.smartcardio.Card;
import javax.smartcardio.CardChannel;
import javax.smartcardio.CardException;
import javax.smartcardio.CardTerminal;
import javax.smartcardio.CardTerminals;
import javax.smartcardio.CommandAPDU;
import javax.smartcardio.ResponseAPDU;
import javax.smartcardio.TerminalFactory;
//JCE imports
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

public class TopUpTerminal {

  final private static double VERSION    = 1.5;
  final private static String TERMINAL_ID = "CK_TERM_RU0001TP";
  final static short MAX_BALANCE          = (short) 0x61A8; // Max balance  250.00€   short decimal=25000
  private boolean CARD_LEGIT;
  private CardChannel ch;
  private CardTerminal terminal;
  private DESKey sk;
  private Signature sg;
  private SecretKeySpec AES_KEY_CARD;
  private Cipher cp;
  private String cID;
  private Hashtable<Integer,Object> blacklist;
  //Logfile in /PATH_TO_cheapknip/logs/cheaplogTopUp.txt
  final private static String home = System.getProperty("user.dir")+"/../logs/cheaplogTopUp.txt";

  // APPLET_ID --> 10 Bytes
  final private static byte[] CHEAPKNIP_AID = { (byte) 0x63,(byte) 0x68,
    (byte) 0x65,(byte) 0x61,(byte) 0x70,(byte) 0x6B,(byte) 0x6E,
    (byte) 0x69,(byte) 0x70,(byte) 0x01 };

  // SELECT APDU
  final private static CommandAPDU SELECT_APDU = new CommandAPDU(
      (byte) 0x00, (byte) 0xA4,(byte) 0x04, (byte) 0x00,CHEAPKNIP_AID );

  //APDU INS operation codes in the apdu header
  final private static byte INS_HANDSHAKE      = (byte) 0x20;
  final private static byte INS_GET_BALANCE    = (byte) 0x30;
  final private static byte INS_MODIFY_BALANCE = (byte) 0x40;
  final private static byte INS_GET_OWNER_INFO = (byte) 0x50;
  final static private byte INS_GET_TRNSCT_LOG = (byte) 0x60;
  final static private byte INS_BLOCK_CARD     = (byte) 0x99;
  //Log operation types
  final static private byte LOG_OP_PAY        = (byte0x11;
  final static private byte LOG_OP_TOPUP      = (byte0x22;
  final static private byte LOG_OP_ISSUE      = (byte0x33;

  // CONSTRUCTOR
  public TopUpTerminal(){
    TerminalFactory    tf;
    CardTerminals      ct;
    List<CardTerminal> cs;
    Card card;

    loadBlacklist();
    CARD_LEGIT=false;
    cID="0";
    sg = Signature.getInstance(Signature.ALG_DES_MAC8_ISO9797_M2, false);
    try {
      cp = Cipher.getInstance("AES/ECB/NoPadding");
    } catch (Exception e) {
      Utilities.writeToLogs(home,"[E] "+e.toString());     
    }
    try {
      tf = TerminalFactory.getDefault();
      ct = tf.terminals();
      cs = ct.list(CardTerminals.State.CARD_PRESENT);
      if (cs.isEmpty()){
        Utilities.writeToLogs(home,"[E] No terminal with a card found!");
        System.out.println("[-] No terminal with a card found!");
        return;
      }
      for(CardTerminal c : cs){
        if (c.isCardPresent()){
          try{
            card = c.connect("*");
            try{
              ch =card.getBasicChannel();
              ResponseAPDU resp = ch.transmit(SELECT_APDU);
              terminal=c;

              if (resp.getSW() !=  0x9000){
                Utilities.writeToLogs(home,"[E] Problems: Card Device not selectable != 0x9000");
                throw new Exception("[!] Problems: Card Device not selectable");
              }

            }catch(Exception e){
              Utilities.writeToLogs(home,"[E] "+e.toString());
              System.out.println("[-] Card isn't ok!");
            }
            //card.disconnect(false);
          }catch (CardException ce){
            Utilities.writeToLogs(home,"[E] "+ce.toString());
            System.err.println("[-] Couldn't connect to card!");
          }
          return;
        } else{
          Utilities.writeToLogs(home,"[W] No card present!");
          System.out.println("[-] No card present!");
        }
      }
    } catch (CardException ce){
      Utilities.writeToLogs(home,"[E] "+ce.toString());
      System.out.println("[-] Card status problem!");
   
  }


  public static void printMainMenuOptions(){
    System.out.println("##################################################################################################");
    System.out.println("#                              Welcome to CHEAPKNIP terminal!                                    #");
    System.out.println("#                              ------------------------------                                    #");
    System.out.println("#                              1.- Check the balance                                             #");
    System.out.println("#                              2.- Top-up the balance                                            #");
    System.out.println("#                              3.- Check card transaction log                                    #");
    System.out.println("#                              0.- Exit                                                          #");
    System.out.println("#                                                                                                #");
    System.out.printf ("#                            2012 Cheapknip Payment  %.2f                                        #\n",VERSION);    
    System.out.println("##################################################################################################");
  }

  public static void printMenuTopUp(){
    System.out.println("\n[1] Top-up your Cheapknip");
    System.out.println("[+] How much do you want to top-up your Cheapknip?");
    System.out.println("[*] 1.-    5€");
    System.out.println("[*] 2.-   10€");
    System.out.println("[*] 3.-   20€");
    System.out.println("[*] 4.-   50€");
    System.out.println("[*] 5.-  100€");
    System.out.println("[*] 6.-  Another amount ");
    System.out.println("\n[+] 0.-  Exit");
  }

  /**
   * The real balance is actualBalanceInCard/100 (to avoid decimals); retrieve the short
   * @return Balance
   * @throws CardException
   * @author Eduardo Novella
   */
  private double checkBalance() throws CardException {
    if(!CARD_LEGIT)
      handshakeProtocol();
    if(CARD_LEGIT){
      if(terminal==null ||(terminal!=null && terminal.isCardPresent()==false)){
        Utilities.writeToLogs(home, "[E] Card was teared! Card ID:"+cID);
        System.out.println("Card was teared. Terminal will now reboot.");
        System.exit(-1);
      }
      CommandAPDU apduBalance = new CommandAPDU((byte) 0x00, INS_GET_BALANCE,(byte) 0x00, (byte) 0x00,16+8);
      ResponseAPDU res        = ch.transmit(apduBalance);
      double balance          = 0;

      if (res.getSW() !=  0x9000){
        Utilities.writeToLogs(home,"[E] Error during operation. Remove card from terminal !=  0x9000 "+cID);
        throw new CardException("Error while reading Card Balance. Aborting operation.");
      }
      else{
        if(verifySignature(res.getData(), 16)){
          byte[] plainData = decryptAES128(res.getData(),0,16);
          short b          = Utilities.getShort(plainData, 0);
          balance          = (double) (b / 100.0);
          return balance;
        }
        else{
          System.out.println("Error during operation. Remove card from terminal");
          CARD_LEGIT=false;
          Utilities.writeToLogs(home,"[E] Signature failure ---- Error during operation. Remove card from terminal "+cID);
          throw new CardException("Signature failure");
        }
      }   
    }
    else{
      Utilities.writeToLogs(home,"[E] Handshake protocol failure "+cID);
      throw new CardException("Handshake protocol failure");
    }
  }


  /**
   * Function that increases card Balance
   * @param inc
   * @return boolean as true if topup was successful
   * @author Eduardo Novella & Rafael Boix
   */
  private  boolean topUpThis(double inc) throws CardException{
    getUserData();
    double balance=checkBalance();

    if (CARD_LEGIT){
      if(terminal==null ||(terminal!=null && terminal.isCardPresent()==false)){
        Utilities.writeToLogs(home, "[E] Card was teared! Card ID:"+cID);
        System.out.println("Card was teared. Terminal will now reboot.");
        System.exit(-1);
      }
      if ((balance+inc)*100 > MAX_BALANCE){
        Utilities.writeToLogs(home,"[E] Requested topup amount surpasses card limit. Try it with less! MAX 250.00€ "+cID);
        System.out.println("[!] Requested topup amount surpasses card limit. Try it with less! MAX 250.00€");
        return false;
      }
      if(requestMoneyFromBankMockupFunction()){
        byte[] data =operationAndTopup(inc,1);
        // Expansion of array from 3 bytes to 16 bytes(for AES128 encryption)
        data=Arrays.copyOf(data, 16);
        byte[] encdata=encryptAES128(data, 0, 16);
        // Expansion of array from 16 bytes to 16+8 bytes(for MAC signature to fit)
        encdata=Arrays.copyOf(encdata, 24);
        signMessage(encdata, 16, encdata, 16);
        CommandAPDU apduInc = new CommandAPDU((byte) 0x00,INS_MODIFY_BALANCE,(byte) 0x00, (byte) 0x00,encdata,16+8);
        ResponseAPDU res           = ch.transmit(apduInc);

        if (res.getSW() !=  0x9000){
          if (res.getSW() == 0x6984){
            Utilities.writeToLogs(home,"[E] Requested topup amount surpasses card limit. Try it with less! MAX 250.00€ "+cID);
            System.out.println("[!] Requested topup amount surpasses card limit. Try it with less! MAX 250.00€");
            throw new CardException("Requested topup amount exceeds card limit");
          }
          else{
            Utilities.writeToLogs(home,"[E] Error while reading Card Balance "+cID);
            throw new CardException("Error while reading Card Balance");
          }
        }
        else
          return true;
      }
      else{
        Utilities.writeToLogs(home,"[W] Insufficient balance in your credit source. "+cID);
        System.out.println("[!] Insufficient balance in your credit source.");
        return false;
      }

    }else{
      Utilities.writeToLogs(home,"[E] Wrong signature");
      return false;
    }
  }

  private boolean requestMoneyFromBankMockupFunction() {
    // Blackbox function, to be replaced with a real bank transaction function
    // As for now, our customer is rich and always has money
    return true;
  }

  /**
   * Function that creates a byte[] for topup
   * We have 3 bytes, the first one is for specifying operation:
   * "Increase Money"==01, "Subtract Money"==00
   * The others 2 bytes are the balance to add
   *  01 XX XX
   *  op $$ $$
   *
   * @param money
   * @return byte[] for sending inside an APDU buffer
   * @author Eduardo Novella & Rafael Boix
   */
  private byte[] operationAndTopup(double money,int operacion){
    byte[] op   = new byte[]{(byte) operacion};
    byte[] pay  = new byte[2]
    pay         = Utilities.getArrayBytes((short)(money*100));
    byte[] oppay= new byte[ op.length +pay.length];
    System.arraycopy(op, 0, oppay, 0, op.length);
    System.arraycopy(pay,0, oppay, op.length, pay.length);
    return oppay;
  }

  /**
   * Handshake function: verifies that the card is legit and also proves that the terminal
   * is legit to the card (mutual authentication). Includes two challenge/response operations.
   *
   * @author Eduardo Novella & Rafael Boix
   *
   */
  private  void handshakeProtocol() throws CardException {
    SecureRandom sr = new SecureRandom();
    //STEP 1: generate nonceT, send it to the Card as challenge in plaintext
    //----------------------------------------------------------
    byte[] nonceT = new byte[8];
    byte[] nonceT2 = new byte[8];
    sr.nextBytes(nonceT);
    sr.nextBytes(nonceT2);
    //Card handshake INS=0x20 ; response length=2bytes cardID+16bytes nonceR+8bytes signature
    CommandAPDU challenge1 = new CommandAPDU((byte) 0x00, (byte) 0x20,(byte) 0x00, (byte) 0x00,nonceT,18+8);
    if(ch==null) throw new CardException("Card not present!");
    ResponseAPDU res= ch.transmit(challenge1);
    byte[] buff= res.getData();
    if (res.getSW() !=  0x9000){
      Utilities.writeToLogs(home,"[E] Error reading the card. Aborting operation. Error while handshake - step 1 !=0x9000 "+cID);
      System.out.println("[!] Error reading the card. Aborting operation. "); throw new CardException("Error while handshake - step 1");
    }
    else{
      //Step 2: recover data from response APDU = cardID, nonceR
      //----------------------------------------------------------

      // CardID goes in plaintext
      byte[] cardID=Arrays.copyOfRange(buff, 16, 18);
      cID=Integer.toString((int)Utilities.getShort(cardID, 0)); //We put the read CardID in the cID variable for logging purposes

      //We check if the card is blacklisted; if it is, block it && break handshake
      if(isBlacklisted(cardID)){
        Utilities.writeToLogs(home,"[E] Error: Blacklisted card handshake attempt "+cID);
        System.out.println("[!] Error: your card is blocked. Go to the closest CheapKnip Customer Service Point");
        CARD_LEGIT=false;
        CommandAPDU blockCard = new CommandAPDU((byte) 0x00, INS_BLOCK_CARD,(byte) 0x00, (byte) 0x00);
        res= ch.transmit(blockCard);
        System.exit(-1);
      }

      // Compute & init card keys
      if (AES_KEY_CARD==null)
        AES_KEY_CARD=getKeyFromCardID();
      if (sk==null)
        initSignatureKey(cardID);
      if(!verifySignature(buff, 18)){
        Utilities.writeToLogs(home,"[E] Error: check that your card is a CheapKnip card(SIGNATURE CHECK FAILED) "+cID);
        System.out.println("[!] Error: check that your card is a CheapKnip card");
        CARD_LEGIT=false;
        return;
      }

      // Decrypt nonceR --> AES(nonceR)K
      byte[] cryptoBuff = decryptAES128(buff,0,16);
      byte[] nonceR    = Arrays.copyOf(cryptoBuff,8);

      byte[] nonceC_T2=new byte[16];
      //Recover nonceC from Card: nonceR XOR nonceT
      //We put nonceC in the first 8 bytes of the array
      for(int i=0;i<8;i++){
        nonceC_T2[i]=(byte)((nonceR[i])^(nonceT[i]));
      }
      for(int i=8;i<16;i++){
        nonceC_T2[i]=nonceT2[i-8];
      }
      //encrypt properly nonceC_T2

      //Step3: we send AES(nonceC,nonceT2)K back to the card and wait for response AES(nonceR2)K
      //----------------------------------------------------------
      byte[] enc_nonceC_T2=encryptAES128(nonceC_T2, 0, 16);
      //Note in step3: state is a parameter; P1 is 0x10!!
      CommandAPDU challenge2 = new CommandAPDU((byte) 0x00, INS_HANDSHAKE,(byte) 0x10, (byte) 0x00,enc_nonceC_T2,16+8);
      res= ch.transmit(challenge2);
      buff= res.getData();
      if(!verifySignature(buff, 16)){
        Utilities.writeToLogs(home,"[E] Card error. Remove your card & try again(SIGNATURE FAILED) "+cID);
        System.out.println("[!] Card error. Remove your card & try again");
        CARD_LEGIT=false;
        return;
      }
      if (res.getSW() !=  0x9000){
        Utilities.writeToLogs(home,"[E] Error reading the card. Aborting operation. Error while handshake - step 3 "+cID);
        throw new CardException("Error while handshake - step 3");
      }
      else{
        //Step 4: check if card is legit: nonceT2 should be the same than the one in memory
        //----------------------------------------------------------
        cryptoBuff=Arrays.copyOf(buff, 16);
        buff=decryptAES128(cryptoBuff, 0, 16);
        byte[] nonceR2=Arrays.copyOf(buff,8);

        //We rebuild nonceT2 from nonceR2 by doing XOR(nonceR2,nonceT)
        Utilities.XOR(nonceR2, nonceT, nonceR2, 0, 0, 0, 8);
        if(Arrays.equals(Arrays.copyOf(nonceR2, 8), nonceT2)){
          CARD_LEGIT=true;
        }
        else{
          CARD_LEGIT=false;
          Utilities.writeToLogs(home,"[E] Bogus answer from card, failed challenge2." +cID);
          System.out.println("[!] Error reading the card. Aborting operation. ");
          throw new CardException("Bogus answer from card, failed challenge2.");
        }
      }
    }
  }


  /**
   * Prints user data
   *   User details: name, address ,birthday  100 Bytes
   *  Name     == 40 bytes
   *  Address  == 54 bytes
   *  Birthday == 6 bytes
   * @throws CardException
   */
  @SuppressWarnings("unused")
  private void getUserData() throws CardException{
    if(!CARD_LEGIT)
      handshakeProtocol();
    if(CARD_LEGIT){
      if(terminal==null ||(terminal!=null && terminal.isCardPresent()==false)){
        Utilities.writeToLogs(home, "[E] Card was teared! Card ID:"+cID);
        System.out.println("Card was teared. Terminal will now reboot.");
        System.exit(-1);
      }
      CommandAPDU dataApdu = new CommandAPDU((byte) 0x00, INS_GET_OWNER_INFO,(byte) 0x00, (byte) 0x00,100);
      ResponseAPDU res        = ch.transmit(dataApdu);

      if (res.getSW() !=  0x9000){
        Utilities.writeToLogs(home,"[E] Error while reading Card Balance.  Aborting operation. != 0x9000 "+cID);
        throw new CardException("Error while reading Card Balance.  Aborting operation.");
      }
      else{
        String userdata= Utilities.byteArrayToHexString(res.getData());
        String name    = userdata.substring(0, 40);
        String address = userdata.substring(40, 94);
        String date    = userdata.substring(94,100);

        System.out.printf("[+] Mr/Ms. %s \n",Utilities.hex2ascii(name))
      }   
    }
    else{
      Utilities.writeToLogs(home,"[E] Failure with getUserData");
      throw new CardException("Failure");
    }
  }


  //Mockup for a real implementation of getting the Master key
  private byte[] getMasterKey(){
    return "1234567890123456".getBytes();
  }

  //Main function: just prints a menu, does some basic input sanitization and calls the terminal methods
  public static void main(String[] args) {

    System.out.printf("Terminal:%s\t\t\t\t\t\t\t%s\n",TERMINAL_ID,Utilities.getSystemDate());

    int opt=-1;
    TopUpTerminal tu = new TopUpTerminal();


    do{
      printMainMenuOptions();
      opt=Utilities.enterOption(0,3);
      boolean topUpOK=false;

      try{
        switch(opt){
        case 3:
          tu.getTransactionLog();
          break;
        case 1:
          System.out.println("[1] Check the balance ");
          double balance =-999999.99;
          try{
            balance= tu.checkBalance();
          }
          catch (CardException e) {
            Utilities.writeToLogs(home,"[E] Error reading the card balance. Aborting operation. "+tu.cID);
            System.out.println("[!] Error reading the card balance. Aborting operation. \n");
          }
          if(balance!=-999999.99){
            Utilities.writeToLogs(home, "[+] The current balance in the CheapKnip card is "+ balance + " "+tu.cID);
            System.out.printf("[+] The current balance in the CheapKnip card is %3.2f €\n", balance);
          }
          break;
        case 2:
          double increment=0.0;


          printMenuTopUp();
          int optop = Utilities.enterOption(0,6);

          switch(optop){
          case 0:
            break;
          case 1:
            topUpOK=tu.topUpThis(5);
            break;
          case 2:
            topUpOK=tu.topUpThis(10);
            break;
          case 3:
            topUpOK=tu.topUpThis(20);
            break;
          case 4:
            topUpOK=tu.topUpThis(50);
            break;
          case 5:
            topUpOK=tu.topUpThis(100);
            break;
          case 6:
            do{
              System.out.print("[+] How much do you want to top up your Cheapknip?: (€) ");
              increment = Utilities.enterDouble();
              if(250< increment|| 0>=increment)
                System.out.println("[!] Error - Write amount in range [0 - 250] € ");
            }while(250< increment|| 0>=increment);

            topUpOK=tu.topUpThis(increment);
            break
          }

          if (topUpOK){
            double money=0;
            try{
              money=tu.checkBalance();
            }catch(CardException e){
              Utilities.writeToLogs(home, "[E] Error reading the card balance. Aborting operation. "+tu.cID);
              System.out.println("[!] Error reading/topuping the card balance. Aborting operation.");
            }
            Utilities.writeToLogs(home,"[+] Your top-up was successful! Now the current balance in the CheapKnip card is "+ money + " "+tu.cID);
            System.out.printf("[+] Your top-up was successful! \nNow the current balance in the CheapKnip card is %3.2f €\n", money);
          }else{
            Utilities.writeToLogs(home,"[W] Your top-up wasn't successful! " +tu.cID);
            System.out.println("[!] Your top-up wasn't successful!");
          }
        }
      }catch(CardException e){
        Utilities.writeToLogs(home,"[E] Error reading/topuping the card balance. Aborting operation. "+tu.cID);
        System.out.println("[!] Error reading/topuping the card balance. Aborting operation.");
      }
    }while(opt!=0);
    System.out.println("Thank you for using our CheapKnip Terminal!");System.exit(0);
  }

  /**
   *
   * CRYPTO FUNCTIONS
   *
   */

  /**
   * Sign message with MAC using same algorithm as the JavaCard does
   * @author Eduardo Novella & Rafael Boix
   */
  private void signMessage(byte[] msg, int msgLength, byte[] signature, int signOffset){
    if(sk!=null)
    {sg.init(sk, Signature.MODE_SIGN);
    sg.sign(msg, (short)0, (short)msgLength, signature, (short)signOffset);
    }   
    else
      Utilities.writeToLogs(home,"[E] Signature key not initialized");
  }

  /**
   * Verify 8byte signature byte array
   * @author Eduardo Novella & Rafael Boix
   */
  private boolean verifySignature(byte[] msgPlusSignature, int msgLength){
    if(sk!=null)
    {sg.init(sk, Signature.MODE_VERIFY);
    return sg.verify(msgPlusSignature, (short)0, (short)msgLength, msgPlusSignature,(short)msgLength,(short)8);}
    else{
      Utilities.writeToLogs(home,"[E] Signature key not initialized"); return false;
    }
  }

  /**
   * Encrypt byte array
   * @author Eduardo Novella & Rafael Boix
   */
  public byte[] encryptAES128 (byte[] plain,int offset,int len) {
    byte[] encrypted=null;
    byte[] plainTrimmed = new byte[len];
    System.arraycopy(plain, offset, plainTrimmed, 0, len);
    try {
      if(AES_KEY_CARD==null)
        cp.init(Cipher.ENCRYPT_MODE,getKeyFromCardID());
      else
        cp.init(Cipher.ENCRYPT_MODE,AES_KEY_CARD);
      encrypted = cp.doFinal(plainTrimmed);
    } catch (Exception e){
      Utilities.writeToLogs(home,"[E] "+e.toString());
    }
    return encrypted;
  }

  /**
   * Decrypt byte array
   * @author Eduardo Novella & Rafael Boix
   */
  public byte[] decryptAES128 (byte[] encrypted,int offset,int len){
    byte[] plaintext=null;
    byte[] encryptedTrimmed = new byte[len];
    System.arraycopy(encrypted, offset, encryptedTrimmed, 0, len);
    try {
      if(AES_KEY_CARD==null)
        cp.init(Cipher.DECRYPT_MODE,getKeyFromCardID());
      else
        cp.init(Cipher.DECRYPT_MODE,AES_KEY_CARD);
      plaintext = cp.doFinal(encryptedTrimmed);
    } catch (Exception e){
      Utilities.writeToLogs(home,"[E] "+e.toString());
    }
    return plaintext;
  }

  /**
   * Get card key from card directly
   * @author Eduardo Novella & Rafael Boix
   */
  private SecretKeySpec getKeyFromCardID() throws CardException{
    SecretKeySpec k = null;
    CommandAPDU cardIDrequest = new CommandAPDU((byte) 0x00, (byte) 0x69,(byte) 0x00, (byte) 0x00,2);
    ResponseAPDU res= ch.transmit(cardIDrequest);
    byte[] cardID= res.getData();
    if (res.getSW() !=  0x9000){
      Utilities.writeToLogs(home,"[E] Error reading card ID. Check card connection & reader. ");
      throw new CardException("Error while reading Card ID");
    }
    else{
      //We set K as the CardID encrypted with the master key
      SecretKeySpec sks = new SecretKeySpec(getMasterKey(), "AES");
      try {
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.ENCRYPT_MODE, sks);
        k= new SecretKeySpec(cipher.doFinal(cardID),"AES");

      } catch (Exception e) {
        Utilities.writeToLogs(home,"[E] "+e.toString());
      }
    }
    return k;

  }

  /**
   * Get card key from byte array
   * @author Eduardo Novella & Rafael Boix
   */
  @SuppressWarnings("unused")
  private SecretKeySpec getKeyFromCardID(byte[] cardID) throws CardException{
    SecretKeySpec k = null;
    SecretKeySpec sks = new SecretKeySpec(getMasterKey(), "AES");
    try {
      Cipher cipher = Cipher.getInstance("AES");
      cipher.init(Cipher.ENCRYPT_MODE, sks);
      k= new SecretKeySpec(cipher.doFinal(cardID),"AES");

    } catch (Exception e) {
      Utilities.writeToLogs(home,"[E] "+e.toString());
    }
    return k;
  }

  /**
   * @author Eduardo Novella & Rafael Boix
   */
  private boolean initSignatureKey(byte[] cardID){
    //Note that this works *in the terminal* due to the JCardSim wrapper of BouncyCastle :)
    SecretKeySpec sks = new SecretKeySpec(getMasterKey(), "AES");
    try {
      Cipher cipher = Cipher.getInstance("AES");
      int len=cardID.length;
      byte[] sigCardID = Arrays.copyOf(cardID, 8*len);
      for(int i=len;i<8*len;i++)
        sigCardID[i]=(byte)(sigCardID[i-1]^sigCardID[i-2]*i);
      cipher.init(Cipher.ENCRYPT_MODE, sks);
      byte[] sgn = Arrays.copyOf(cipher.doFinal(sigCardID),24); //3DES key for signature purposes= 192 bit
      sk = (DESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_DES, KeyBuilder.LENGTH_DES3_3KEY, false);
      sk.setKey(sgn, (short)0);
    } catch (Exception e) {
      Utilities.writeToLogs(home,"[E] "+e.toString());
      return false;
    }
    return true;
  }

  /**
   * @author Eduardo Novella & Rafael Boix
   */
  private String logOperationToString(byte logOp){
    switch(logOp){
    case LOG_OP_ISSUE:
      return new String("CARD_ISSUE");
    case LOG_OP_PAY:
      return new String("PAYMENT");
    case LOG_OP_TOPUP:
      return new String("TOPUP");
    }
    return new String("UNDEFINED");
  }

  private void loadBlacklist() {
    //Mockup for function which loads a blacklist in the terminal; we will just update the blacklist with one card value
    blacklist=new Hashtable<Integer, Object>();
    blacklist.put(12346, true);
  }

  private boolean isBlacklisted(byte[] cardid){
    int ID=Utilities.getShort(cardid, 0);
    if(blacklist.containsKey(ID)){
      return true;
    }
    return false;
  }
  private void getTransactionLog() throws CardException{
    if(!CARD_LEGIT)
      handshakeProtocol();
    if(CARD_LEGIT){
      if(terminal==null ||(terminal!=null && terminal.isCardPresent()==false)){
        Utilities.writeToLogs(home, "[E] Card was teared! Card ID:"+cID);
        System.out.println("Card was teared. Terminal will now reboot.");
        System.exit(-1);
      }
      CommandAPDU cardIDrequest = new CommandAPDU((byte) 0x00, INS_GET_TRNSCT_LOG,(byte) 0x00, (byte) 0x00,60);
      ResponseAPDU res= ch.transmit(cardIDrequest);
      byte[] trLog= res.getData();
      if (res.getSW() !=  0x9000){
        Utilities.writeToLogs(home,"[E] Error while reading transaction log. "+cID);
        throw new CardException("Error while reading transaction log.");
      }
      else{
        if(!verifySignature(trLog, 52))
          Utilities.writeToLogs(home,"[E] LOG SIGNATURE FAILED - CHECK FOR TAMPERED DATA. "+cID);
        int count=Utilities.getShort(trLog, 0);
        byte op;
        double oldbalance=0;
        double newbalance=0;
        int index;
        if(count<=10){
          System.out.println("Log of last operations ** "+Utilities.getSystemDate());
          System.out.println("#OP");
          for(int i=0;i<count;i++){
            op=trLog[(i*5)+2];
            oldbalance=(double)(Utilities.getShort(trLog, ((i*5)+3))/100.0);
            newbalance=(double)(Utilities.getShort(trLog, ((i*5)+5))/100.0);
            System.out.println((i+1)+".- Operation: "+logOperationToString(op)+" ** Old Balance: "+oldbalance+" ** New Balance: "+newbalance);
          }
        }
        else if (count>10){
          System.out.println("Log of last operations ** "+Utilities.getSystemDate());
          int correctIndexes=count%10; //Up to correctIndexes the indexes are in the same decade as count
          for(int i=0;i<10;i++){
            if(i<correctIndexes)
              index=count-correctIndexes+i;
            else
              index=count-correctIndexes-10+i;
            op=trLog[(i*5)+2];
            oldbalance=(double)(Utilities.getShort(trLog, ((i*5)+3))/100.0);
            newbalance=(double)(Utilities.getShort(trLog, ((i*5)+5))/100.0);
            System.out.println((index+1)+".- Operation: "+logOperationToString(op)+" ** Old Balance: "+oldbalance+" ** New Balance: "+newbalance);
          }
        }
      }
    }
  }
}
TOP

Related Classes of TopUpTerminal

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.