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.");
}
}
}
}