Skip to content

Commit

Permalink
Populate terminal data fields for the current transaction
Browse files Browse the repository at this point in the history
The emv_initiate_application_processing() function will create these
fields and append them to the terminal data:
* Point-of-Service (POS) Entry Mode; field 9F39
* Application Identifier (AID) - terminal; field 9F06
* Transaction Status Information (TSI); field 9B
* Terminal Verification Results (TVR); field 95
  • Loading branch information
leonlynch committed May 11, 2024
1 parent 6919bbd commit 63f9cff
Show file tree
Hide file tree
Showing 5 changed files with 187 additions and 19 deletions.
80 changes: 78 additions & 2 deletions src/emv.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ int emv_ctx_reset(struct emv_ctx_t* ctx)

emv_tlv_list_clear(&ctx->params);
emv_tlv_list_clear(&ctx->icc);
emv_tlv_list_clear(&ctx->terminal);
emv_app_free(ctx->selected_app);
ctx->selected_app = NULL;

Expand Down Expand Up @@ -486,7 +487,10 @@ int emv_select_application(
return r;
}

int emv_initiate_application_processing(struct emv_ctx_t* ctx)
int emv_initiate_application_processing(
struct emv_ctx_t* ctx,
uint8_t pos_entry_mode
)
{
int r;
const struct emv_tlv_t* pdol;
Expand All @@ -501,8 +505,9 @@ int emv_initiate_application_processing(struct emv_ctx_t* ctx)
return EMV_ERROR_INVALID_PARAMETER;
}

// Clear existing ICC data list to avoid ambiguity
// Clear existing ICC data and terminal data lists to avoid ambiguity
emv_tlv_list_clear(&ctx->icc);
emv_tlv_list_clear(&ctx->terminal);

// Process PDOL, if available
// See EMV 4.4 Book 3, 10.1
Expand Down Expand Up @@ -611,6 +616,77 @@ int emv_initiate_application_processing(struct emv_ctx_t* ctx)
goto error;
}

// Create Point-of-Service (POS) Entry Mode (field 9F39)
r = emv_tlv_list_push(
&ctx->terminal,
EMV_TAG_9F39_POS_ENTRY_MODE,
1,
&pos_entry_mode,
0
);
if (r) {
emv_debug_trace_msg("emv_tlv_list_push() failed; r=%d", r);

// Internal error; terminate session
emv_debug_error("Internal error");
r = EMV_ERROR_INTERNAL;
goto error;
}

// Create Application Identifier (AID) - terminal (field 9F06)
// See EMV 4.4 Book 1, 12.4
r = emv_tlv_list_push(
&ctx->terminal,
EMV_TAG_9F06_AID,
ctx->selected_app->aid->length,
ctx->selected_app->aid->value,
0
);
if (r) {
emv_debug_trace_msg("emv_tlv_list_push() failed; r=%d", r);

// Internal error; terminate session
emv_debug_error("Internal error");
r = EMV_ERROR_INTERNAL;
goto error;
}

// Create Transaction Status Information (TSI, field 9B)
// See EMV 4.4 Book 3, 10.1
r = emv_tlv_list_push(
&ctx->terminal,
EMV_TAG_9B_TRANSACTION_STATUS_INFORMATION,
2,
(uint8_t[]){ 0x00, 0x00 },
0
);
if (r) {
emv_debug_trace_msg("emv_tlv_list_push() failed; r=%d", r);

// Internal error; terminate session
emv_debug_error("Internal error");
r = EMV_ERROR_INTERNAL;
goto error;
}

// Create Terminal Verification Results (TVR, field 95)
// See EMV 4.4 Book 3, 10.1
r = emv_tlv_list_push(
&ctx->terminal,
EMV_TAG_95_TERMINAL_VERIFICATION_RESULTS,
5,
(uint8_t[]){ 0x00, 0x00, 0x00, 0x00, 0x00 },
0
);
if (r) {
emv_debug_trace_msg("emv_tlv_list_push() failed; r=%d", r);

// Internal error; terminate session
emv_debug_error("Internal error");
r = EMV_ERROR_INTERNAL;
goto error;
}

// Success
r = 0;
goto exit;
Expand Down
25 changes: 21 additions & 4 deletions src/emv.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

#include <sys/cdefs.h>
#include <stddef.h>
#include <stdint.h>

__BEGIN_DECLS

Expand Down Expand Up @@ -110,6 +111,13 @@ struct emv_ctx_t {
* @ref emv_read_application_data().
*/
struct emv_tlv_list_t icc;

/**
* @brief Terminal data for the current transaction.
*
* Populated by @ref emv_initiate_application_processing().
*/
struct emv_tlv_list_t terminal;
};

/**
Expand Down Expand Up @@ -158,6 +166,7 @@ int emv_ctx_init(struct emv_ctx_t* ctx, struct emv_ttl_t* ttl);
* This function will clear these transaction specific context members:
* - @ref emv_ctx_t.params
* - @ref emv_ctx_t.icc
* - @ref emv_ctx_t.terminal
*
* And this function will preserve these members that can be reused for the
* next transaction:
Expand Down Expand Up @@ -268,20 +277,28 @@ int emv_select_application(
* - @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 list and append the output of
* GET PROCESSING OPTIONS.
* @note This functions clears @ref emv_ctx_t.icc and @ref emv_ctx_t.terminal
* and then populates them appropriately. Upon success, the selected
* application's TLV data will be moved to @ref emv_ctx_t.icc and the
* output of GET PROCESSING OPTIONS will be appended. Upon success,
* @ref emv_ctx_t.terminal will be populated with various fields,
* including @ref EMV_TAG_9F39_POS_ENTRY_MODE.
*
* @remark See EMV 4.4 Book 3, 10.1
* @remark See EMV 4.4 Book 4, 6.3.1
*
* @param ctx EMV processing context
* @param pos_entry_mode Point-of-Service (POS) Entry Mode (field 9F39) value.
* See @ref pos-entry-mode-values "values".
*
* @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_ctx_t* ctx);
int emv_initiate_application_processing(
struct emv_ctx_t* ctx,
uint8_t pos_entry_mode
);

/**
* Read EMV application data by performing READ RECORD for all records
Expand Down
13 changes: 9 additions & 4 deletions src/emv_fields.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,14 @@ __BEGIN_DECLS
#define EMV_TERM_CAPS_SECURITY_XDA (0x04) ///< Security Capability: Extended Data Authentication (XDA)
#define EMV_TERM_CAPS_SECURITY_RFU (0x13) ///< Security Capability: RFU

// Point-of-Service (POS) Entry Mode (field 9F39)
// See ISO 8583:1987, 4.3.14
// See ISO 8583:1993, A.8
// See ISO 8583:2021 J.2.2.1
/**
* @name Point-of-Service (POS) Entry Mode (field 9F39)
* @remark See ISO 8583:1987, 4.3.14
* @remark See ISO 8583:1993, A.8
* @remark See ISO 8583:2021 J.2.2.1
* @anchor pos-entry-mode-values
*/
/// @{
#define EMV_POS_ENTRY_MODE_UNKNOWN (0x00) ///< POS Entry Mode: Unknown
#define EMV_POS_ENTRY_MODE_MANUAL (0x01) ///< POS Entry Mode: Manual PAN entry
#define EMV_POS_ENTRY_MODE_MAG (0x02) ///< POS Entry Mode: Magnetic stripe
Expand All @@ -110,6 +114,7 @@ __BEGIN_DECLS
#define EMV_POS_ENTRY_MODE_CONTACTLESS_MAG (0x91) ///< POS Entry Mode: Auto entry via contactless magnetic stripe
#define EMV_POS_ENTRY_MODE_ICC_WITHOUT_CVV (0x95) ///< POS Entry Mode: Integrated circuit card (ICC). CVV may not be checked.
#define EMV_POS_ENTRY_MODE_ORIGINAL_TXN (0x99) ///< POS Entry Mode: Same as original transaction
/// @}

// Additional Terminal Capabilities (field 9F40) byte 1
// See EMV 4.4 Book 4, Annex A3, table 28
Expand Down
76 changes: 70 additions & 6 deletions tests/emv_initiate_application_processing_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "emv_tags.h"
#include "emv_ttl.h"
#include "emv_app.h"
#include "emv_fields.h"

#include <stddef.h>
#include <stdio.h>
Expand Down Expand Up @@ -176,7 +177,7 @@ int main(void)
emul_ctx.xpdu_list = test1_apdu_list;
emul_ctx.xpdu_current = NULL;
emv_tlv_list_clear(&emv.icc);
r = emv_initiate_application_processing(&emv);
r = emv_initiate_application_processing(&emv, EMV_POS_ENTRY_MODE_ICC_WITH_CVV);
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;
Expand Down Expand Up @@ -222,6 +223,22 @@ int main(void)
r = 1;
goto exit;
}
// This is ugly but it is what it is
if (emv_tlv_list_is_empty(&emv.terminal) ||
emv.terminal.front->tag != EMV_TAG_9F39_POS_ENTRY_MODE ||
!emv.terminal.front->next ||
emv.terminal.front->next->tag != EMV_TAG_9F06_AID ||
!emv.terminal.front->next->next ||
emv.terminal.front->next->next->tag != EMV_TAG_9B_TRANSACTION_STATUS_INFORMATION ||
!emv.terminal.front->next->next->next ||
emv.terminal.front->next->next->next->tag != EMV_TAG_95_TERMINAL_VERIFICATION_RESULTS ||
emv.terminal.front->next->next->next->next
) {
fprintf(stderr, "Unexpected terminal data list state\n");
print_emv_tlv_list(&emv.terminal);
r = 1;
goto exit;
}
emv_app_free(emv.selected_app);
emv.selected_app = NULL;
printf("Success\n");
Expand All @@ -236,7 +253,7 @@ int main(void)
emul_ctx.xpdu_list = test2_apdu_list;
emul_ctx.xpdu_current = NULL;
emv_tlv_list_clear(&emv.icc);
r = emv_initiate_application_processing(&emv);
r = emv_initiate_application_processing(&emv, EMV_POS_ENTRY_MODE_ICC_WITH_CVV);
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;
Expand Down Expand Up @@ -282,6 +299,22 @@ int main(void)
r = 1;
goto exit;
}
// This is ugly but it is what it is
if (emv_tlv_list_is_empty(&emv.terminal) ||
emv.terminal.front->tag != EMV_TAG_9F39_POS_ENTRY_MODE ||
!emv.terminal.front->next ||
emv.terminal.front->next->tag != EMV_TAG_9F06_AID ||
!emv.terminal.front->next->next ||
emv.terminal.front->next->next->tag != EMV_TAG_9B_TRANSACTION_STATUS_INFORMATION ||
!emv.terminal.front->next->next->next ||
emv.terminal.front->next->next->next->tag != EMV_TAG_95_TERMINAL_VERIFICATION_RESULTS ||
emv.terminal.front->next->next->next->next
) {
fprintf(stderr, "Unexpected terminal data list state\n");
print_emv_tlv_list(&emv.terminal);
r = 1;
goto exit;
}
emv_app_free(emv.selected_app);
emv.selected_app = NULL;
printf("Success\n");
Expand All @@ -296,7 +329,7 @@ int main(void)
emul_ctx.xpdu_list = test3_apdu_list;
emul_ctx.xpdu_current = NULL;
emv_tlv_list_clear(&emv.icc);
r = emv_initiate_application_processing(&emv);
r = emv_initiate_application_processing(&emv, EMV_POS_ENTRY_MODE_ICC_WITH_CVV);
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;
Expand All @@ -312,6 +345,11 @@ int main(void)
r = 1;
goto exit;
}
if (!emv_tlv_list_is_empty(&emv.terminal)) {
fprintf(stderr, "Terminal list unexpectedly NOT empty\n");
r = 1;
goto exit;
}
emv_app_free(emv.selected_app);
emv.selected_app = NULL;
printf("Success\n");
Expand All @@ -326,7 +364,7 @@ int main(void)
emul_ctx.xpdu_list = test4_apdu_list;
emul_ctx.xpdu_current = NULL;
emv_tlv_list_clear(&emv.icc);
r = emv_initiate_application_processing(&emv);
r = emv_initiate_application_processing(&emv, EMV_POS_ENTRY_MODE_ICC_WITH_CVV);
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;
Expand Down Expand Up @@ -372,6 +410,22 @@ int main(void)
r = 1;
goto exit;
}
// This is ugly but it is what it is
if (emv_tlv_list_is_empty(&emv.terminal) ||
emv.terminal.front->tag != EMV_TAG_9F39_POS_ENTRY_MODE ||
!emv.terminal.front->next ||
emv.terminal.front->next->tag != EMV_TAG_9F06_AID ||
!emv.terminal.front->next->next ||
emv.terminal.front->next->next->tag != EMV_TAG_9B_TRANSACTION_STATUS_INFORMATION ||
!emv.terminal.front->next->next->next ||
emv.terminal.front->next->next->next->tag != EMV_TAG_95_TERMINAL_VERIFICATION_RESULTS ||
emv.terminal.front->next->next->next->next
) {
fprintf(stderr, "Unexpected terminal data list state\n");
print_emv_tlv_list(&emv.terminal);
r = 1;
goto exit;
}
emv_app_free(emv.selected_app);
emv.selected_app = NULL;
printf("Success\n");
Expand All @@ -386,7 +440,7 @@ int main(void)
emul_ctx.xpdu_list = test5_apdu_list;
emul_ctx.xpdu_current = NULL;
emv_tlv_list_clear(&emv.icc);
r = emv_initiate_application_processing(&emv);
r = emv_initiate_application_processing(&emv, EMV_POS_ENTRY_MODE_ICC_WITH_CVV);
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;
Expand All @@ -402,6 +456,11 @@ int main(void)
r = 1;
goto exit;
}
if (!emv_tlv_list_is_empty(&emv.terminal)) {
fprintf(stderr, "Terminal list unexpectedly NOT empty\n");
r = 1;
goto exit;
}
emv_app_free(emv.selected_app);
emv.selected_app = NULL;
printf("Success\n");
Expand All @@ -416,7 +475,7 @@ int main(void)
emul_ctx.xpdu_list = test6_apdu_list;
emul_ctx.xpdu_current = NULL;
emv_tlv_list_clear(&emv.icc);
r = emv_initiate_application_processing(&emv);
r = emv_initiate_application_processing(&emv, EMV_POS_ENTRY_MODE_ICC_WITH_CVV);
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;
Expand All @@ -432,6 +491,11 @@ int main(void)
r = 1;
goto exit;
}
if (!emv_tlv_list_is_empty(&emv.terminal)) {
fprintf(stderr, "Terminal list unexpectedly NOT empty\n");
r = 1;
goto exit;
}
emv_app_free(emv.selected_app);
emv.selected_app = NULL;
printf("Success\n");
Expand Down
Loading

0 comments on commit 63f9cff

Please sign in to comment.