WaitMS(10);
}
-// Wait max 300ms or until SCL goes LOW.
+// Wait max 1800ms or until SCL goes LOW.
+// It timeout reading response from card
// Which ever comes first
-static bool WaitSCL_L_300ms(void) {
- volatile uint16_t delay = 310;
+bool WaitSCL_L_timeout(void){
+ volatile uint16_t delay = 1800;
while ( delay-- ) {
// exit on SCL LOW
if (!SCL_read)
}
static bool I2C_WaitForSim() {
- // variable delay here.
- if (!WaitSCL_L_300ms())
+ // wait for data from card
+ if (!WaitSCL_L_timeout())
return false;
// 8051 speaks with smart card.
}
if (*responselen < 0 ) {
- return 2;
+ return 1;
} else {
return 0;
}
TLV_ADD(0x9F6A, "\x01\x02\x03\x04");
//9F66:(Terminal Transaction Qualifiers (TTQ)) len:4
TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC
+ //95:(Terminal Verification Results) len:5
+ // all OK TVR
+ TLV_ADD(0x95, "\x00\x00\x00\x00\x00");
+}
+
+void PrintChannel(EMVCommandChannel channel) {
+ switch(channel) {
+ case ECC_CONTACTLESS:
+ PrintAndLogEx(INFO, "Channel: CONTACTLESS");
+ break;
+ case ECC_CONTACT:
+ PrintAndLogEx(INFO, "Channel: CONTACT");
+ break;
+ }
}
int CmdEMVSelect(const char *cmd) {
#ifdef WITH_SMARTCARD
if (arg_get_lit(5))
channel = ECC_CONTACT;
+ PrintChannel(channel);
CLIGetHexWithReturn(6, data, &datalen);
#else
CLIGetHexWithReturn(5, data, &datalen);
if (arg_get_lit(5))
channel = ECC_CONTACT;
#endif
+ PrintChannel(channel);
CLIParserFree();
SetAPDULogging(APDULogging);
if (arg_get_lit(7))
channel = ECC_CONTACT;
#endif
+ PrintChannel(channel);
CLIParserFree();
SetAPDULogging(APDULogging);
#else
CLIGetHexWithReturn(6, data, &datalen);
#endif
+ PrintChannel(channel);
CLIParserFree();
SetAPDULogging(APDULogging);
#else
CLIGetHexWithReturn(4, data, &datalen);
#endif
+ PrintChannel(channel);
CLIParserFree();
if (datalen != 2) {
#else
CLIGetHexWithReturn(8, data, &datalen);
#endif
+ PrintChannel(channel);
CLIParserFree();
SetAPDULogging(APDULogging);
if (arg_get_lit(3))
channel = ECC_CONTACT;
#endif
+ PrintChannel(channel);
CLIParserFree();
SetAPDULogging(APDULogging);
#else
CLIGetHexWithReturn(6, data, &datalen);
#endif
+ PrintChannel(channel);
CLIParserFree();
SetAPDULogging(APDULogging);
}
}
+void ProcessACResponseFormat1(struct tlvdb *tlvRoot, uint8_t *buf, size_t len, bool decodeTLV) {
+ if (buf[0] == 0x80) {
+ if (decodeTLV){
+ PrintAndLog("GPO response format1:");
+ TLVPrintFromBuffer(buf, len);
+ }
+
+ uint8_t elmlen = len - 2; // wo 0x80XX
+
+ if (len < 4 + 2 || (elmlen - 2) % 4 || elmlen != buf[1]) {
+ PrintAndLogEx(ERR, "GPO response format1 parsing error. length=%d", len);
+ } else {
+ struct tlvdb *tlvElm = NULL;
+ if (decodeTLV)
+ PrintAndLog("\n------------ Format1 decoded ------------");
+
+ // CID (Cryptogram Information Data)
+ tlvdb_change_or_add_node_ex(tlvRoot, 0x9f27, 1, &buf[2], &tlvElm);
+ if (decodeTLV)
+ TLVPrintFromTLV(tlvElm);
+
+ // ATC (Application Transaction Counter)
+ tlvdb_change_or_add_node_ex(tlvRoot, 0x9f36, 2, &buf[3], &tlvElm);
+ if (decodeTLV)
+ TLVPrintFromTLV(tlvElm);
+
+ // AC (Application Cryptogram)
+ tlvdb_change_or_add_node_ex(tlvRoot, 0x9f26, MIN(8, elmlen - 3), &buf[5], &tlvElm);
+ if (decodeTLV)
+ TLVPrintFromTLV(tlvElm);
+
+ // IAD (Issuer Application Data) - optional
+ if (len > 11 + 2) {
+ tlvdb_change_or_add_node_ex(tlvRoot, 0x9f10, elmlen - 11, &buf[13], &tlvElm);
+ if (decodeTLV)
+ TLVPrintFromTLV(tlvElm);
+ }
+ }
+ } else {
+ if (decodeTLV)
+ TLVPrintFromBuffer(buf, len);
+ }
+}
+
int CmdEMVExec(const char *cmd) {
uint8_t buf[APDU_RESPONSE_LEN] = {0};
size_t len = 0;
if (arg_get_lit(11))
channel = ECC_CONTACT;
#endif
+ PrintChannel(channel);
uint8_t psenum = (channel == ECC_CONTACT) ? 1 : 2;
char *PSE_or_PPSE = psenum == 1 ? "PSE" : "PPSE";
uint8_t SFIend = AFL->value[i * 4 + 2];
uint8_t SFIoffline = AFL->value[i * 4 + 3];
- PrintAndLogEx(NORMAL, "* * SFI[%02x] start:%02x end:%02x offline:%02x", SFI, SFIstart, SFIend, SFIoffline);
+ PrintAndLogEx(NORMAL, "* * SFI[%02x] start:%02x end:%02x offline count:%02x", SFI, SFIstart, SFIend, SFIoffline);
if (SFI == 0 || SFI == 31 || SFIstart == 0 || SFIstart > SFIend) {
PrintAndLogEx(NORMAL, "SFI ERROR! Skipped...");
continue;
// Build Input list for Offline Data Authentication
// EMV 4.3 book3 10.3, page 96
- if (SFIoffline) {
+ if (SFIoffline > 0) {
if (SFI < 11) {
const unsigned char *abuf = buf;
size_t elmlen = len;
memcpy(&ODAiList[ODAiListLen], buf, len);
ODAiListLen += len;
}
+
+ SFIoffline--;
}
}
}
}
}
+ // VSDC
+ if (GetCardPSVendor(AID, AIDlen) == CV_VISA && (TrType == TT_VSDC || TrType == TT_CDA)){
+ 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) {
+ 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, "* * 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);
+
+ if (res) {
+ PrintAndLogEx(NORMAL, "AC1 error(%d): %4x. Exit...", res, sw);
+ dreturn(7);
+ }
+
+ // process Format1 (0x80) and print Format2 (0x77)
+ ProcessACResponseFormat1(tlvRoot, buf, len, decodeTLV);
+
+ PrintAndLogEx(NORMAL, "\n* * Processing online request\n");
+
+ // authorization response code from acquirer
+ const char HostResponse[] = "00"; //0 x3030
+ PrintAndLogEx(NORMAL, "* * Host Response: `%s`", HostResponse);
+ tlvdb_change_or_add_node(tlvRoot, 0x8a, sizeof(HostResponse) - 1, (const unsigned char *)HostResponse);
+
+ }
+
if (channel == ECC_CONTACTLESS) {
DropField();
}
#else
CLIGetStrWithReturn(11, relfname, &relfnamelen);
#endif
+ PrintChannel(channel);
uint8_t psenum = (channel == ECC_CONTACT) ? 1 : 2;
CLIParserFree();
CLIParserInit("emv roca",
"Tries to extract public keys and run the ROCA test against them.\n",
"Usage:\n"
- "\temv roca -w -> select CONTACT card and run test\n\temv roca -> select CONTACTLESS card and run test\n");
+ "\temv roca -w -> select --CONTACT-- card and run test\n"
+ "\temv roca -> select --CONTACTLESS-- card and run test\n"
+ );
void* argtable[] = {
arg_param_begin,
+ arg_lit0("tT", "selftest", "self test"),
arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."),
arg_param_end
};
EMVCommandChannel channel = ECC_CONTACTLESS;
if (arg_get_lit(1))
+ return roca_self_test();
+#ifdef WITH_SMARTCARD
+ if (arg_get_lit(2))
channel = ECC_CONTACT;
+#endif
+ PrintChannel(channel);
// select card
uint8_t psenum = (channel == ECC_CONTACT) ? 1 : 2;
+ char *PSE_or_PPSE = psenum == 1 ? "PSE" : "PPSE";
SetAPDULogging(false);
const char *al = "Applets list";
struct tlvdb *tlvSelect = tlvdb_fixed(1, strlen(al), (const unsigned char *)al);
- // EMV PPSE
- PrintAndLogEx(NORMAL, "--> PPSE.");
- res = EMVSearchPSE(channel, false, true, psenum, false, tlvSelect);
+ // EMV PSE/PPSE
+ PrintAndLogEx(NORMAL, "--> %s.", PSE_or_PPSE);
+ res = EMVSearchPSE(channel, true, true, psenum, false, tlvSelect);
- // check PPSE and select application id
+ // check PSE/PPSE and select application id
if (!res) {
TLVPrintAIDlistFromSelectTLV(tlvSelect);
} else {
// EMV SEARCH with AID list
PrintAndLogEx(NORMAL, "--> AID search.");
if (EMVSearch(channel, false, true, false, tlvSelect)) {
- PrintAndLogEx(ERR, "Can't found any of EMV AID. Exit...");
+ PrintAndLogEx(ERR, "Couldn't find any known EMV AID. Exit...");
tlvdb_free(tlvSelect);
DropField();
return 3;
}
// EMV SELECT application
- SetAPDULogging(false);
+ SetAPDULogging(true);
EMVSelectApplication(tlvSelect, AID, &AIDlen);
tlvdb_free(tlvSelect);
uint8_t msgtype,
size_t *len,
const struct tlv *cert_tlv,
- ... /* A list of tlv pointers, end with NULL */
+ int tlv_count,
+ ... /* A list of tlv pointers */
)
{
struct crypto_pk *kcp;
size_t hash_len = crypto_hash_get_size(ch);
crypto_hash_write(ch, data + 1, data_len - 2 - hash_len);
- va_start(vl, cert_tlv);
- while (true) {
+ va_start(vl, tlv_count);
+ for (int i = 0; i < tlv_count; i++) {
const struct tlv *add_tlv = va_arg(vl, const struct tlv *);
if (!add_tlv)
- break;
+ continue;
crypto_hash_write(ch, add_tlv->value, add_tlv->len);
}
va_end(vl);
- if (memcmp(data + data_len - 1 - hash_len, crypto_hash_read(ch), hash_len)) {
+ uint8_t hash[hash_len];
+ memset(hash, 0, hash_len);
+ memcpy(hash, crypto_hash_read(ch), hash_len);
+ if (memcmp(data + data_len - 1 - hash_len, hash, hash_len)) {
printf("ERROR: Calculated wrong hash\n");
printf("decoded: %s\n",sprint_hex(data + data_len - 1 - hash_len, hash_len));
- printf("calculated: %s\n",sprint_hex(crypto_hash_read(ch), hash_len));
+ printf("calculated: %s\n",sprint_hex(hash, hash_len));
if (strictExecution) {
crypto_hash_close(ch);
const struct tlv *exp_tlv,
const struct tlv *rem_tlv,
const struct tlv *add_tlv,
+ const struct tlv *sdatl_tlv,
bool showData
)
{
data = emv_pki_decode_message(enc_pk, msgtype, &data_len,
cert_tlv,
+ 5,
rem_tlv,
exp_tlv,
add_tlv,
+ sdatl_tlv,
NULL);
if (!data || data_len < 11 + pan_length) {
printf("ERROR: Can't decode message\n");
const struct tlv *cert_tlv,
const struct tlv *exp_tlv,
const struct tlv *rem_tlv,
- const struct tlv *add_tlv
+ const struct tlv *add_tlv,
+ const struct tlv *sdatl_tlv
) {
- return emv_pki_decode_key_ex(enc_pk, msgtype, pan_tlv, cert_tlv, exp_tlv, rem_tlv, add_tlv, false);
+ return emv_pki_decode_key_ex(enc_pk, msgtype, pan_tlv, cert_tlv, exp_tlv, rem_tlv, add_tlv, sdatl_tlv, false);
}
struct emv_pk *emv_pki_recover_issuer_cert(const struct emv_pk *pk, struct tlvdb *db)
tlvdb_get(db, 0x90, NULL),
tlvdb_get(db, 0x9f32, NULL),
tlvdb_get(db, 0x92, NULL),
+ NULL,
NULL);
}
struct emv_pk *emv_pki_recover_icc_cert(const struct emv_pk *pk, struct tlvdb *db, const struct tlv *sda_tlv)
{
- return emv_pki_decode_key(pk, 4,
+ size_t sdatl_len;
+ unsigned char *sdatl = emv_pki_sdatl_fill(db, &sdatl_len);
+ struct tlv sda_tdata = {
+ .tag = 0x00, // dummy tag
+ .len = sdatl_len,
+ .value = sdatl
+ };
+
+ struct emv_pk *res = emv_pki_decode_key(pk, 4,
tlvdb_get(db, 0x5a, NULL),
tlvdb_get(db, 0x9f46, NULL),
tlvdb_get(db, 0x9f47, NULL),
tlvdb_get(db, 0x9f48, NULL),
- sda_tlv);
+ sda_tlv,
+ &sda_tdata);
+
+ free(sdatl); // malloc here: emv_pki_sdatl_fill
+ return res;
}
struct emv_pk *emv_pki_recover_icc_pe_cert(const struct emv_pk *pk, struct tlvdb *db)
tlvdb_get(db, 0x9f2d, NULL),
tlvdb_get(db, 0x9f2e, NULL),
tlvdb_get(db, 0x9f2f, NULL),
+ NULL,
NULL);
}
+unsigned char *emv_pki_sdatl_fill(const struct tlvdb *db, size_t *sdatl_len) {
+ uint8_t buf[2048] = {0};
+ size_t len = 0;
+
+ *sdatl_len = 0;
+
+ const struct tlv *sda_tl = tlvdb_get(db, 0x9f4a, NULL);
+ if (!sda_tl || sda_tl->len <= 0)
+ return NULL;
+
+ for (int i = 0; i < sda_tl->len; i++) {
+ uint32_t tag = sda_tl->value[i]; // here may be multibyte, but now not
+ const struct tlv *elm = tlvdb_get(db, tag, NULL);
+ if (elm) {
+ memcpy(&buf[len], elm->value, elm->len);
+ len += elm->len;
+ }
+ }
+
+ if (len) {
+ *sdatl_len = len;
+ unsigned char *value = malloc(len);
+ memcpy(value, buf, len);
+ return value;
+ }
+
+ return NULL;
+}
+
+
struct tlvdb *emv_pki_recover_dac_ex(const struct emv_pk *enc_pk, const struct tlvdb *db, const struct tlv *sda_tlv, bool showData)
{
size_t data_len;
+
+ // Static Data Authentication Tag List
+ size_t sdatl_len;
+ unsigned char *sdatl = emv_pki_sdatl_fill(db, &sdatl_len);
+ struct tlv sda_tdata = {
+ .tag = 0x00, // dummy tag
+ .len = sdatl_len,
+ .value = sdatl
+ };
+
unsigned char *data = emv_pki_decode_message(enc_pk, 3, &data_len,
tlvdb_get(db, 0x93, NULL),
+ 3,
sda_tlv,
+ &sda_tdata,
NULL);
+ free(sdatl); // malloc here: emv_pki_sdatl_fill
+
if (!data || data_len < 5)
return NULL;
size_t data_len;
unsigned char *data = emv_pki_decode_message(enc_pk, 5, &data_len,
tlvdb_get(db, 0x9f4b, NULL),
+ 2,
dyn_tlv,
NULL);
size_t data_len;
unsigned char *data = emv_pki_decode_message(enc_pk, 5, &data_len,
tlvdb_get(db, 0x9f4b, NULL),
+ 5,
tlvdb_get(db, 0x9f37, NULL),
tlvdb_get(db, 0x9f02, NULL),
tlvdb_get(db, 0x5f2a, NULL),
size_t data_len = 0;
unsigned char *data = emv_pki_decode_message(enc_pk, 5, &data_len,
tlvdb_get(this_db, 0x9f4b, NULL),
+ 2,
un_tlv,
NULL);
if (!data || data_len < 3) {
extern void PKISetStrictExecution(bool se);
+unsigned char *emv_pki_sdatl_fill(const struct tlvdb *db, size_t *sdatl_len);
struct emv_pk *emv_pki_recover_issuer_cert(const struct emv_pk *pk, struct tlvdb *db);
struct emv_pk *emv_pki_recover_icc_cert(const struct emv_pk *pk, struct tlvdb *db, const struct tlv *sda_tlv);
struct emv_pk *emv_pki_recover_icc_pe_cert(const struct emv_pk *pk, struct tlvdb *db);
#include <stdbool.h>
#include "ui.h"
+#include "util.h"
#include "mbedtls/bignum.h"
return ret;
}
-int roca_self_test( int verbose ) {
+int roca_self_test( void ) {
int ret = 0;
- if( verbose != 0 )
- printf( "\nROCA check vulnerability tests\n" );
+ PrintAndLogEx(INFO, "ROCA check vulnerability tests" );
// positive
uint8_t keyp[] = "\x94\x4e\x13\x20\x8a\x28\x0c\x37\xef\xc3\x1c\x31\x14\x48\x5e\x59"\
"\x27\x83\x30\xd3\xf4\x71\xa2\x53\x8f\xa6\x67\x80\x2e\xd2\xa3\xc4"\
"\x4a\x8b\x7d\xea\x82\x6e\x88\x8d\x0a\xa3\x41\xfd\x66\x4f\x7f\xa7";
- if( verbose != 0 )
- printf( " ROCA positive test: " );
if (emv_rocacheck(keyp, 64, false)) {
- if( verbose != 0 )
- printf( "passed\n" );
- } else {
- ret = 1;
- if( verbose != 0 )
- printf( "failed\n" );
+ PrintAndLogEx(SUCCESS, "Weak modulus [ %s]", _GREEN_(PASS) );
+ }
+ else {
+ ret++;
+ PrintAndLogEx(FAILED, "Weak modulus [ %s]", _RED_(FAIL) );
}
// negative
"\x27\x83\x30\xd3\xf4\x71\xa2\x53\x8f\xa6\x67\x80\x2e\xd2\xa3\xc4"\
"\x4a\x8b\x7d\xea\x82\x6e\x88\x8d\x0a\xa3\x41\xfd\x66\x4f\x7f\xa7";
- if( verbose != 0 )
- printf( " ROCA negative test: " );
-
if (emv_rocacheck(keyn, 64, false)) {
- ret = 1;
- if( verbose != 0 )
- printf( "failed\n" );
+ ret++;
+ PrintAndLogEx(FAILED, "Strong modulus [ %s]", _RED_(FAIL) );
} else {
- if( verbose != 0 )
- printf( "passed\n" );
+ PrintAndLogEx(SUCCESS, "Strong modulus [ %s]", _GREEN_(PASS) );
}
-
return ret;
}
#define ROCA_PRINTS_LENGTH 17
extern bool emv_rocacheck( const unsigned char *buf, size_t buflen, bool verbose );
-extern int roca_self_test( int verbose );
+extern int roca_self_test( void );
#endif
return res;
}
+
+int EMVSelectWithRetry(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, uint8_t *AID, size_t AIDLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) {
+ int retrycnt = 0;
+ int res = 0;
+ do {
+ res = EMVSelect(channel, false, true, AID, AIDLen, Result, MaxResultLen, ResultLen, sw, tlv);
+
+ // retry if error and not returned sw error
+ if (res && res != 5) {
+ if (++retrycnt < 3){
+ continue;
+ } else {
+ // card select error, proxmark error
+ if (res == 1) {
+ PrintAndLogEx(WARNING, "Exit...");
+ return 1;
+ }
+
+ retrycnt = 0;
+ PrintAndLogEx(NORMAL, "Retry failed [%s]. Skiped...", sprint_hex_inrow(AID, AIDLen));
+ return res;
+ }
+ }
+ } while (res && res != 5);
+
+ return res;
+}
+
+
+int EMVCheckAID(EMVCommandChannel channel, bool decodeTLV, struct tlvdb *tlvdbelm, struct tlvdb *tlv){
+ uint8_t data[APDU_RESPONSE_LEN] = {0};
+ size_t datalen = 0;
+ int res = 0;
+ uint16_t sw = 0;
+
+ while (tlvdbelm) {
+ const struct tlv *tgAID = tlvdb_get_inchild(tlvdbelm, 0x4f, NULL);
+ if (tgAID) {
+ res = EMVSelectWithRetry(channel, false, true, (uint8_t *)tgAID->value, tgAID->len, data, sizeof(data), &datalen, &sw, tlv);
+
+ // if returned sw error
+ if (res == 5) {
+ // next element
+ tlvdbelm = tlvdb_find_next(tlvdbelm, 0x61);
+ continue;
+ }
+
+ if (res)
+ break;
+
+ // all is ok
+ if (decodeTLV){
+ PrintAndLogEx(NORMAL, "%s:", sprint_hex_inrow(tgAID->value, tgAID->len));
+ TLVPrintFromBuffer(data, datalen);
+ }
+ }
+ tlvdbelm = tlvdb_find_next(tlvdbelm, 0x61);
+ }
+ return res;
+}
+
+
int EMVSearchPSE(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, uint8_t PSENum, bool decodeTLV, struct tlvdb *tlv) {
uint8_t data[APDU_RESPONSE_LEN] = {0};
size_t datalen = 0;
+ uint8_t sfidata[0x11][APDU_RESPONSE_LEN] = {0};
+ size_t sfidatalen[0x11] = {0};
uint16_t sw = 0;
int res;
+ bool fileFound = false;
char *PSE_or_PPSE = PSENum == 1 ? "PSE" : "PPSE";
res = EMVSelectPSE(channel, ActivateField, true, PSENum, data, sizeof(data), &datalen, &sw);
if (!res){
+ if (sw != 0x9000) {
+ PrintAndLogEx(FAILED, "Select PSE error. APDU error: %04x.", sw);
+ return 1;
+ }
+
struct tlvdb *t = NULL;
t = tlvdb_parse_multi(data, datalen);
if (t) {
- int retrycnt = 0;
- struct tlvdb *ttmp = tlvdb_find_path(t, (tlv_tag_t[]){0x6f, 0xa5, 0xbf0c, 0x61, 0x00});
- if (!ttmp)
- PrintAndLogEx(FAILED, "%s doesn't have any records.", PSE_or_PPSE);
-
- while (ttmp) {
- const struct tlv *tgAID = tlvdb_get_inchild(ttmp, 0x4f, NULL);
- if (tgAID) {
- res = EMVSelect(channel, false, true, (uint8_t *)tgAID->value, tgAID->len, data, sizeof(data), &datalen, &sw, tlv);
-
- // retry if error and not returned sw error
- if (res && res != 5) {
- if (++retrycnt < 3){
- continue;
- } else {
- // card select error, proxmark error
- if (res == 1) {
- PrintAndLogEx(WARNING, "Exit...");
- return 1;
- }
-
- retrycnt = 0;
- PrintAndLogEx(NORMAL, "Retry failed [%s]. Skiped...", sprint_hex_inrow(tgAID->value, tgAID->len));
- }
-
- // next element
- ttmp = tlvdb_find_next(ttmp, 0x61);
- continue;
+ // PSE/PPSE with SFI
+ struct tlvdb *tsfi = tlvdb_find_path(t, (tlv_tag_t[]){0x6f, 0xa5, 0x88, 0x00});
+ if (tsfi) {
+ uint8_t sfin = 0;
+ tlv_get_uint8(tlvdb_get_tlv(tsfi), &sfin);
+ PrintAndLogEx(INFO, "* PPSE get SFI: 0x%02x.", sfin);
+
+ for (uint8_t ui = 0x01; ui <= 0x10; ui++) {
+ PrintAndLogEx(INFO, "* * Get SFI: 0x%02x. num: 0x%02x", sfin, ui);
+ res = EMVReadRecord(channel, true, sfin, ui, sfidata[ui], APDU_RESPONSE_LEN, &sfidatalen[ui], &sw, NULL);
+
+ // end of records
+ if (sw == 0x6a83) {
+ sfidatalen[ui] = 0;
+ PrintAndLogEx(INFO, "* * PPSE get SFI. End of records.");
+ break;
+ }
+
+ // error catch!
+ if (sw != 0x9000) {
+ sfidatalen[ui] = 0;
+ PrintAndLogEx(FAILED, "PPSE get Error. APDU error: %04x.", sw);
+ break;
}
- retrycnt = 0;
- // all is ok
if (decodeTLV){
- PrintAndLogEx(NORMAL, "%s:", sprint_hex_inrow(tgAID->value, tgAID->len));
- TLVPrintFromBuffer(data, datalen);
+ TLVPrintFromBuffer(sfidata[ui], sfidatalen[ui]);
}
}
- ttmp = tlvdb_find_next(ttmp, 0x61);
+ for (uint8_t ui = 0x01; ui <= 0x10; ui++) {
+ if (sfidatalen[ui]) {
+ struct tlvdb *tsfi = NULL;
+ tsfi = tlvdb_parse_multi(sfidata[ui], sfidatalen[ui]);
+ if (tsfi) {
+ struct tlvdb *tsfitmp = tlvdb_find_path(tsfi, (tlv_tag_t[]){0x70, 0x61, 0x00});
+ if (!tsfitmp) {
+ PrintAndLogEx(FAILED, "SFI 0x%02d doesn't have any records.", sfidatalen[ui]);
+ continue;
+ }
+ res = EMVCheckAID(channel, decodeTLV, tsfitmp, tlv);
+ fileFound = true;
+ }
+ tlvdb_free(tsfi);
+ }
+ }
}
+
+ // PSE/PPSE plain (wo SFI)
+ struct tlvdb *ttmp = tlvdb_find_path(t, (tlv_tag_t[]){0x6f, 0xa5, 0xbf0c, 0x61, 0x00});
+ if (ttmp) {
+ res = EMVCheckAID(channel, decodeTLV, ttmp, tlv);
+ fileFound = true;
+ }
+
+ if (!fileFound)
+ PrintAndLogEx(FAILED, "PPSE doesn't have any records.");
+
tlvdb_free(t);
} else {
PrintAndLogEx(WARNING, "%s ERROR: Can't get TLV from response.", PSE_or_PPSE);
struct tlvdb *dac_db = emv_pki_recover_dac(issuer_pk, tlv, sda_tlv);
if (dac_db) {
const struct tlv *dac_tlv = tlvdb_get(dac_db, 0x9f45, NULL);
- PrintAndLogEx(NORMAL, "SDA verified OK. (%02hhx:%02hhx)\n", dac_tlv->value[0], dac_tlv->value[1]);
+ PrintAndLogEx(NORMAL, "SDA verified OK. (Data Authentication Code: %02hhx:%02hhx)\n", dac_tlv->value[0], dac_tlv->value[1]);
tlvdb_add(tlv, dac_db);
} else {
emv_pk_free(issuer_pk);
}
const struct tlv *sda_tlv = tlvdb_get(tlv, 0x21, NULL);
- if (!sda_tlv || sda_tlv->len < 1) {
+/* if (!sda_tlv || sda_tlv->len < 1) { it may be 0!!!!
emv_pk_free(pk);
PrintAndLogEx(WARNING, "Error: Can't find input list for Offline Data Authentication. Exit.");
return 3;
}
-
+*/
struct emv_pk *issuer_pk = emv_pki_recover_issuer_cert(pk, tlv);
if (!issuer_pk) {
emv_pk_free(pk);
if (!icc_pk) {
emv_pk_free(pk);
emv_pk_free(issuer_pk);
- PrintAndLogEx(WARNING, "Error: ICC setrificate not found. Exit.");
+ PrintAndLogEx(WARNING, "Error: ICC certificate not found. Exit.");
return 2;
}
PrintAndLogEx(SUCCESS, "ICC PK recovered. RID %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx CSN %02hhx:%02hhx:%02hhx\n",
icc_pk->serial[2]
);
- struct emv_pk *icc_pe_pk = emv_pki_recover_icc_pe_cert(issuer_pk, tlv);
- if (!icc_pe_pk) {
- PrintAndLogEx(WARNING, "WARNING: ICC PE PK recover error. ");
+ if (tlvdb_get(tlv, 0x9f2d, NULL)) {
+ struct emv_pk *icc_pe_pk = emv_pki_recover_icc_pe_cert(issuer_pk, tlv);
+ if (!icc_pe_pk) {
+ PrintAndLogEx(WARNING, "WARNING: ICC PE PK recover error. ");
+ } else {
+ PrintAndLogEx(SUCCESS, "ICC PE PK recovered. RID %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx CSN %02hhx:%02hhx:%02hhx\n",
+ icc_pe_pk->rid[0],
+ icc_pe_pk->rid[1],
+ icc_pe_pk->rid[2],
+ icc_pe_pk->rid[3],
+ icc_pe_pk->rid[4],
+ icc_pe_pk->index,
+ icc_pe_pk->serial[0],
+ icc_pe_pk->serial[1],
+ icc_pe_pk->serial[2]
+ );
+ }
} else {
- PrintAndLogEx(SUCCESS, "ICC PE PK recovered. RID %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx CSN %02hhx:%02hhx:%02hhx\n",
- icc_pe_pk->rid[0],
- icc_pe_pk->rid[1],
- icc_pe_pk->rid[2],
- icc_pe_pk->rid[3],
- icc_pe_pk->rid[4],
- icc_pe_pk->index,
- icc_pe_pk->serial[0],
- icc_pe_pk->serial[1],
- icc_pe_pk->serial[2]
- );
+ PrintAndLogEx(INFO, "ICC PE PK (PIN Encipherment Public Key Certificate) not found.\n");
}
// 9F4B: Signed Dynamic Application Data
struct tlvdb *dac_db = emv_pki_recover_dac(issuer_pk, tlv, sda_tlv);
if (dac_db) {
const struct tlv *dac_tlv = tlvdb_get(dac_db, 0x9f45, NULL);
- PrintAndLogEx(NORMAL, "SDA verified OK. (%02hhx:%02hhx)\n", dac_tlv->value[0], dac_tlv->value[1]);
+ PrintAndLogEx(NORMAL, "SDAD verified OK. (Data Authentication Code: %02hhx:%02hhx)\n", dac_tlv->value[0], dac_tlv->value[1]);
tlvdb_add(tlv, dac_db);
} else {
PrintAndLogEx(WARNING, "Error: SSAD verify error");
if (len < 3 ) {
PrintAndLogEx(WARNING, "Error: Internal Authenticate format1 parsing error. length=%d", len);
} else {
+ // parse response 0x80
+ struct tlvdb *t80 = tlvdb_parse_multi(buf, len);
+ const struct tlv * t80tlv = tlvdb_get_tlv(t80);
+
// 9f4b Signed Dynamic Application Data
- dda_db = tlvdb_fixed(0x9f4b, len - 2, buf + 2);
+ dda_db = tlvdb_fixed(0x9f4b, t80tlv->len, t80tlv->value);
tlvdb_add(tlv, dda_db);
+
+ tlvdb_free(t80);
+
if (decodeTLV){
PrintAndLogEx(NORMAL, "* * Decode response format 1:");
TLVPrintFromTLV(dda_db);
res = exec_crypto_test(verbose);
if (res) TestFail = true;
- res = roca_self_test(verbose);
+ res = roca_self_test();
if (res) TestFail = true;
PrintAndLog("\n--------------------------");
if (TestFail)
- PrintAndLog("Test(s) [ERROR].");
+ PrintAndLogEx(FAILED, "\tTest(s) [ %s ]", _RED_(FAIL) );
else
- PrintAndLog("Tests [OK].");
+ PrintAndLogEx(SUCCESS, "\tTest(s) [ %s ]", _GREEN_(OK) );
return TestFail;
}
tlvdb->next = other;
}
-void tlvdb_change_or_add_node(struct tlvdb *tlvdb, tlv_tag_t tag, size_t len, const unsigned char *value)
+void tlvdb_change_or_add_node_ex(struct tlvdb *tlvdb, tlv_tag_t tag, size_t len, const unsigned char *value, struct tlvdb **tlvdb_elm)
{
struct tlvdb *telm = tlvdb_find_full(tlvdb, tag);
if (telm == NULL) {
// new tlv element
- tlvdb_add(tlvdb, tlvdb_fixed(tag, len, value));
+ struct tlvdb *elm = tlvdb_fixed(tag, len, value);
+ tlvdb_add(tlvdb, elm);
+ if (tlvdb_elm)
+ *tlvdb_elm = elm;
} else {
// the same tlv structure
if (telm->tag.tag == tag && telm->tag.len == len && !memcmp(telm->tag.value, value, len))
// free old element with childrens
telm->next = NULL;
tlvdb_free(telm);
+
+ if (tlvdb_elm)
+ *tlvdb_elm = tnewelm;
}
return;
}
+void tlvdb_change_or_add_node(struct tlvdb *tlvdb, tlv_tag_t tag, size_t len, const unsigned char *value)
+{
+ tlvdb_change_or_add_node_ex(tlvdb, tag, len, value, NULL);
+}
+
void tlvdb_visit(const struct tlvdb *tlvdb, tlv_cb cb, void *data, int level)
{
struct tlvdb *next = NULL;
{
return tlvdb->parent;
}
+
+bool tlv_get_uint8(const struct tlv *etlv, uint8_t *value)
+{
+ *value = 0;
+ if (etlv)
+ {
+ if (etlv->len == 0)
+ return true;
+
+ if (etlv->len == 1)
+ {
+ *value = etlv->value[0];
+ return true;
+ }
+ }
+ return false;
+}
+
+bool tlv_get_int(const struct tlv *etlv, int *value)
+{
+ *value = 0;
+ if (etlv)
+ {
+ if (etlv->len == 0)
+ return true;
+
+ if (etlv->len <= 4)
+ {
+ for (int i = 0; i < etlv->len; i++)
+ {
+ *value += etlv->value[i] * (1 << (i * 8));
+ }
+ return true;
+ }
+ }
+ return false;
+}
void tlvdb_add(struct tlvdb *tlvdb, struct tlvdb *other);
void tlvdb_change_or_add_node(struct tlvdb *tlvdb, tlv_tag_t tag, size_t len, const unsigned char *value);
+void tlvdb_change_or_add_node_ex(struct tlvdb *tlvdb, tlv_tag_t tag, size_t len, const unsigned char *value, struct tlvdb **tlvdb_elm);
void tlvdb_visit(const struct tlvdb *tlvdb, tlv_cb cb, void *data, int level);
const struct tlv *tlvdb_get(const struct tlvdb *tlvdb, tlv_tag_t tag, const struct tlv *prev);
bool tlv_is_constructed(const struct tlv *tlv);
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 tlv_get_uint8(const struct tlv *etlv, uint8_t *value);
+bool tlv_get_int(const struct tlv *etlv, int *value);
+
#endif