Skip to content

Commit f4897fc

Browse files
committed
Implement cardholder application selection loop
This loop reuses the existing application selection test code but will be refactored once the high level EMV library interface provides an application selection function.
1 parent 609f573 commit f4897fc

File tree

3 files changed

+156
-49
lines changed

3 files changed

+156
-49
lines changed

Diff for: src/emv_app.c

+35
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,41 @@ struct emv_app_t* emv_app_list_pop(struct emv_app_list_t* list)
408408
return app;
409409
}
410410

411+
struct emv_app_t* emv_app_list_remove_index(
412+
struct emv_app_list_t* list,
413+
unsigned int index
414+
)
415+
{
416+
struct emv_app_t* prev = NULL;
417+
418+
if (!emv_app_list_is_valid(list)) {
419+
return NULL;
420+
}
421+
422+
for (struct emv_app_t* app = list->front; app != NULL; app = app->next) {
423+
if (index == 0) {
424+
if (!prev) {
425+
// Remove app from front of list
426+
return emv_app_list_pop(list);
427+
}
428+
429+
prev->next = app->next;
430+
if (!app->next) {
431+
// App at back of list
432+
list->back = prev;
433+
}
434+
app->next = NULL;
435+
return app;
436+
}
437+
438+
// Advance and remember previous app
439+
--index;
440+
prev = app;
441+
}
442+
443+
return NULL;
444+
}
445+
411446
static int emv_app_list_insert(
412447
struct emv_app_list_t* list,
413448
struct emv_app_t* pos,

Diff for: src/emv_app.h

+11
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,17 @@ int emv_app_list_push(struct emv_app_list_t* list, struct emv_app_t* app);
167167
*/
168168
struct emv_app_t* emv_app_list_pop(struct emv_app_list_t* list);
169169

170+
/**
171+
* Remove EMV application from EMV application list by index
172+
* @param list EMV application list
173+
* @param index Index (starting from zero) of EMV application to remove
174+
* @return EMV application. Use @ref emv_tlv_free() to free memory.
175+
*/
176+
struct emv_app_t* emv_app_list_remove_index(
177+
struct emv_app_list_t* list,
178+
unsigned int index
179+
);
180+
170181
/**
171182
* Sort EMV application list according to the priority field
172183
* @param list EMV application list

Diff for: tools/emv-tool.c

+110-49
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,9 @@ struct emv_txn_t {
137137

138138
// ICC data
139139
struct emv_tlv_list_t icc;
140+
141+
// Cardholder application selection required?
142+
bool application_selection_required;
140143
};
141144
static struct emv_txn_t emv_txn;
142145

@@ -388,6 +391,7 @@ int main(int argc, char** argv)
388391
size_t reader_idx;
389392
uint8_t atr[PCSC_MAX_ATR_SIZE];
390393
size_t atr_len = 0;
394+
bool first_application_selection_prompt = true;
391395

392396
if (argc == 1) {
393397
// No command line arguments
@@ -539,71 +543,127 @@ int main(int argc, char** argv)
539543
goto emv_exit;
540544
}
541545

542-
if (emv_app_list_is_empty(&app_list)) {
543-
printf("No supported applications\n");
544-
goto emv_exit;
545-
}
546-
547546
printf("Candidate applications:\n");
548547
for (struct emv_app_t* app = app_list.front; app != NULL; app = app->next) {
549548
print_emv_app(app);
550549
}
551550

552-
if (emv_app_list_selection_is_required(&app_list)) {
551+
emv_txn.application_selection_required = emv_app_list_selection_is_required(&app_list);
552+
if (emv_txn.application_selection_required) {
553553
printf("Cardholder selection is required\n");
554554
}
555555

556-
// HACK: test application selection
557-
{
558-
char str[1024];
556+
do {
557+
struct emv_app_t* current_app;
559558

560-
// Use first application
561-
struct emv_app_t* current_app = emv_app_list_pop(&app_list);
562-
if (!current_app) {
563-
printf("No supported applications\n");
559+
// All application selection failures return to the start of the loop
560+
// If no applications remain, terminate session
561+
// See EMV 4.4 Book 1, 12.4
562+
// See EMV 4.4 Book 4, 11.3
563+
if (emv_app_list_is_empty(&app_list)) {
564+
emv_debug_info("Candidate list empty");
565+
printf("OUTCOME: %s\n", emv_outcome_get_string(EMV_OUTCOME_NOT_ACCEPTED));
564566
goto emv_exit;
565567
}
566-
emv_app_list_clear(&app_list);
567-
568-
uint8_t current_aid[16];
569-
size_t current_aid_len = current_app->aid->length;
570-
memcpy(current_aid, current_app->aid->value, current_app->aid->length);
571-
emv_app_free(current_app);
572-
current_app = NULL;
573-
574-
// Select application
575-
print_buf("\nSELECT application", current_aid, current_aid_len);
576-
uint8_t fci[EMV_RAPDU_DATA_MAX];
577-
size_t fci_len = sizeof(fci);
578-
uint16_t sw1sw2;
579-
r = emv_ttl_select_by_df_name(&emv_txn.ttl, current_aid, current_aid_len, fci, &fci_len, &sw1sw2);
580-
if (r) {
581-
printf("Failed to select application; r=%d\n", r);
582-
goto emv_exit;
583-
}
584-
print_buf("FCI", fci, fci_len);
585-
print_emv_buf(fci, fci_len, " ", 0);
586-
printf("SW1SW2 = %04hX (%s)\n", sw1sw2, iso7816_sw1sw2_get_string(sw1sw2 >> 8, sw1sw2 & 0xff, str, sizeof(str)));
587568

588-
if (sw1sw2 != 0x9000) {
589-
goto emv_exit;
569+
if (emv_txn.application_selection_required) {
570+
unsigned int app_count = 0;
571+
int r;
572+
char s[4]; // two digits, newline and null
573+
unsigned int input = 0;
574+
575+
if (first_application_selection_prompt) {
576+
printf("\nSelect application:\n");
577+
first_application_selection_prompt = false;
578+
} else {
579+
// See EMV 4.4 Book 4, 11.3
580+
printf("\nTry again:\n");
581+
}
582+
for (struct emv_app_t* app = app_list.front; app != NULL; app = app->next) {
583+
++app_count;
584+
printf("%u - %s\n", app_count, app->display_name);
585+
}
586+
printf("Enter number: ");
587+
if (!fgets(s, sizeof(s), stdin)) {
588+
printf("Invalid input. Try again.\n");
589+
continue;
590+
}
591+
r = sscanf(s, "%u", &input);
592+
if (r != 1) {
593+
printf("Invalid input. Try again.\n");
594+
continue;
595+
}
596+
if (!input || input > app_count) {
597+
printf("Invalid input. Try again.\n");
598+
continue;
599+
}
600+
601+
current_app = emv_app_list_remove_index(&app_list, input - 1);
602+
603+
} else {
604+
// Use first application
605+
current_app = emv_app_list_pop(&app_list);
590606
}
591607

592-
// Create EMV application object
593-
struct emv_app_t* app;
594-
app = emv_app_create_from_fci(fci, fci_len);
595-
if (r) {
596-
printf("emv_app_populate_from_fci() failed; r=%d\n", r);
597-
goto emv_exit;
608+
// HACK: test application selection
609+
{
610+
char str[1024];
611+
612+
uint8_t current_aid[16];
613+
size_t current_aid_len = current_app->aid->length;
614+
memcpy(current_aid, current_app->aid->value, current_app->aid->length);
615+
emv_app_free(current_app);
616+
current_app = NULL;
617+
618+
// Select application
619+
print_buf("\nSELECT application", current_aid, current_aid_len);
620+
uint8_t fci[EMV_RAPDU_DATA_MAX];
621+
size_t fci_len = sizeof(fci);
622+
uint16_t sw1sw2;
623+
r = emv_ttl_select_by_df_name(&emv_txn.ttl, current_aid, current_aid_len, fci, &fci_len, &sw1sw2);
624+
if (r) {
625+
fprintf(stderr, "emv_ttl_select_by_df_name() failed; r=%d\n", r);
626+
continue;
627+
}
628+
print_buf("FCI", fci, fci_len);
629+
print_emv_buf(fci, fci_len, " ", 0);
630+
printf("SW1SW2 = %04hX (%s)\n", sw1sw2, iso7816_sw1sw2_get_string(sw1sw2 >> 8, sw1sw2 & 0xff, str, sizeof(str)));
631+
632+
if (sw1sw2 != 0x9000) {
633+
continue;
634+
}
635+
636+
// Create EMV application object
637+
struct emv_app_t* app;
638+
app = emv_app_create_from_fci(fci, fci_len);
639+
if (r) {
640+
fprintf(stderr, "emv_app_populate_from_fci() failed; r=%d\n", r);
641+
continue;
642+
}
643+
printf("\n");
644+
print_emv_app(app);
645+
print_emv_tlv_list(&app->tlv_list);
646+
647+
// TODO: EMV 4.4 Book 1, 12.4, ensure that 84 matches SELECT command
648+
649+
// Capture ICC data
650+
emv_txn.icc = app->tlv_list;
651+
app->tlv_list = EMV_TLV_LIST_INIT;
652+
emv_app_free(app);
653+
654+
// Application selection was successful
655+
// TODO: EMV 4.4 Book 1, 12.4, create 9F06 from 84
656+
break;
598657
}
599-
printf("\n");
600-
print_emv_app(app);
601-
print_emv_tlv_list(&app->tlv_list);
658+
} while (true);
602659

603-
// Capture ICC data
604-
emv_txn.icc = app->tlv_list;
605-
app->tlv_list = EMV_TLV_LIST_INIT;
606-
emv_app_free(app);
660+
// Application selection has been successful and the application list
661+
// is no longer needed.
662+
emv_app_list_clear(&app_list);
663+
664+
// HACK: test GPO and Read Application Data
665+
{
666+
char str[1024];
607667

608668
// Process PDOL
609669
struct emv_tlv_t* pdol;
@@ -660,6 +720,7 @@ int main(int argc, char** argv)
660720
printf("\nGET PROCESSING OPTIONS\n");
661721
uint8_t gpo_response[EMV_RAPDU_DATA_MAX];
662722
size_t gpo_response_len = sizeof(gpo_response);
723+
uint16_t sw1sw2;
663724
r = emv_ttl_get_processing_options(&emv_txn.ttl, gpo_data, gpo_data_len, gpo_response, &gpo_response_len, &sw1sw2);
664725
if (r) {
665726
printf("Failed to get processign options; r=%d\n", r);

0 commit comments

Comments
 (0)