/**
* 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 = (byte) 0x11;
final static private byte LOG_OP_TOPUP = (byte) 0x22;
final static private byte LOG_OP_ISSUE = (byte) 0x33;
// 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);
}
}
}
}
}
}