Commit 77979da2 by developer4

Alpha 0.3

parent 594f18ab
......@@ -94,8 +94,12 @@ emv_tags_t emv_tags[] = {
{0x9f59, "Upper Consecutive Offline Limit (Card Check)", BIN, 2},
{0x9f5c, "Cumulative Total Transaction Amount Upper Limit", BIN, 2},
{0x9f72, "Consecutive Transaction Limit (International - Country)", BIN, 2},
{0x9F62, "PCVC3 (Track1)", BIN, 2},
{0x9F63, "PUNATC (Track1)", BIN, 2},
{0x9F64, "NATC (Track1)", BIN, 2},
{0x9f65, "Track 2 Bit Map for CVC3", BIN, 2},
{0x9f66, "Terminal Transaction Qualifiers (TTQ)", BIN, 2},
{0x9f67, "NATC (Track2)", BIN, 2},
{0x9f68, "Mag Stripe CVM List", BIN, 2},
{0x9f69, "Unpredictable Number Data Object List (UDOL)", BIN, 2},
{0x9f6b, "Track 2 Data", BIN, 2},
......@@ -124,6 +128,7 @@ emv_tags_t emv_tags[] = {
{0x42, "Issuer Identification Number (IIN)", BIN, 1},
{0x4f, "Application Identifier (AID)", BIN, 1},
{0x50, "Application Label", STR, 1},
{0x56, "Track 1 Equivalent Data", BIN, 1},
{0x57, "Track 2 Equivalent Data", BIN, 1},
{0x5a, "Application Primary Account Number (PAN)", BCD_4BY4, 1},
{0x61, "Application Template", NODE, 1},
......@@ -135,7 +140,7 @@ emv_tags_t emv_tags[] = {
{0x77, "Response Message Template Format 2", NODE, 1},
{0x80, "Response Message Template Format 1", BIN, 1},
{0x81, "Amount, Authorised (Binary)", BIN, 1},
{0x82, "Application Interchange Profile", BIN, 1},
{0x82, "Application Interchange Profile (AIP)", BIN, 1},
{0x83, "Command Template", BIN, 1},
{0x84, "Dedicated File (DF) Name", BIN_OR_STR, 1},
{0x86, "Issuer Script Command", BIN, 1},
......@@ -166,6 +171,13 @@ emv_tags_t emv_tags[] = {
char * months[] = {"", "jan.", "feb.", "mar.", "apr.", "may ", "jun.", "jul.", "aug.", "sep.", "oct.", "nov.", "dec."};
//------------------------------------------------------------------------------
// Local function prototypes:
emv_tag_index_t findEmvTagIndex(emv_tag_t tag);
void print_tab(int tabulator);
void printEmvNode(emv_tree_node_t *tag_node, int tabulator);
EMV_STATUS newAflListItem(afl_list_item_t **afl_list);
EMV_STATUS parseEmvTag(uint8_t *tag_ptr, emv_tag_t *tag, uint8_t **tag_val, int32_t *tag_len, int32_t *tag_len_len, int32_t *tag_val_len);
//==============================================================================
emv_tag_index_t findEmvTagIndex(emv_tag_t tag)
{
emv_tag_index_t i = 0;
......@@ -350,7 +362,7 @@ EMV_STATUS getSfi(emv_tree_node_t *tag_node, uint8_t *sfi) {
}
}
//------------------------------------------------------------------------------
EMV_STATUS getAID(emv_tree_node_t *tag_node, uint8_t *aid, uint8_t *aid_len)
EMV_STATUS getAidHoriz(emv_tree_node_t *tag_node, uint8_t *aid, uint8_t *aid_len)
{
if (!tag_node)
return EMV_ERR_TAG_NOT_FOUND;
......@@ -365,13 +377,243 @@ EMV_STATUS getAID(emv_tree_node_t *tag_node, uint8_t *aid, uint8_t *aid_len)
}
} else {
if (tag_node->subnode) {
return getAID(tag_node->subnode, aid, aid_len);
return getAid(tag_node->subnode, aid, aid_len);
} else {
return getAID(tag_node->next, aid, aid_len);
return getAid(tag_node->next, aid, aid_len);
}
}
}
EMV_STATUS getAid(emv_tree_node_t *tag_node, uint8_t *aid, uint8_t *aid_len)
{
EMV_STATUS status;
while(tag_node) {
status = getAidHoriz(tag_node, aid, aid_len);
if (status == EMV_OK) {
return EMV_OK;
}
tag_node = tag_node->next;
}
return EMV_ERR_TAG_NOT_FOUND;
}
//------------------------------------------------------------------------------
EMV_STATUS newAflListItem(afl_list_item_t **afl_list)
{
afl_list_item_t *p;
p = malloc(sizeof(afl_list_item_t));
if (p == NULL) {
return SYS_ERR_OUT_OF_MEMORY;
} else {
*afl_list = p;
}
p->sfi = 0;
p->record_first = 0;
p->record_last = 0;
p->record_num_offline_auth = 0;
p->next = NULL;
return EMV_OK;
}
//------------------------------------------------------------------------------
void emvAflListCleanup(afl_list_item_t *head) {
afl_list_item_t *temp;
while (head) {
temp = head->next;
free(head);
head = temp;
}
}
//------------------------------------------------------------------------------
EMV_STATUS getAfl(emv_tree_node_t *tag_node, afl_list_item_t **afl_list_item, uint8_t *afl_list_count)
{
uint8_t items;
uint8_t *value_ptr;
afl_list_item_t *temp = NULL, *p;
EMV_STATUS status;
*afl_list_count = 0;
if (!tag_node)
return EMV_ERR_TAG_NOT_FOUND;
if (tag_node->tag == 0x94) {
if (!tag_node->value_len || (tag_node->value_len % 4)) { // first 2 bytes are AIP
return EMV_ERR_TAG_WRONG_SIZE;
} else {
items = tag_node->value_len / 4;
value_ptr = tag_node->value; // first 2 bytes are AIP
while (items) {
status = newAflListItem(&p); // all members are cleared
if (*afl_list_item == NULL) {
if (status)
return status;
*afl_list_item = p;
temp = p;
}
else
{
if (status) {
emvAflListCleanup(*afl_list_item);
*afl_list_item = NULL;
return status;
}
temp->next = p;
temp = temp->next;
}
p->sfi = *value_ptr++;
p->sfi >>= 3;
p->record_first = *value_ptr++;
p->record_last = *value_ptr++;
p->record_num_offline_auth = *value_ptr++;
items--;
}
*afl_list_count = tag_node->value_len / 4;
return EMV_OK;
}
} else {
if (tag_node->subnode) {
return getAfl(tag_node->subnode, afl_list_item, afl_list_count);
} else {
return getAfl(tag_node->next, afl_list_item, afl_list_count);
}
}
}
//------------------------------------------------------------------------------
EMV_STATUS getAflFromResponseMessageTemplateFormat1(emv_tree_node_t *tag_node, afl_list_item_t **afl_list_item, uint8_t *afl_list_count)
{
uint8_t items, len;
uint8_t *value_ptr;
bool first_item = true;
afl_list_item_t *temp, *p;
EMV_STATUS status;
*afl_list_count = 0;
if (!tag_node)
return EMV_ERR_TAG_NOT_FOUND;
if (tag_node->tag == 0x80) {
len = tag_node->value_len - 2; // first 2 bytes are AIP
if (!len || (len % 4)) { // first 2 bytes are AIP
return EMV_ERR_TAG_WRONG_SIZE;
} else {
items = len / 4;
value_ptr = &tag_node->value[2]; // first 2 bytes are AIP
while (items) {
status = newAflListItem(&p); // all members are cleared
if (first_item) {
if (status)
return status;
*afl_list_item = p;
temp = p;
first_item = false;
}
else
{
if (status) {
emvAflListCleanup(*afl_list_item);
return status;
}
temp->next = p;
temp = temp->next;
}
p->sfi = *value_ptr++;
p->sfi >>= 3;
p->record_first = *value_ptr++;
p->record_last = *value_ptr++;
p->record_num_offline_auth = *value_ptr++;
items--;
}
*afl_list_count = len / 4;
return EMV_OK;
}
} else {
if (tag_node->subnode) {
return getAfl(tag_node->subnode, afl_list_item, afl_list_count);
} else {
return getAfl(tag_node->next, afl_list_item, afl_list_count);
}
}
}
//------------------------------------------------------------------------------
EMV_STATUS getPdol(emv_tree_node_t *tag_node, emv_tree_node_t **pdol)
{
if (!tag_node)
return EMV_ERR_TAG_NOT_FOUND;
if (tag_node->tag == 0x9f38) {
if (tag_node->value_len) {
*pdol = tag_node->tl_list_format;
return EMV_OK;
} else {
return EMV_ERR_TAG_WRONG_SIZE;
}
} else {
if (tag_node->subnode) {
return getPdol(tag_node->subnode, pdol);
} else {
return getPdol(tag_node->next, pdol);
}
}
}
//------------------------------------------------------------------------------
EMV_STATUS formatGetProcessingOptionsDataField(emv_tree_node_t *tag_node, uint8_t **gpo_data_field, uint16_t *gpo_data_field_size)
{
uint8_t *temp;
emv_tree_node_t *pdol = NULL, *p = NULL;
EMV_STATUS status;
*gpo_data_field = NULL;
*gpo_data_field_size = 0;
status = getPdol(tag_node, &pdol);
if (status && status != EMV_ERR_TAG_NOT_FOUND)
return status;
if (status == EMV_OK && !pdol)
return EMV_ERR_PDOL_IS_EMPTY;
if (status != EMV_ERR_TAG_NOT_FOUND) {
p = pdol;
while (p) {
*gpo_data_field_size += p->value_len;
p = p->next;
}
if (*gpo_data_field_size == 0)
return EMV_ERR_PDOL_IS_EMPTY;
}
*gpo_data_field_size += 2;
*gpo_data_field = malloc(*gpo_data_field_size);
if (*gpo_data_field == NULL) {
*gpo_data_field_size = 0;
return SYS_ERR_OUT_OF_MEMORY;
}
memset(*gpo_data_field, 0, *gpo_data_field_size);
(*gpo_data_field)[0] = 0x83;
(*gpo_data_field)[1] = *gpo_data_field_size - 2;
if (status != EMV_ERR_TAG_NOT_FOUND) {
p = pdol;
temp = *gpo_data_field + 2;
while (p) {
if (p->tag == 0x9F66) // Terminal Transaction Qualifiers (TTQ) Tag
{
temp[0] = 0x28;
}
temp += p->value_len;
p = p->next;
}
}
return EMV_OK;
}
//------------------------------------------------------------------------------
EMV_STATUS parseEmvTag(uint8_t *tag_ptr, emv_tag_t *tag, uint8_t **tag_val, int32_t *tag_len, int32_t *tag_len_len, int32_t *tag_val_len)
{
// Tag parsing rules: there is at least 2 bytes if (MSB & 0x1F) == 0x1F
......@@ -499,9 +741,10 @@ void emvTreeCleanup(emv_tree_node_t *head) {
emv_tree_node_t *temp;
while (head) {
if (head->subnode) {
if (head->subnode)
emvTreeCleanup(head->subnode);
}
if (head->tl_list_format)
emvTreeCleanup(head->tl_list_format);
if (head->value)
free(head->value);
temp = head->next;
......
......@@ -41,7 +41,8 @@ typedef enum E_EMV_STATUS {
EMV_ERR_TAG_NOT_FOUND,
EMV_ERR_TAG_WRONG_SIZE,
EMV_ERR_IN_CARD_READER,
EMV_ERR_READING_RECORD
EMV_ERR_READING_RECORD,
EMV_ERR_PDOL_IS_EMPTY
} EMV_STATUS;
//==============================================================================
typedef uint32_t emv_tag_t;
......@@ -67,11 +68,30 @@ struct emv_tree_node_s {
emv_tree_node_t *next;
emv_tree_node_t *subnode;
};
//------------------------------------------------------------------------------
typedef struct afl_list_item_s afl_list_item_t;
struct afl_list_item_s {
uint8_t sfi;
uint8_t record_first;
uint8_t record_last;
uint8_t record_num_offline_auth;
afl_list_item_t *next;
};
//------------------------------------------------------------------------------
// Public function prototypes:
EMV_STATUS getSfi(emv_tree_node_t *tag_node, uint8_t *sfi);
EMV_STATUS getAID(emv_tree_node_t *tag_node, uint8_t *aid, uint8_t *aid_len);
EMV_STATUS getAid(emv_tree_node_t *tag_node, uint8_t *aid, uint8_t *aid_len);
EMV_STATUS getAfl(emv_tree_node_t *tag_node, afl_list_item_t **afl_list_item, uint8_t *afl_list_count);
EMV_STATUS getAflFromResponseMessageTemplateFormat1(emv_tree_node_t *tag_node, afl_list_item_t **afl_list_item, uint8_t *afl_list_count);
EMV_STATUS getPdol(emv_tree_node_t *tag_node, emv_tree_node_t **pdol);
// formatGetProcessingOptionsDataField()
// free() heap gpo_data_field point to when not needed any more...
EMV_STATUS formatGetProcessingOptionsDataField(emv_tree_node_t *tag_node, uint8_t **gpo_data_field, uint16_t *gpo_data_field_size);
void printEmvBranch(emv_tree_node_t *tag_node, int tabulator);
EMV_STATUS newEmvTag(emv_tree_node_t **head, uint8_t *input, int32_t input_bytes_left, bool is_list_format);
void emvTreeCleanup(emv_tree_node_t *head);
void emvAflListCleanup(afl_list_item_t *head);
// emvReadRecord()
// uint8_t *r_apdu - minimum of the 256 bytes have to be allocated before call
EMV_STATUS emvReadRecord(uint8_t *r_apdu, uint32_t *Ne, uint8_t sfi, uint8_t record, uint8_t sw[2]);
......
......@@ -200,16 +200,16 @@ UFR_STATUS NewCardInField(uint8_t sak, uint8_t *uid, uint8_t uid_size) {
return UFR_OK;
}
//------------------------------------------------------------------------------
void checkEmvPse(const char *df_name, const char *szTitlePse) {
EMV_STATUS emv_status;
UFR_STATUS status;
emv_tree_node_t *head = NULL, *tail = NULL;
void checkEmvPse(const char *df_name, const char *szTitlePse)
{
emv_tree_node_t *head = NULL, *tail = NULL, *temp = NULL;
uint8_t r_apdu[RAW_RES_MAX_LEN];
uint32_t Ne; // without SW
uint8_t sw[2];
uint8_t sfi, record, cnt;
uint16_t *sw16_ptr = (uint16_t *) &sw;
emv_tree_node_t *temp = NULL;
EMV_STATUS emv_status;
UFR_STATUS status;
printf(" ===================================================================\n");
printf(" Checking if if the card support Payment System Environment (%s) \n", szTitlePse);
......@@ -318,14 +318,17 @@ void tryEmvPseCardRead(const char *df_name, const char *szTitlePse) {
EMV_STATUS emv_status;
UFR_STATUS status;
bool head_attached = false;
emv_tree_node_t *head = NULL, *tail = NULL;
char *sz_hex_r_apdu;
emv_tree_node_t *head = NULL, *tail = NULL, *temp = NULL;
afl_list_item_t *afl_list = NULL, *afl_list_item = NULL;
uint8_t afl_list_count;
// char *sz_hex_r_apdu;
uint8_t r_apdu[RAW_RES_MAX_LEN];
uint32_t Ne; // without SW
uint8_t sw[2], aid[MAX_AID_LEN];
uint8_t sfi, record, cnt = 1, aid_len;
uint16_t *sw16_ptr = (uint16_t *) &sw;
emv_tree_node_t *temp = NULL;
uint8_t *gpo_data_field = NULL;
uint16_t gpo_data_field_size;
printf(" ===================================================================\n");
printf(" Read and parse EMV card supporting %s \n", szTitlePse);
......@@ -340,10 +343,10 @@ void tryEmvPseCardRead(const char *df_name, const char *szTitlePse) {
printf(" %d. Issuing \"Select PSE\" command (\"%s\"):\n"
" [C] 00 A4 04 00 %02X ", cnt++, df_name, (unsigned)strlen(df_name));
print_hex((const uint8_t *)df_name, strlen(df_name), " ");
print_hex((uint8_t *)df_name, strlen(df_name), " ");
printf(" 00\n");
Ne = 256;
status = APDUTransceive(0x00, 0xA4, 0x04, 0x00, (const uint8_t *)df_name, strlen(df_name), r_apdu, &Ne, 1, sw);
status = APDUTransceive(0x00, 0xA4, 0x04, 0x00, (uint8_t *)df_name, strlen(df_name), r_apdu, &Ne, 1, sw);
if (status != UFR_OK) {
printf(" Error while executing APDU command, uFR status is: 0x%08X\n", status);
break;
......@@ -407,14 +410,14 @@ void tryEmvPseCardRead(const char *df_name, const char *szTitlePse) {
} while (emv_status == EMV_OK);
}
emv_status = getAID(head, aid, &aid_len);
emv_status = getAid(head, aid, &aid_len);
if (emv_status == EMV_OK) {
printf("\n %d. Issuing \"Select the application\" command:\n"
" [C] 00 A4 04 00 %02X ", cnt++, aid_len);
print_hex((const uint8_t *)aid, aid_len, " ");
print_hex(aid, aid_len, " ");
printf(" 00\n");
Ne = 256;
status = APDUTransceive(0x00, 0xA4, 0x04, 0x00, (const uint8_t *)aid, aid_len, r_apdu, &Ne, 1, sw);
status = APDUTransceive(0x00, 0xA4, 0x04, 0x00, aid, aid_len, r_apdu, &Ne, 1, sw);
if (status != UFR_OK) {
printf(" Error while executing APDU command, uFR status is: 0x%08X\n", status);
break;
......@@ -435,6 +438,11 @@ void tryEmvPseCardRead(const char *df_name, const char *szTitlePse) {
}
emv_status = newEmvTag(&temp, r_apdu, Ne, false);
if (emv_status) {
printf(" EMV parsing error code: 0x%08X", emv_status);
break;
}
if (!head_attached) {
head->next = tail = temp;
head_attached = true;
......@@ -444,7 +452,7 @@ void tryEmvPseCardRead(const char *df_name, const char *szTitlePse) {
}
// Ovde se mora, ako postoji PDOL, sračunati dužinu bajtova i to poslati u narednoj komandi.
// Ako PDOL sadrži Terminal Capabilities Tag '9F33' onda po mnogima se mogu poslati sve nule.
// Ako PDOL sadrži Terminal Capabilities Tag '9F33' onda se po mnogima mogu poslati sve nule.
// Ako PDOL sadrži Terminal Transaction Qualifiers (TTQ) Tag '9F66' onda tu treba postaviti bar "28 00 00 00"
// da ne bi kartica vratila grešku "69 85" = "Conditions of use not satisfied". Radi i sa
// "20 00 00 00".
......@@ -453,13 +461,45 @@ void tryEmvPseCardRead(const char *df_name, const char *szTitlePse) {
// da kartica vrati Response Message Template Format 1 Tag '80' {Contains the data objects (without tags and lengths)
// returned by the ICC in response to a command}. U tom slučaju prva dva bajta u okviru vraćene binarne vrednosti
// Taga '80' predstavljaju AIP a ostatak AFL u grupama po 4 bajta koje treba parsirati isto kao Tag '94':
// - prvi bajt je SFI šiftovan 3 bajta levo (za read record cmd samo treba još orovati sa 4)
// - prvi bajt je SFI šiftovan 3 bita levo (za read record cmd samo treba još orovati sa 4)
// - drugi bajt je prvi record tog SFI-a
// - treći bajt je poslednji record tog SFI-a
// - broj zapisa koji učestvuju u "offline data authentication" tog SFI-a počevši od prvog record-a.
// Ako kartica ne vrati Tag '80' onda treba očekivati da vrati tagove '82' za AIP i '94' za AFL.
printf("\n %d. Formating GET PROCESSING OPTIONS instruction (checking PDOL).\n", cnt++);
emv_status = formatGetProcessingOptionsDataField(temp, &gpo_data_field, &gpo_data_field_size);
if (emv_status) {
printf(" EMV parsing error code: 0x%08X", emv_status);
break;
}
printf("\n %d. Issuing \"Get processing options\" command:\n"
" [C] 80 A8 00 00 15 83 13 28 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n", cnt++);
status = ApduCommand("80 A8 00 00 15 83 13 28 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", &sz_hex_r_apdu, sw);
" [C] 80 A8 00 00 %02X ", cnt++, gpo_data_field_size);
print_hex(gpo_data_field, gpo_data_field_size, " ");
printf(" 00\n");
Ne = 256;
status = APDUTransceive(0x80, 0xA8, 0x00, 0x00, gpo_data_field, gpo_data_field_size, r_apdu, &Ne, 1, sw);
if (status != UFR_OK) {
printf(" Error while executing APDU command, uFR status is: 0x%08X\n", status);
break;
} else {
if (*sw16_ptr != 0x90) {
printf(" [SW] ");
print_hex_ln(sw, 2, " ");
printf(" Could not continue execution due to an APDU error.\n");
break;
}
if (Ne) {
printf(" APDU command executed: response data length = %d bytes\n", (int) Ne);
printf(" [R] ");
print_hex_ln(r_apdu, Ne, " ");
}
printf(" [SW] ");
print_hex_ln(sw, 2, " ");
}
/* status = ApduCommand("80 A8 00 00 15 83 13 28 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", &sz_hex_r_apdu, sw);
if (status != UFR_OK) {
printf(" Error while executing APDU command, uFR status is: 0x%08X\n", status);
break;
......@@ -476,19 +516,76 @@ void tryEmvPseCardRead(const char *df_name, const char *szTitlePse) {
} else {
hex2bin(r_apdu, sz_hex_r_apdu);
emv_status = newEmvTag(&temp, r_apdu, Ne, false);
...CHECK status...
tail->next = temp;
tail = tail->next;
}
}
}
*/
emv_status = newEmvTag(&temp, r_apdu, Ne, false);
if (emv_status) {
printf(" EMV parsing error code: 0x%08X", emv_status);
break;
}
tail->next = temp;
tail = tail->next;
emv_status = getAfl(temp, &afl_list, &afl_list_count);
if (emv_status == EMV_ERR_TAG_NOT_FOUND)
emv_status = getAflFromResponseMessageTemplateFormat1(temp, &afl_list, &afl_list_count);
if (emv_status) {
printf(" EMV parsing error code: 0x%08X", emv_status);
break;
}
printf("\n-------------------------------------------------------------------\n");
printEmvBranch(head, 0);
afl_list_item = afl_list;
while (afl_list_item) {
for (int r = afl_list_item->record_first; r <= afl_list_item->record_last; r++) {
printf("\n %d. Issuing \"Read Record\" command (record = %d, sfi = %d):\n"
" [C] 00 B2 %02X %02X 00\n", cnt,
r, afl_list_item->sfi, r, (afl_list_item->sfi << 3) | 4);
emv_status = emvReadRecord(r_apdu, &Ne, afl_list_item->sfi, r, sw);
if (emv_status == EMV_OK) {
emv_status = newEmvTag(&temp, r_apdu, Ne, false);
if (emv_status == EMV_OK) {
tail->next = temp;
tail = tail->next;
}
if (Ne) {
printf(" APDU command executed: response data length = %d bytes\n", (int) Ne);
printf(" [R] ");
print_hex_ln(r_apdu, Ne, " ");
}
printf(" [SW] ");
print_hex_ln(sw, 2, " ");
} else {
if (*sw16_ptr != 0x90) {
printf(" [SW] ");
print_hex_ln(sw, 2, " ");
}
}
cnt++;
}
afl_list_item = afl_list_item->next;
}
// if (outer_exit)
// break;
}
printf("\n-------------------------------------------------------------------\n");
} while (0);
printEmvBranch(head, 0);
printf(" ===================================================================\n");
emvTreeCleanup(head);
if (afl_list)
emvAflListCleanup(afl_list);
if (gpo_data_field)
free(gpo_data_field);
if (head)
emvTreeCleanup(head);
s_block_deselect(100);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment