From 71c35427105910b6b8988e087175e354baced254 Mon Sep 17 00:00:00 2001 From: Leon Lynch Date: Sun, 5 May 2024 19:25:00 +0200 Subject: [PATCH] Refactor emv_initiate_application_processing() to use emv_ctx_t Note that emv_initiate_application_processing() will now clear the ICC data list in case it was populated by a previous application selection attempt. Also, emv_initiate_application_processing() no longer has separate TLV source lists as parameters and will instead search the TLV lists when building the PDOL data for GET PROCESSING OPTIONS in this order: * emv_ctx_t.params * emv_ctx_t.config In future, a more advanced per-AID configuration mechanism will impact the above order. --- src/emv.c | 30 ++- src/emv.h | 26 ++- ...emv_initiate_application_processing_test.c | 180 +++++++----------- tools/emv-tool.c | 8 +- 4 files changed, 98 insertions(+), 146 deletions(-) diff --git a/src/emv.c b/src/emv.c index 94429a6..1f4cc26 100644 --- a/src/emv.c +++ b/src/emv.c @@ -486,13 +486,7 @@ int emv_select_application( return r; } -int emv_initiate_application_processing( - struct emv_ttl_t* ttl, - struct emv_app_t* selected_app, - const struct emv_tlv_list_t* source1, - const struct emv_tlv_list_t* source2, - struct emv_tlv_list_t* icc -) +int emv_initiate_application_processing(struct emv_ctx_t* ctx) { int r; const struct emv_tlv_t* pdol; @@ -501,15 +495,18 @@ int emv_initiate_application_processing( size_t gpo_data_len; struct emv_tlv_list_t gpo_output = EMV_TLV_LIST_INIT; - if (!ttl || !selected_app || !source1) { - emv_debug_trace_msg("ttl=%p, selected_app=%p, source1=%p, source2=%p", ttl, selected_app, source1, source2); + if (!ctx || !ctx->selected_app) { + emv_debug_trace_msg("ctx=%p, selected_app=%p", ctx, ctx->selected_app); emv_debug_error("Invalid parameter"); return EMV_ERROR_INVALID_PARAMETER; } + // Clear existing ICC data list to avoid ambiguity + emv_tlv_list_clear(&ctx->icc); + // Process PDOL, if available // See EMV 4.4 Book 3, 10.1 - pdol = emv_tlv_list_find_const(&selected_app->tlv_list, EMV_TAG_9F38_PDOL); + pdol = emv_tlv_list_find_const(&ctx->selected_app->tlv_list, EMV_TAG_9F38_PDOL); if (pdol) { int dol_data_len; size_t gpo_data_offset; @@ -545,8 +542,8 @@ int emv_initiate_application_processing( r = emv_dol_build_data( pdol->value, pdol->length, - source1, - source2, + &ctx->params, + &ctx->config, gpo_data + gpo_data_offset, &gpo_data_len ); @@ -569,7 +566,7 @@ int emv_initiate_application_processing( gpo_data_len = 0; } - r = emv_tal_get_processing_options(ttl, gpo_data, gpo_data_len, &gpo_output, NULL, NULL); + r = emv_tal_get_processing_options(ctx->ttl, gpo_data, gpo_data_len, &gpo_output, NULL, NULL); if (r) { emv_debug_trace_msg("emv_tal_get_processing_options() failed; r=%d", r); if (r < 0) { @@ -600,11 +597,11 @@ int emv_initiate_application_processing( } // Move application data to ICC data list - *icc = selected_app->tlv_list; - selected_app->tlv_list = EMV_TLV_LIST_INIT; + ctx->icc = ctx->selected_app->tlv_list; + ctx->selected_app->tlv_list = EMV_TLV_LIST_INIT; // Append GPO output to ICC data list - r = emv_tlv_list_append(icc, &gpo_output); + r = emv_tlv_list_append(&ctx->icc, &gpo_output); if (r) { emv_debug_trace_msg("emv_tlv_list_append() failed; r=%d", r); @@ -619,7 +616,6 @@ int emv_initiate_application_processing( goto exit; error: - emv_tlv_list_clear(icc); emv_tlv_list_clear(&gpo_output); exit: return r; diff --git a/src/emv.h b/src/emv.h index 4c3a49d..274dd8a 100644 --- a/src/emv.h +++ b/src/emv.h @@ -98,7 +98,8 @@ struct emv_ctx_t { /** * @brief Currently selected application * - * Populated by @ref emv_select_application(). + * Populated by @ref emv_select_application() and used by + * @ref emv_initiate_application_processing(). */ struct emv_app_t* selected_app; @@ -261,29 +262,26 @@ int emv_select_application( /** * Initiate EMV application processing by assessing the Processing Options * Data Object List (PDOL) and performing GET PROCESSING OPTIONS. + * + * When building the PDOL data required for GET PROCESSING OPTIONS, this + * function will search the TLV lists in this order: + * - @ref emv_ctx_t.params + * - @ref emv_ctx_t.config + * * @note Upon success, this function will also move the selected application's - * TLV data to the ICC data output and append the output of + * TLV data to the ICC data list and append the output of * GET PROCESSING OPTIONS. + * * @remark See EMV 4.4 Book 3, 10.1 * @remark See EMV 4.4 Book 4, 6.3.1 * - * @param ttl EMV Terminal Transport Layer context - * @param selected_app Selected EMV application - * @param source1 EMV TLV list used as primary source. Required. - * @param source2 EMV TLV list used as secondary source. NULL to ignore. - * @param icc ICC data output + * @param ctx EMV processing context * * @return Zero for success * @return Less than zero for errors. See @ref emv_error_t * @return Greater than zero for EMV processing outcome. See @ref emv_outcome_t */ -int emv_initiate_application_processing( - struct emv_ttl_t* ttl, - struct emv_app_t* selected_app, - const struct emv_tlv_list_t* source1, - const struct emv_tlv_list_t* source2, - struct emv_tlv_list_t* icc -); +int emv_initiate_application_processing(struct emv_ctx_t* ctx); /** * Read EMV application data by performing READ RECORD for all records diff --git a/tests/emv_initiate_application_processing_test.c b/tests/emv_initiate_application_processing_test.c index 158bd5e..a5e4e34 100644 --- a/tests/emv_initiate_application_processing_test.c +++ b/tests/emv_initiate_application_processing_test.c @@ -34,18 +34,19 @@ #include "print_helpers.h" // Reuse source data for all tests -const struct emv_tlv_t test_source1_data[] = { +const struct emv_tlv_t test_param_data[] = { { {{ EMV_TAG_9C_TRANSACTION_TYPE, 1, (uint8_t[]){ 0x09 }, 0 }}, NULL }, { {{ EMV_TAG_9A_TRANSACTION_DATE, 3, (uint8_t[]){ 0x24, 0x02, 0x17 }, 0 }}, NULL }, { {{ EMV_TAG_5F2A_TRANSACTION_CURRENCY_CODE, 2, (uint8_t[]){ 0x09, 0x78 }, 0 }}, NULL }, { {{ EMV_TAG_9F02_AMOUNT_AUTHORISED_NUMERIC, 6, (uint8_t[]){ 0x00, 0x01, 0x23, 0x45, 0x67, 0x89 }, 0 }}, NULL }, { {{ EMV_TAG_9F03_AMOUNT_OTHER_NUMERIC, 6, (uint8_t[]){ 0x00, 0x09, 0x87, 0x65, 0x43, 0x21 }, 0 }}, NULL }, + { {{ EMV_TAG_9F33_TERMINAL_CAPABILITIES, 3, (uint8_t[]){ 0x60, 0xF0, 0xC8 }, 0 }}, NULL }, // Override 9F33 in config }; -const struct emv_tlv_t test_source2_data[] = { +const struct emv_tlv_t test_config_data[] = { { {{ EMV_TAG_9F1A_TERMINAL_COUNTRY_CODE, 2, (uint8_t[]){ 0x05, 0x28 }, 0 }}, NULL }, - { {{ EMV_TAG_9F33_TERMINAL_CAPABILITIES, 3, (uint8_t[]){ 0x60, 0xF0, 0xC8 }, 0 }}, NULL }, - { {{ EMV_TAG_9F37_UNPREDICTABLE_NUMBER, 4, (uint8_t[]){ 0xDE, 0xAD, 0xBE, 0xEF }, 0 }}, NULL }, - { {{ EMV_TAG_95_TERMINAL_VERIFICATION_RESULTS, 5, (uint8_t[]){ 0x12, 0x34, 0x55, 0x43, 0x21 }, 0 }}, NULL }, + { {{ EMV_TAG_9F33_TERMINAL_CAPABILITIES, 3, (uint8_t[]){ 0x60, 0xFD, 0xC8 }, 0 }}, NULL }, // Overridden by params + { {{ EMV_TAG_9F35_TERMINAL_TYPE, 1, (uint8_t[]){ 0x22 }, 0 }}, NULL }, + { {{ EMV_TAG_9F40_ADDITIONAL_TERMINAL_CAPABILITIES, 5, (uint8_t[]){ 0xFA, 0x00, 0xF0, 0xA3, 0xFF }, 0 }}, NULL }, }; static const uint8_t test1_fci[] = { 0x6F, 0x12, 0x84, 0x07, 0xA0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x03, 0xA5, 0x07, 0x50, 0x05, 0x44, 0x65, 0x62, 0x69, 0x74 }; @@ -104,7 +105,7 @@ static const struct xpdu_t test6_apdu_list[] = { { 0 } }; -static int populate_source( +static int populate_tlv_list( const struct emv_tlv_t* tlv_array, size_t tlv_array_count, struct emv_tlv_list_t* source @@ -128,10 +129,7 @@ int main(void) int r; struct emv_cardreader_emul_ctx_t emul_ctx; struct emv_ttl_t ttl; - struct emv_tlv_list_t source1 = EMV_TLV_LIST_INIT; - struct emv_tlv_list_t source2 = EMV_TLV_LIST_INIT; - struct emv_app_t* app = NULL; - struct emv_tlv_list_t icc = EMV_TLV_LIST_INIT; + struct emv_ctx_t emv; const struct emv_tlv_t* aip; const struct emv_tlv_t* afl; @@ -139,13 +137,20 @@ int main(void) ttl.cardreader.ctx = &emul_ctx; ttl.cardreader.trx = &emv_cardreader_emul; + r = emv_ctx_init(&emv, &ttl); + if (r) { + fprintf(stderr, "emv_ctx_init() failed; r=%d\n", r); + r = 1; + goto exit; + } + // Populate data sources - r = populate_source(test_source1_data, sizeof(test_source1_data) / sizeof(test_source1_data[0]), &source1); + r = populate_tlv_list(test_param_data, sizeof(test_param_data) / sizeof(test_param_data[0]), &emv.params); if (r) { fprintf(stderr, "populate_source() failed; r=%d\n", r); return 1; } - r = populate_source(test_source2_data, sizeof(test_source2_data) / sizeof(test_source2_data[0]), &source2); + r = populate_tlv_list(test_config_data, sizeof(test_config_data) / sizeof(test_config_data[0]), &emv.config); if (r) { fprintf(stderr, "populate_source() failed; r=%d\n", r); return 1; @@ -162,22 +167,16 @@ int main(void) } printf("\nTest 1: No PDOL and GPO response format 1...\n"); - app = emv_app_create_from_fci(test1_fci, sizeof(test1_fci)); - if (!app) { - fprintf(stderr, "emv_app_create_from_fci() failed; app=%p\n", app); + emv.selected_app = emv_app_create_from_fci(test1_fci, sizeof(test1_fci)); + if (!emv.selected_app) { + fprintf(stderr, "emv_app_create_from_fci() failed; selected_app=%p\n", emv.selected_app); r = 1; goto exit; } emul_ctx.xpdu_list = test1_apdu_list; emul_ctx.xpdu_current = NULL; - emv_tlv_list_clear(&icc); - r = emv_initiate_application_processing( - &ttl, - app, - &source1, - &source2, - &icc - ); + emv_tlv_list_clear(&emv.icc); + r = emv_initiate_application_processing(&emv); if (r) { fprintf(stderr, "emv_initiate_application_processing() failed; error %d: %s\n", r, r < 0 ? emv_error_get_string(r) : emv_outcome_get_string(r)); r = 1; @@ -188,12 +187,12 @@ int main(void) r = 1; goto exit; } - if (emv_tlv_list_is_empty(&icc)) { + if (emv_tlv_list_is_empty(&emv.icc)) { fprintf(stderr, "ICC list unexpectedly empty\n"); r = 1; goto exit; } - aip = emv_tlv_list_find_const(&icc, EMV_TAG_82_APPLICATION_INTERCHANGE_PROFILE); + aip = emv_tlv_list_find_const(&emv.icc, EMV_TAG_82_APPLICATION_INTERCHANGE_PROFILE); if (!aip) { fprintf(stderr, "Failed to find AIP\n"); r = 1; @@ -208,7 +207,7 @@ int main(void) r = 1; goto exit; } - afl = emv_tlv_list_find_const(&icc, EMV_TAG_94_APPLICATION_FILE_LOCATOR); + afl = emv_tlv_list_find_const(&emv.icc, EMV_TAG_94_APPLICATION_FILE_LOCATOR); if (!afl) { fprintf(stderr, "Failed to find AFL\n"); r = 1; @@ -223,27 +222,21 @@ int main(void) r = 1; goto exit; } - emv_app_free(app); - app = NULL; + emv_app_free(emv.selected_app); + emv.selected_app = NULL; printf("Success\n"); printf("\nTest 2: No PDOL and GPO response format 2...\n"); - app = emv_app_create_from_fci(test2_fci, sizeof(test2_fci)); - if (!app) { - fprintf(stderr, "emv_app_create_from_fci() failed; app=%p\n", app); + emv.selected_app = emv_app_create_from_fci(test2_fci, sizeof(test2_fci)); + if (!emv.selected_app) { + fprintf(stderr, "emv_app_create_from_fci() failed; selected_app=%p\n", emv.selected_app); r = 1; goto exit; } emul_ctx.xpdu_list = test2_apdu_list; emul_ctx.xpdu_current = NULL; - emv_tlv_list_clear(&icc); - r = emv_initiate_application_processing( - &ttl, - app, - &source1, - &source2, - &icc - ); + emv_tlv_list_clear(&emv.icc); + r = emv_initiate_application_processing(&emv); if (r) { fprintf(stderr, "emv_initiate_application_processing() failed; error %d: %s\n", r, r < 0 ? emv_error_get_string(r) : emv_outcome_get_string(r)); r = 1; @@ -254,12 +247,12 @@ int main(void) r = 1; goto exit; } - if (emv_tlv_list_is_empty(&icc)) { + if (emv_tlv_list_is_empty(&emv.icc)) { fprintf(stderr, "ICC list unexpectedly empty\n"); r = 1; goto exit; } - aip = emv_tlv_list_find_const(&icc, EMV_TAG_82_APPLICATION_INTERCHANGE_PROFILE); + aip = emv_tlv_list_find_const(&emv.icc, EMV_TAG_82_APPLICATION_INTERCHANGE_PROFILE); if (!aip) { fprintf(stderr, "Failed to find AIP\n"); r = 1; @@ -274,7 +267,7 @@ int main(void) r = 1; goto exit; } - afl = emv_tlv_list_find_const(&icc, EMV_TAG_94_APPLICATION_FILE_LOCATOR); + afl = emv_tlv_list_find_const(&emv.icc, EMV_TAG_94_APPLICATION_FILE_LOCATOR); if (!afl) { fprintf(stderr, "Failed to find AFL\n"); r = 1; @@ -289,27 +282,21 @@ int main(void) r = 1; goto exit; } - emv_app_free(app); - app = NULL; + emv_app_free(emv.selected_app); + emv.selected_app = NULL; printf("Success\n"); printf("\nTest 3: PDOL present and GPO status 6985...\n"); - app = emv_app_create_from_fci(test3_fci, sizeof(test3_fci)); - if (!app) { - fprintf(stderr, "emv_app_create_from_fci() failed; app=%p\n", app); + emv.selected_app = emv_app_create_from_fci(test3_fci, sizeof(test3_fci)); + if (!emv.selected_app) { + fprintf(stderr, "emv_app_create_from_fci() failed; selected_app=%p\n", emv.selected_app); r = 1; goto exit; } emul_ctx.xpdu_list = test3_apdu_list; emul_ctx.xpdu_current = NULL; - emv_tlv_list_clear(&icc); - r = emv_initiate_application_processing( - &ttl, - app, - &source1, - &source2, - &icc - ); + emv_tlv_list_clear(&emv.icc); + r = emv_initiate_application_processing(&emv); if (r != EMV_OUTCOME_GPO_NOT_ACCEPTED) { fprintf(stderr, "emv_initiate_application_processing() did not return EMV_OUTCOME_GPO_NOT_ACCEPTED; error %d: %s\n", r, r < 0 ? emv_error_get_string(r) : emv_outcome_get_string(r)); r = 1; @@ -320,32 +307,26 @@ int main(void) r = 1; goto exit; } - if (!emv_tlv_list_is_empty(&icc)) { + if (!emv_tlv_list_is_empty(&emv.icc)) { fprintf(stderr, "ICC list unexpectedly NOT empty\n"); r = 1; goto exit; } - emv_app_free(app); - app = NULL; + emv_app_free(emv.selected_app); + emv.selected_app = NULL; printf("Success\n"); printf("\nTest 4: PDOL present and GPO response format 1..\n"); - app = emv_app_create_from_fci(test4_fci, sizeof(test4_fci)); - if (!app) { - fprintf(stderr, "emv_app_create_from_fci() failed; app=%p\n", app); + emv.selected_app = emv_app_create_from_fci(test4_fci, sizeof(test4_fci)); + if (!emv.selected_app) { + fprintf(stderr, "emv_app_create_from_fci() failed; selected_app=%p\n", emv.selected_app); r = 1; goto exit; } emul_ctx.xpdu_list = test4_apdu_list; emul_ctx.xpdu_current = NULL; - emv_tlv_list_clear(&icc); - r = emv_initiate_application_processing( - &ttl, - app, - &source1, - &source2, - &icc - ); + emv_tlv_list_clear(&emv.icc); + r = emv_initiate_application_processing(&emv); if (r) { fprintf(stderr, "emv_initiate_application_processing() failed; error %d: %s\n", r, r < 0 ? emv_error_get_string(r) : emv_outcome_get_string(r)); r = 1; @@ -356,12 +337,12 @@ int main(void) r = 1; goto exit; } - if (emv_tlv_list_is_empty(&icc)) { + if (emv_tlv_list_is_empty(&emv.icc)) { fprintf(stderr, "ICC list unexpectedly empty\n"); r = 1; goto exit; } - aip = emv_tlv_list_find_const(&icc, EMV_TAG_82_APPLICATION_INTERCHANGE_PROFILE); + aip = emv_tlv_list_find_const(&emv.icc, EMV_TAG_82_APPLICATION_INTERCHANGE_PROFILE); if (!aip) { fprintf(stderr, "Failed to find AIP\n"); r = 1; @@ -376,7 +357,7 @@ int main(void) r = 1; goto exit; } - afl = emv_tlv_list_find_const(&icc, EMV_TAG_94_APPLICATION_FILE_LOCATOR); + afl = emv_tlv_list_find_const(&emv.icc, EMV_TAG_94_APPLICATION_FILE_LOCATOR); if (!afl) { fprintf(stderr, "Failed to find AFL\n"); r = 1; @@ -391,27 +372,21 @@ int main(void) r = 1; goto exit; } - emv_app_free(app); - app = NULL; + emv_app_free(emv.selected_app); + emv.selected_app = NULL; printf("Success\n"); printf("\nTest 5: Invalid PDOL length and no GPO processing...\n"); - app = emv_app_create_from_fci(test5_fci, sizeof(test5_fci)); - if (!app) { - fprintf(stderr, "emv_app_create_from_fci() failed; app=%p\n", app); + emv.selected_app = emv_app_create_from_fci(test5_fci, sizeof(test5_fci)); + if (!emv.selected_app) { + fprintf(stderr, "emv_app_create_from_fci() failed; selected_app=%p\n", emv.selected_app); r = 1; goto exit; } emul_ctx.xpdu_list = test5_apdu_list; emul_ctx.xpdu_current = NULL; - emv_tlv_list_clear(&icc); - r = emv_initiate_application_processing( - &ttl, - app, - &source1, - &source2, - &icc - ); + emv_tlv_list_clear(&emv.icc); + r = emv_initiate_application_processing(&emv); if (r != EMV_OUTCOME_CARD_ERROR) { fprintf(stderr, "emv_initiate_application_processing() did not return EMV_OUTCOME_CARD_ERROR; error %d: %s\n", r, r < 0 ? emv_error_get_string(r) : emv_outcome_get_string(r)); r = 1; @@ -422,32 +397,26 @@ int main(void) r = 1; goto exit; } - if (!emv_tlv_list_is_empty(&icc)) { + if (!emv_tlv_list_is_empty(&emv.icc)) { fprintf(stderr, "ICC list unexpectedly NOT empty\n"); r = 1; goto exit; } - emv_app_free(app); - app = NULL; + emv_app_free(emv.selected_app); + emv.selected_app = NULL; printf("Success\n"); printf("\nTest 6: PDOL present and GPO status 6A81...\n"); - app = emv_app_create_from_fci(test6_fci, sizeof(test6_fci)); - if (!app) { - fprintf(stderr, "emv_app_create_from_fci() failed; app=%p\n", app); + emv.selected_app = emv_app_create_from_fci(test6_fci, sizeof(test6_fci)); + if (!emv.selected_app) { + fprintf(stderr, "emv_app_create_from_fci() failed; selected_app=%p\n", emv.selected_app); r = 1; goto exit; } emul_ctx.xpdu_list = test6_apdu_list; emul_ctx.xpdu_current = NULL; - emv_tlv_list_clear(&icc); - r = emv_initiate_application_processing( - &ttl, - app, - &source1, - &source2, - &icc - ); + emv_tlv_list_clear(&emv.icc); + r = emv_initiate_application_processing(&emv); if (r != EMV_OUTCOME_CARD_ERROR) { fprintf(stderr, "emv_initiate_application_processing() did not return EMV_OUTCOME_CARD_ERROR; error %d: %s\n", r, r < 0 ? emv_error_get_string(r) : emv_outcome_get_string(r)); r = 1; @@ -458,13 +427,13 @@ int main(void) r = 1; goto exit; } - if (!emv_tlv_list_is_empty(&icc)) { + if (!emv_tlv_list_is_empty(&emv.icc)) { fprintf(stderr, "ICC list unexpectedly NOT empty\n"); r = 1; goto exit; } - emv_app_free(app); - app = NULL; + emv_app_free(emv.selected_app); + emv.selected_app = NULL; printf("Success\n"); // Success @@ -472,12 +441,7 @@ int main(void) goto exit; exit: - emv_tlv_list_clear(&source1); - emv_tlv_list_clear(&source2); - if (app) { - emv_app_free(app); - } - emv_tlv_list_clear(&icc); + emv_ctx_clear(&emv); return r; } diff --git a/tools/emv-tool.c b/tools/emv-tool.c index 8db2e52..8b44b69 100644 --- a/tools/emv-tool.c +++ b/tools/emv-tool.c @@ -855,13 +855,7 @@ int main(int argc, char** argv) } printf("\nInitiate application processing:\n"); - r = emv_initiate_application_processing( - emv.ttl, - emv.selected_app, - &emv.params, - &emv.config, - &emv.icc - ); + r = emv_initiate_application_processing(&emv); if (r < 0) { printf("ERROR: %s\n", emv_error_get_string(r)); goto emv_exit;