Emv scan via contact interface (#789)

* share getATR from smartcard.h/c
* remove duplicates in tlv.h and add get_uint_8
* check ATS/ATR length
This commit is contained in:
Oleg Moiseenko 2019-02-21 23:02:22 +02:00 committed by pwpiwi
commit 0b6efd01ec
5 changed files with 151 additions and 28 deletions

View file

@ -311,7 +311,7 @@ static int PrintATR(uint8_t *atr, size_t atrlen) {
return 0; return 0;
} }
static bool smart_getATR(smart_card_atr_t *card) bool smart_getATR(smart_card_atr_t *card)
{ {
if (UseAlternativeSmartcardReader) { if (UseAlternativeSmartcardReader) {
return pcscGetATR(card); return pcscGetATR(card);
@ -805,6 +805,11 @@ static int CmdSmartInfo(const char *Cmd){
return 1; return 1;
} }
if (!card.atr_len) {
if (!silent) PrintAndLogEx(ERR, "can't get ATR from a smart card");
return 1;
}
// print header // print header
PrintAndLogEx(INFO, "--- Smartcard Information ---------"); PrintAndLogEx(INFO, "--- Smartcard Information ---------");
PrintAndLogEx(INFO, "-------------------------------------------------------------"); PrintAndLogEx(INFO, "-------------------------------------------------------------");

View file

@ -13,8 +13,10 @@
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include "smartcard.h"
extern int CmdSmartcard(const char *Cmd); extern int CmdSmartcard(const char *Cmd);
extern bool smart_getATR(smart_card_atr_t *card);
extern int ExchangeAPDUSC(uint8_t *datain, int datainlen, bool activateCard, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen); extern int ExchangeAPDUSC(uint8_t *datain, int datainlen, bool activateCard, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen);
#endif #endif

View file

@ -29,6 +29,7 @@
#include "dol.h" #include "dol.h"
#include "emv_tags.h" #include "emv_tags.h"
#include "cmdhf14a.h" #include "cmdhf14a.h"
#include "cmdsmartcard.h"
#define TLV_ADD(tag, value)( tlvdb_change_or_add_node(tlvRoot, tag, sizeof(value) - 1, (const unsigned char *)value) ) #define TLV_ADD(tag, value)( tlvdb_change_or_add_node(tlvRoot, tag, sizeof(value) - 1, (const unsigned char *)value) )
void ParamLoadDefaults(struct tlvdb *tlvRoot) { void ParamLoadDefaults(struct tlvdb *tlvRoot) {
@ -1255,13 +1256,110 @@ int CmdEMVExec(const char *cmd) {
// process Format1 (0x80) and print Format2 (0x77) // process Format1 (0x80) and print Format2 (0x77)
ProcessACResponseFormat1(tlvRoot, buf, len, decodeTLV); ProcessACResponseFormat1(tlvRoot, buf, len, decodeTLV);
PrintAndLogEx(NORMAL, "\n* * Processing online request\n"); uint8_t CID = 0;
tlvdb_get_uint8(tlvRoot, 0x9f27, &CID);
// AC1 print result
PrintAndLog("");
if ((CID & EMVAC_AC_MASK) == EMVAC_AAC) PrintAndLogEx(INFO, "AC1 result: AAC (Transaction declined)");
if ((CID & EMVAC_AC_MASK) == EMVAC_TC) PrintAndLogEx(INFO, "AC1 result: TC (Transaction approved)");
if ((CID & EMVAC_AC_MASK) == EMVAC_ARQC) PrintAndLogEx(INFO, "AC1 result: ARQC (Online authorisation requested)");
if ((CID & EMVAC_AC_MASK) == EMVAC_AC_MASK) PrintAndLogEx(INFO, "AC1 result: RFU");
// decode Issuer Application Data (IAD)
uint8_t CryptoVersion = 0;
const struct tlv *IAD = tlvdb_get(tlvRoot, 0x9f10, NULL);
if (IAD && (IAD->len > 1)) {
PrintAndLogEx(NORMAL, "\n* * Issuer Application Data (IAD):");
uint8_t VDDlen = IAD->value[0]; // Visa discretionary data length
uint8_t IDDlen = 0; // Issuer discretionary data length
PrintAndLogEx(NORMAL, "IAD length: %d", IAD->len);
PrintAndLogEx(NORMAL, "VDDlen: %d", VDDlen);
if (VDDlen < IAD->len - 1)
IDDlen = IAD->value[VDDlen + 1];
PrintAndLogEx(NORMAL, "IDDlen: %d", IDDlen);
uint8_t DerivKeyIndex = IAD->value[1];
CryptoVersion = IAD->value[2];
PrintAndLogEx(NORMAL, "CryptoVersion: %d", CryptoVersion);
PrintAndLogEx(NORMAL, "DerivKeyIndex: %d", DerivKeyIndex);
// Card Verification Results (CVR) decode
if ((VDDlen - 2) > 0) {
uint8_t CVRlen = IAD->value[3];
if (CVRlen == (VDDlen - 2 - 1)) {
PrintAndLogEx(NORMAL, "CVR length: %d", CVRlen);
PrintAndLogEx(NORMAL, "CVR: %s", sprint_hex(&IAD->value[4], CVRlen));
} else {
PrintAndLogEx(NORMAL, "Wrong CVR length! CVR: %s", sprint_hex(&IAD->value[3], VDDlen - 2));
}
}
if (IDDlen)
PrintAndLogEx(NORMAL, "IDD: %s", sprint_hex(&IAD->value[VDDlen + 1], IDDlen));
} else {
PrintAndLogEx(NORMAL, "Issuer Application Data (IAD) not found.");
}
PrintAndLogEx(NORMAL, "\n* * Processing online request");
// authorization response code from acquirer // authorization response code from acquirer
const char HostResponse[] = "00"; // 0x3030 const char HostResponse[] = "00"; // 0x3030
PrintAndLogEx(NORMAL, "* * Host Response: `%s`", HostResponse); size_t HostResponseLen = sizeof(HostResponse) - 1;
tlvdb_change_or_add_node(tlvRoot, 0x8a, sizeof(HostResponse) - 1, (const unsigned char *)HostResponse); PrintAndLogEx(NORMAL, "Host Response: `%s`", HostResponse);
tlvdb_change_or_add_node(tlvRoot, 0x8a, HostResponseLen, (const unsigned char *)HostResponse);
if (CryptoVersion == 10) {
PrintAndLogEx(NORMAL, "\n* * Generate ARPC");
// Application Cryptogram (AC)
const struct tlv *AC = tlvdb_get(tlvRoot, 0x9f26, NULL);
if (AC && (AC->len > 0)) {
PrintAndLogEx(NORMAL, "AC: %s", sprint_hex(AC->value, AC->len));
size_t rawARPClen = AC->len;
uint8_t rawARPC[rawARPClen];
memcpy(rawARPC, AC->value, AC->len);
for (int i = 0; (i < HostResponseLen) && (i < rawARPClen); i++)
rawARPC[i] ^= HostResponse[i];
PrintAndLogEx(NORMAL, "raw ARPC: %s", sprint_hex(rawARPC, rawARPClen));
// here must be calculation of ARPC, but we dont know a bank keys.
PrintAndLogEx(NORMAL, "ARPC: n/a");
} else {
PrintAndLogEx(NORMAL, "Application Cryptogram (AC) not found.");
}
// here must be external authenticate, but we dont know ARPC
}
// needs to send AC2 command (res == ARQC)
if ((CID & EMVAC_AC_MASK) == EMVAC_ARQC) {
PrintAndLogEx(NORMAL, "\n* * Calc CDOL2");
struct tlv *cdol2_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x8d, NULL), tlvRoot, 0x01); // 0x01 - dummy tag
if (!cdol2_data_tlv) {
PrintAndLogEx(WARNING, "Error: can't create CDOL2 TLV.");
dreturn(6);
}
PrintAndLogEx(NORMAL, "CDOL2 data[%d]: %s", cdol2_data_tlv->len, sprint_hex(cdol2_data_tlv->value, cdol2_data_tlv->len));
//PrintAndLogEx(NORMAL, "* * AC2");
// here must be AC2, but we dont make external authenticate (
/* // AC2
PRINT_INDENT(level);
if ((CID & EMVAC_AC2_MASK) == EMVAC_AAC2) fprintf(f, "\tAC2: AAC (Transaction declined)\n");
if ((CID & EMVAC_AC2_MASK) == EMVAC_TC2) fprintf(f, "\tAC2: TC (Transaction approved)\n");
if ((CID & EMVAC_AC2_MASK) == EMVAC_ARQC2) fprintf(f, "\tAC2: not requested (ARQC)\n");
if ((CID & EMVAC_AC2_MASK) == EMVAC_AC2_MASK) fprintf(f, "\tAC2: RFU\n");
*/
}
} }
@ -1345,12 +1443,6 @@ int CmdEMVScan(const char *cmd) {
SetAPDULogging(showAPDU); SetAPDULogging(showAPDU);
// TODO
if (channel == ECC_CONTACT) {
PrintAndLogEx(ERR, "Do not use contact interface. Exit.");
return 1;
}
// current path + file name // current path + file name
if (!strstr(crelfname, ".json")) if (!strstr(crelfname, ".json"))
strcat(crelfname, ".json"); strcat(crelfname, ".json");
@ -1376,6 +1468,9 @@ int CmdEMVScan(const char *cmd) {
// drop field at start // drop field at start
DropFieldEx( channel ); DropFieldEx( channel );
JsonSaveStr(root, "$.File.Created", "proxmark3 `emv scan`");
if (channel == ECC_CONTACTLESS) {
// iso 14443 select // iso 14443 select
PrintAndLogEx(NORMAL, "--> GET UID, ATS."); PrintAndLogEx(NORMAL, "--> GET UID, ATS.");
@ -1383,14 +1478,30 @@ int CmdEMVScan(const char *cmd) {
if (Hf14443_4aGetCardData(&card)) { if (Hf14443_4aGetCardData(&card)) {
return 2; return 2;
} }
if (!card.uidlen) {
PrintAndLogEx(ERR, "get ATS error");
return 2;
}
JsonSaveStr(root, "$.File.Created", "proxmark3 `emv scan`"); JsonSaveStr(root, "$.Card.Contactless.Communication", "iso14443-4a");
JsonSaveBufAsHex(root, "$.Card.Contactless.UID", (uint8_t *)&card.uid, card.uidlen);
JsonSaveHex(root, "$.Card.Contactless.ATQA", card.atqa[0] + (card.atqa[1] << 2), 2);
JsonSaveHex(root, "$.Card.Contactless.SAK", card.sak, 0);
JsonSaveBufAsHex(root, "$.Card.Contactless.ATS", (uint8_t *)card.ats, card.ats_len);
} else {
PrintAndLogEx(NORMAL, "--> GET ATR.");
JsonSaveStr(root, "$.Card.Communication", "iso14443-4a"); smart_card_atr_t ccard;
JsonSaveBufAsHex(root, "$.Card.UID", (uint8_t *)&card.uid, card.uidlen); smart_getATR(&ccard);
JsonSaveHex(root, "$.Card.ATQA", card.atqa[0] + (card.atqa[1] << 2), 2);
JsonSaveHex(root, "$.Card.SAK", card.sak, 0); if (!ccard.atr_len) {
JsonSaveBufAsHex(root, "$.Card.ATS", (uint8_t *)card.ats, card.ats_len); PrintAndLogEx(ERR, "get ATR error");
return 2;
}
JsonSaveStr(root, "$.Card.Contact.Communication", "iso7816");
JsonSaveBufAsHex(root, "$.Card.Contact.ATR", (uint8_t *)ccard.atr, ccard.atr_len);
}
// init applets list tree // init applets list tree
const char *al = "Applets list"; const char *al = "Applets list";

View file

@ -588,3 +588,9 @@ bool tlv_get_int(const struct tlv *etlv, int *value)
} }
return false; return false;
} }
bool tlvdb_get_uint8(struct tlvdb *tlvRoot, tlv_tag_t tag, uint8_t *value)
{
const struct tlv *tlvelm = tlvdb_get(tlvRoot, tag, NULL);
return tlv_get_uint8(tlvelm, value);
}

View file

@ -65,7 +65,6 @@ bool tlv_equal(const struct tlv *a, const struct tlv *b);
bool tlv_get_uint8(const struct tlv *etlv, uint8_t *value); bool tlv_get_uint8(const struct tlv *etlv, uint8_t *value);
bool tlv_get_int(const struct tlv *etlv, int *value); bool tlv_get_int(const struct tlv *etlv, int *value);
bool tlv_get_uint8(const struct tlv *etlv, uint8_t *value); bool tlvdb_get_uint8(struct tlvdb *tlvRoot, tlv_tag_t tag, uint8_t *value);
bool tlv_get_int(const struct tlv *etlv, int *value);
#endif #endif