From 609f57368582214d5a4c666579ba0007a5521c42 Mon Sep 17 00:00:00 2001 From: Leon Lynch Date: Fri, 9 Feb 2024 22:51:29 +0100 Subject: [PATCH] Determine whether cardholder application selection is required --- src/emv_app.c | 22 ++++++ src/emv_app.h | 12 +++ tests/emv_build_candidate_list_test.c | 105 ++++++++++++++++++++++++++ tools/emv-tool.c | 4 + 4 files changed, 143 insertions(+) diff --git a/src/emv_app.c b/src/emv_app.c index e792b5d..fc5ffe3 100644 --- a/src/emv_app.c +++ b/src/emv_app.c @@ -507,3 +507,25 @@ int emv_app_list_sort_priority(struct emv_app_list_t* list) *list = sorted_list; return 0; } + +bool emv_app_list_selection_is_required(const struct emv_app_list_t* list) +{ + size_t app_count = 0; + + if (!emv_app_list_is_valid(list)) { + return false; + } + + for (const struct emv_app_t* app = list->front; app != NULL; app = app->next) { + if (app->confirmation_required) { + return true; + } + ++app_count; + + if (app_count > 1) { + return true; + } + } + + return false; +} diff --git a/src/emv_app.h b/src/emv_app.h index a4cefe6..5505d04 100644 --- a/src/emv_app.h +++ b/src/emv_app.h @@ -174,6 +174,18 @@ struct emv_app_t* emv_app_list_pop(struct emv_app_list_t* list); */ int emv_app_list_sort_priority(struct emv_app_list_t* list); +/** + * Determine whether cardholder application selection is required + * @note This function should only be used once during transaction processing + * for the initial candidate application list. If it is determined that + * cardholder application selection is required, it continues to be required + * even after the application that required it has been removed from the + * candidate application list. + * @param list EMV application list + * @return Boolean indicating whether cardholder application selection is required + */ +bool emv_app_list_selection_is_required(const struct emv_app_list_t* list); + __END_DECLS #endif diff --git a/tests/emv_build_candidate_list_test.c b/tests/emv_build_candidate_list_test.c index 4c66166..331583d 100644 --- a/tests/emv_build_candidate_list_test.c +++ b/tests/emv_build_candidate_list_test.c @@ -278,6 +278,22 @@ static const struct xpdu_t test_sorted_app_priority[] = { { 0 } }; +static const struct xpdu_t test_app_cardholder_confirmation[] = { + { + 20, (uint8_t[]){ 0x00, 0xA4, 0x04, 0x00, 0x0E, 0x31, 0x50, 0x41, 0x59, 0x2E, 0x53, 0x59, 0x53, 0x2E, 0x44, 0x44, 0x46, 0x30, 0x31, 0x00 }, // SELECT 1PAY.SYS.DDF01 + 36, (uint8_t[]){ 0x6F, 0x20, 0x84, 0x0E, 0x31, 0x50, 0x41, 0x59, 0x2E, 0x53, 0x59, 0x53, 0x2E, 0x44, 0x44, 0x46, 0x30, 0x31, 0xA5, 0x0E, 0x88, 0x01, 0x01, 0x5F, 0x2D, 0x04, 0x6E, 0x6C, 0x65, 0x6E, 0x9F, 0x11, 0x01, 0x01, 0x90, 0x00 }, // FCI + }, + { + 5, (uint8_t[]){ 0x00, 0xB2, 0x01, 0x0C, 0x00 }, // READ RECORD 1,1 + 45, (uint8_t[]){ 0x70, 0x29, 0x61, 0x27, 0x4F, 0x07, 0xA0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, 0x50, 0x0B, 0x56, 0x49, 0x53, 0x41, 0x20, 0x43, 0x52, 0x45, 0x44, 0x49, 0x54, 0x87, 0x01, 0x81, 0x9F, 0x12, 0x0B, 0x56, 0x49, 0x53, 0x41, 0x20, 0x43, 0x52, 0x45, 0x44, 0x49, 0x54, 0x90, 0x00 }, // AEF + }, + { + 5, (uint8_t[]){ 0x00, 0xB2, 0x02, 0x0C, 0x00 }, // READ RECORD 1,2 + 2, (uint8_t[]){ 0x6A, 0x83 }, // Record not found + }, + { 0 } +}; + int main(void) { int r; @@ -335,6 +351,11 @@ int main(void) r = 1; goto exit; } + if (emv_app_list_selection_is_required(&app_list)) { + fprintf(stderr, "Cardholder application selection unexpectedly required\n"); + r = 1; + goto exit; + } printf("Success\n"); printf("\nTesting PSE not found and AID card blocked or SELECT not supported...\n"); @@ -364,6 +385,11 @@ int main(void) r = 1; goto exit; } + if (emv_app_list_selection_is_required(&app_list)) { + fprintf(stderr, "Cardholder application selection unexpectedly required\n"); + r = 1; + goto exit; + } printf("Success\n"); printf("\nTesting PSE not found and no supported applications...\n"); @@ -393,6 +419,11 @@ int main(void) r = 1; goto exit; } + if (emv_app_list_selection_is_required(&app_list)) { + fprintf(stderr, "Cardholder application selection unexpectedly required\n"); + r = 1; + goto exit; + } printf("Success\n"); printf("\nTesting PSE blocked and no supported applications...\n"); @@ -422,6 +453,11 @@ int main(void) r = 1; goto exit; } + if (emv_app_list_selection_is_required(&app_list)) { + fprintf(stderr, "Cardholder application selection unexpectedly required\n"); + r = 1; + goto exit; + } printf("Success\n"); printf("\nTesting PSE not found and AID blocked...\n"); @@ -451,6 +487,11 @@ int main(void) r = 1; goto exit; } + if (emv_app_list_selection_is_required(&app_list)) { + fprintf(stderr, "Cardholder application selection unexpectedly required\n"); + r = 1; + goto exit; + } printf("Success\n"); printf("\nTesting PSE app not supported...\n"); @@ -480,6 +521,11 @@ int main(void) r = 1; goto exit; } + if (emv_app_list_selection_is_required(&app_list)) { + fprintf(stderr, "Cardholder application selection unexpectedly required\n"); + r = 1; + goto exit; + } printf("Success\n"); printf("\nTesting PSE app supported...\n"); @@ -509,6 +555,11 @@ int main(void) for (struct emv_app_t* app = app_list.front; app != NULL; app = app->next) { print_emv_app(app); } + if (emv_app_list_selection_is_required(&app_list)) { + fprintf(stderr, "Cardholder application selection unexpectedly required\n"); + r = 1; + goto exit; + } printf("Success\n"); printf("\nTesting PSE multiple apps supported...\n"); @@ -538,6 +589,11 @@ int main(void) for (struct emv_app_t* app = app_list.front; app != NULL; app = app->next) { print_emv_app(app); } + if (!emv_app_list_selection_is_required(&app_list)) { + fprintf(stderr, "Cardholder application selection unexpectedly NOT required\n"); + r = 1; + goto exit; + } printf("Success\n"); printf("\nTesting PSE not found and multiple AIDs supported...\n"); @@ -567,6 +623,11 @@ int main(void) for (struct emv_app_t* app = app_list.front; app != NULL; app = app->next) { print_emv_app(app); } + if (!emv_app_list_selection_is_required(&app_list)) { + fprintf(stderr, "Cardholder application selection unexpectedly NOT required\n"); + r = 1; + goto exit; + } printf("Success\n"); printf("\nTesting sorted app priority...\n"); @@ -607,6 +668,50 @@ int main(void) goto exit; } } + if (!emv_app_list_selection_is_required(&app_list)) { + fprintf(stderr, "Cardholder application selection unexpectedly NOT required\n"); + r = 1; + goto exit; + } + printf("Success\n"); + + printf("\nTesting cardholder confirmation required for single app...\n"); + emul_ctx.xpdu_list = test_app_cardholder_confirmation; + emul_ctx.xpdu_current = NULL; + emv_app_list_clear(&app_list); + r = emv_build_candidate_list( + &ttl, + &supported_aids, + &app_list + ); + if (r) { + fprintf(stderr, "Unexpected emv_build_candidate_list() result; error %d: %s\n", r, r < 0 ? emv_error_get_string(r) : emv_outcome_get_string(r)); + r = 1; + goto exit; + } + if (emul_ctx.xpdu_current->c_xpdu_len != 0) { + fprintf(stderr, "Incomplete card interaction\n"); + r = 1; + goto exit; + } + if (emv_app_list_is_empty(&app_list)) { + fprintf(stderr, "Candidate list unexpectedly empty\n"); + r = 1; + goto exit; + } + for (struct emv_app_t* app = app_list.front; app != NULL; app = app->next) { + print_emv_app(app); + } + if (app_list.front != app_list.back) { + fprintf(stderr, "Candidate list unexpectedly contains more than one app\n"); + r = 1; + goto exit; + } + if (!emv_app_list_selection_is_required(&app_list)) { + fprintf(stderr, "Cardholder application selection unexpectedly NOT required\n"); + r = 1; + goto exit; + } printf("Success\n"); // Success diff --git a/tools/emv-tool.c b/tools/emv-tool.c index abb5ef6..11038c4 100644 --- a/tools/emv-tool.c +++ b/tools/emv-tool.c @@ -549,6 +549,10 @@ int main(int argc, char** argv) print_emv_app(app); } + if (emv_app_list_selection_is_required(&app_list)) { + printf("Cardholder selection is required\n"); + } + // HACK: test application selection { char str[1024];