diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index f033ad7ba..e2988e1e7 100644 --- a/client/emv/cmdemv.c +++ b/client/emv/cmdemv.c @@ -1182,17 +1182,17 @@ int CmdEMVExec(const char *cmd) { PrintAndLogEx(NORMAL, "\n--> VSDC transaction."); PrintAndLogEx(NORMAL, "* * Calc CDOL1"); - struct tlv *cdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x8c, NULL), tlvRoot, 0x01); // 0x01 - dummy tag - if (!cdol_data_tlv) { + struct tlv *cdol1_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x8c, NULL), tlvRoot, 0x01); // 0x01 - dummy tag + if (!cdol1_data_tlv) { PrintAndLogEx(WARNING, "Error: can't create CDOL1 TLV."); dreturn(6); } - PrintAndLogEx(NORMAL, "CDOL1 data[%d]: %s", cdol_data_tlv->len, sprint_hex(cdol_data_tlv->value, cdol_data_tlv->len)); + PrintAndLogEx(NORMAL, "CDOL1 data[%d]: %s", cdol1_data_tlv->len, sprint_hex(cdol1_data_tlv->value, cdol1_data_tlv->len)); PrintAndLogEx(NORMAL, "* * AC1"); // EMVAC_TC + EMVAC_CDAREQ --- to get SDAD - res = EMVAC(channel, true, (TrType == TT_CDA) ? EMVAC_TC + EMVAC_CDAREQ : EMVAC_TC, (uint8_t *)cdol_data_tlv->value, cdol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot); + res = EMVAC(channel, true, (TrType == TT_CDA) ? EMVAC_TC + EMVAC_CDAREQ : EMVAC_TC, (uint8_t *)cdol1_data_tlv->value, cdol1_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot); if (res) { PrintAndLogEx(NORMAL, "AC1 error(%d): %4x. Exit...", res, sw); @@ -1201,14 +1201,111 @@ int CmdEMVExec(const char *cmd) { // process Format1 (0x80) anf print Format2 (0x77) ProcessACResponseFormat1(tlvRoot, buf, len, decodeTLV); + + 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\n"); + PrintAndLogEx(NORMAL, "\n* * Processing online request"); // authorization response code from acquirer const char HostResponse[] = "00"; // 0x3030 - PrintAndLogEx(NORMAL, "* * Host Response: `%s`", HostResponse); - tlvdb_change_or_add_node(tlvRoot, 0x8a, sizeof(HostResponse) - 1, (const unsigned char *)HostResponse); + size_t HostResponseLen = sizeof(HostResponse) - 1; + 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"); +*/ + } } diff --git a/client/emv/emv_tags.h b/client/emv/emv_tags.h index 246fc72d7..f7d9a8476 100644 --- a/client/emv/emv_tags.h +++ b/client/emv/emv_tags.h @@ -25,6 +25,10 @@ # define EMVAC_TC 0x40 # define EMVAC_ARQC 0x80 # define EMVAC_CDAREQ 0x10 +# define EMVAC_AC2_MASK 0x30 +# define EMVAC_AAC2 0x00 +# define EMVAC_TC2 0x10 +# define EMVAC_ARQC2 0x20 // CID # define EMVCID_ADVICE 0x08 diff --git a/client/emv/emvcore.c b/client/emv/emvcore.c index ae3b0574a..7af08690c 100644 --- a/client/emv/emvcore.c +++ b/client/emv/emvcore.c @@ -263,7 +263,6 @@ int EMVExchangeEx(EMVCommandChannel channel, bool ActivateField, bool LeaveField break; case ECC_CONTACT: #ifdef WITH_SMARTCARD - //int ExchangeAPDUSC(uint8_t *datain, int datainlen, bool activateCard, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen); res = ExchangeAPDUSC(data, (IncludeLe?6:5) + apdu.Lc, ActivateField, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen); #else res = 1; @@ -799,7 +798,7 @@ int trDDA(EMVCommandChannel channel, bool decodeTLV, struct tlvdb *tlv) { return 4; } - PrintAndLogEx(NORMAL, "\n* Calc DDOL"); + PrintAndLogEx(NORMAL, "\n* * Calc DDOL"); const struct tlv *ddol_tlv = tlvdb_get(tlv, 0x9f49, NULL); if (!ddol_tlv) { ddol_tlv = &default_ddol_tlv; @@ -817,7 +816,7 @@ int trDDA(EMVCommandChannel channel, bool decodeTLV, struct tlvdb *tlv) { PrintAndLogEx(NORMAL, "DDOL data[%d]: %s", ddol_data_tlv->len, sprint_hex(ddol_data_tlv->value, ddol_data_tlv->len)); - PrintAndLogEx(NORMAL, "\n* Internal Authenticate"); + PrintAndLogEx(NORMAL, "\n* * Internal Authenticate"); int res = EMVInternalAuthenticate(channel, true, (uint8_t *)ddol_data_tlv->value, ddol_data_tlv->len, buf, sizeof(buf), &len, &sw, NULL); if (res) { PrintAndLogEx(WARNING, "Internal Authenticate error(%d): %4x. Exit...", res, sw); @@ -844,7 +843,7 @@ int trDDA(EMVCommandChannel channel, bool decodeTLV, struct tlvdb *tlv) { tlvdb_free(t80); if (decodeTLV){ - PrintAndLogEx(NORMAL, "* * Decode response format 1:"); + PrintAndLogEx(NORMAL, "* * * Decode response format 1:"); TLVPrintFromTLV(dda_db); } } @@ -879,12 +878,12 @@ int trDDA(EMVCommandChannel channel, bool decodeTLV, struct tlvdb *tlv) { // 9f4c ICC Dynamic Number const struct tlv *idn_tlv = tlvdb_get(idn_db, 0x9f4c, NULL); if(idn_tlv) { - PrintAndLogEx(NORMAL, "\nIDN (ICC Dynamic Number) [%zu] %s", idn_tlv->len, sprint_hex_inrow(idn_tlv->value, idn_tlv->len)); - PrintAndLogEx(NORMAL, "DDA verified OK."); + PrintAndLogEx(INFO, "\nIDN (ICC Dynamic Number) [%zu] %s", idn_tlv->len, sprint_hex_inrow(idn_tlv->value, idn_tlv->len)); + PrintAndLogEx(INFO, "DDA verified OK."); tlvdb_add(tlv, idn_db); tlvdb_free(idn_db); } else { - PrintAndLogEx(NORMAL, "\nERROR: DDA verify error"); + PrintAndLogEx(ERR, "\nDDA verify error"); tlvdb_free(idn_db); emv_pk_free(pk); diff --git a/client/emv/emvcore.h b/client/emv/emvcore.h index 7d53e83bb..9aaaf2e6f 100644 --- a/client/emv/emvcore.h +++ b/client/emv/emvcore.h @@ -39,7 +39,7 @@ typedef enum { enum TransactionType { TT_MSD, - TT_VSDC, // not standart for contactless!!!! + TT_VSDC, // contact only. not standart for contactless TT_QVSDCMCHIP, TT_CDA, }; diff --git a/client/emv/tlv.c b/client/emv/tlv.c index 9722c9311..0daedaf47 100644 --- a/client/emv/tlv.c +++ b/client/emv/tlv.c @@ -469,7 +469,10 @@ const struct tlv *tlvdb_get_inchild(const struct tlvdb *tlvdb, tlv_tag_t tag, co } const struct tlv *tlvdb_get_tlv(const struct tlvdb *tlvdb) { - return &tlvdb->tag; + if (tlvdb) + return &tlvdb->tag; + else + return NULL; } unsigned char *tlv_encode(const struct tlv *tlv, size_t *len) @@ -546,6 +549,12 @@ struct tlvdb *tlvdb_elm_get_parent(struct tlvdb *tlvdb) return tlvdb->parent; } +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); +} + bool tlv_get_uint8(const struct tlv *etlv, uint8_t *value) { *value = 0; diff --git a/client/emv/tlv.h b/client/emv/tlv.h index 1f52e440b..e75bbf986 100644 --- a/client/emv/tlv.h +++ b/client/emv/tlv.h @@ -65,4 +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_int(const struct tlv *etlv, int *value); +bool tlvdb_get_uint8(struct tlvdb *tlvRoot, tlv_tag_t tag, uint8_t *value); + #endif