Skip to content

Commit

Permalink
Stringify Terminal Risk Management Data (field 9F1D)
Browse files Browse the repository at this point in the history
  • Loading branch information
leonlynch committed Jan 31, 2024
1 parent 16001bc commit ddb3d60
Show file tree
Hide file tree
Showing 6 changed files with 263 additions and 0 deletions.
40 changes: 40 additions & 0 deletions src/emv_fields.h
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,46 @@ enum emv_iad_format_t {
#define EMV_IAD_VSDC_CVR_BYTE5_CDCVM_PERFORMED (0x02) ///< Card Verification Results (CVR): CDCVM successfully performed
#define EMV_IAD_VSDC_CVR_BYTE5_EMV_SESSION_KEY (0x01) ///< Card Verification Results (CVR): Secure Messaging uses EMV Session key-based derivation

// Terminal Risk Management Data (field 9F1D) byte 1
// See EMV Contactless Book C-2 v2.11, Annex A.1.161
// See M/Chip Requirements for Contact and Contactless, 28 November 2023, Chapter 5, Terminal Risk Management Data
#define EMV_TRMD_BYTE1_RESTART_SUPPORTED (0x80) ///< Terminal Risk Management Data: Restart supported
#define EMV_TRMD_BYTE1_ENCIPHERED_PIN_ONLINE_CONTACTLESS (0x40) ///< Terminal Risk Management Data: Enciphered PIN verified online (Contactless)
#define EMV_TRMD_BYTE1_SIGNATURE_CONTACTLESS (0x20) ///< Terminal Risk Management Data: Signature (paper) (Contactless)
#define EMV_TRMD_BYTE1_ENCIPHERED_PIN_OFFLINE_CONTACTLESS (0x10) ///< Terminal Risk Management Data: Enciphered PIN verification performed by ICC (Contactless)
#define EMV_TRMD_BYTE1_NO_CVM_CONTACTLESS (0x08) ///< Terminal Risk Management Data: No CVM required (Contactless)
#define EMV_TRMD_BYTE1_CDCVM_CONTACTLESS (0x04) ///< Terminal Risk Management Data: CDCVM (Contactless)
#define EMV_TRMD_BYTE1_PLAINTEXT_PIN_OFFLINE_CONTACTLESS (0x02) ///< Terminal Risk Management Data: Plaintext PIN verification performed by ICC (Contactless)
#define EMV_TRMD_BYTE1_PRESENT_AND_HOLD_SUPPORTED (0x01) ///< Terminal Risk Management Data: Present and Hold supported

// Terminal Risk Management Data (field 9F1D) byte 2
// See EMV Contactless Book C-2 v2.11, Annex A.1.161
// See EMV Contactless Book C-8 v1.1, Annex A.1.129
// See M/Chip Requirements for Contact and Contactless, 28 November 2023, Chapter 5, Terminal Risk Management Data
#define EMV_TRMD_BYTE2_CVM_LIMIT_EXCEEDED (0x80) ///< Terminal Risk Management Data: CVM Limit exceeded
#define EMV_TRMD_BYTE2_ENCIPHERED_PIN_ONLINE_CONTACT (0x40) ///< Terminal Risk Management Data: Enciphered PIN verified online (Contact)
#define EMV_TRMD_BYTE2_SIGNATURE_CONTACT (0x20) ///< Terminal Risk Management Data: Signature (paper) (Contact)
#define EMV_TRMD_BYTE2_ENCIPHERED_PIN_OFFLINE_CONTACT (0x10) ///< Terminal Risk Management Data: Enciphered PIN verification performed by ICC (Contact)
#define EMV_TRMD_BYTE2_NO_CVM_CONTACT (0x08) ///< Terminal Risk Management Data: No CVM required (Contact)
#define EMV_TRMD_BYTE2_CDCVM_CONTACT (0x04) ///< Terminal Risk Management Data: CDCVM (Contact)
#define EMV_TRMD_BYTE2_PLAINTEXT_PIN_OFFLINE_CONTACT (0x02) ///< Terminal Risk Management Data: Plaintext PIN verification performed by ICC (Contact)
#define EMV_TRMD_BYTE2_RFU (0x01) ///< Terminal Risk Management Data: RFU

// Terminal Risk Management Data (field 9F1D) byte 3
// See EMV Contactless Book C-2 v2.11, Annex A.1.161
// See M/Chip Requirements for Contact and Contactless, 28 November 2023, Chapter 5, Terminal Risk Management Data
#define EMV_TRMD_BYTE3_MAGSTRIPE_MODE_CONTACTLESS_NOT_SUPPORTED (0x80) ///< Terminal Risk Management Data: Mag-stripe mode contactless transactions not supported
#define EMV_TRMD_BYTE3_EMV_MODE_CONTACTLESS_NOT_SUPPORTED (0x40) ///< Terminal Risk Management Data: EMV mode contactless transactions not supported
#define EMV_TRMD_BYTE3_CDCVM_WITHOUT_CDA_SUPPORTED (0x20) ///< Terminal Risk Management Data: CDCVM without CDA supported
#define EMV_TRMD_BYTE3_RFU (0x1F) ///< Terminal Risk Management Data: RFU

// Terminal Risk Management Data (field 9F1D) byte 4
// See EMV Contactless Book C-2 v2.11, Annex A.1.161
// See EMV Contactless Book C-8 v1.1, Annex A.1.129
#define EMV_TRMD_BYTE4_CDCVM_BYPASS_REQUESTED (0x80) ///< Terminal Risk Management Data: CDCVM bypass requested
#define EMV_TRMD_BYTE4_SCA_EXEMPT (0x40) ///< Terminal Risk Management Data: SCA exempt
#define EMV_TRMD_BYTE4_RFU (0x3F) ///< Terminal Risk Management Data: RFU

// Terminal Transaction Qualifiers (field 9F66) byte 1
// See EMV Contactless Book A v2.10, 5.7, Table 5-4
// See EMV Contactless Book C-3 v2.10, Annex A.2
Expand Down
138 changes: 138 additions & 0 deletions src/emv_strings.c
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,15 @@ int emv_tlv_get_info(
info->format = EMV_FORMAT_AN;
return emv_tlv_value_get_string(tlv, info->format, 8, value_str, value_str_len);

case EMV_TAG_9F1D_TERMINAL_RISK_MANAGEMENT_DATA:
info->tag_name = "Terminal Risk Management Data";
info->tag_desc =
"Application-specific value used by the contactless card or "
"payment device for risk management purposes. All RFU bits "
"must be set to zero.";
info->format = EMV_FORMAT_B;
return emv_terminal_risk_management_data_get_string_list(tlv->value, tlv->length, value_str, value_str_len);

case EMV_TAG_9F1E_IFD_SERIAL_NUMBER:
info->tag_name = "Interface Device (IFD) Serial Number";
info->tag_desc =
Expand Down Expand Up @@ -4433,6 +4442,135 @@ int emv_iad_get_string_list(
}
}

int emv_terminal_risk_management_data_get_string_list(
const uint8_t* trmd,
size_t trmd_len,
char* str,
size_t str_len
)
{
struct str_itr_t itr;

if (!trmd || !trmd_len) {
return -1;
}

if (!str || !str_len) {
// Caller didn't want the value string
return 0;
}

if (trmd_len != 8) {
// Terminal Risk Management Data (field 9F1D) must be 8 bytes
return 1;
}

emv_str_list_init(&itr, str, str_len);

// Terminal Risk Management Data (field 9F1D) byte 1
// See EMV Contactless Book C-2 v2.11, Annex A.1.161
// See M/Chip Requirements for Contact and Contactless, 28 November 2023, Chapter 5, Terminal Risk Management Data
if (trmd[0] & EMV_TRMD_BYTE1_RESTART_SUPPORTED) {
// NOTE: EMV Contactless Book C-8 v1.1, Annex A.1.129 does not define
// this bit and it avoids confusion if no string is provided when this
// bit is unset
emv_str_list_add(&itr, "Restart supported");
}
if (trmd[0] & EMV_TRMD_BYTE1_ENCIPHERED_PIN_ONLINE_CONTACTLESS) {
emv_str_list_add(&itr, "Enciphered PIN verified online (Contactless)");
}
if (trmd[0] & EMV_TRMD_BYTE1_SIGNATURE_CONTACTLESS) {
emv_str_list_add(&itr, "Signature (paper) (Contactless)");
}
if (trmd[0] & EMV_TRMD_BYTE1_ENCIPHERED_PIN_OFFLINE_CONTACTLESS) {
emv_str_list_add(&itr, "Enciphered PIN verification performed by ICC (Contactless)");
}
if (trmd[0] & EMV_TRMD_BYTE1_NO_CVM_CONTACTLESS) {
emv_str_list_add(&itr, "No CVM required (Contactless)");
}
if (trmd[0] & EMV_TRMD_BYTE1_CDCVM_CONTACTLESS) {
emv_str_list_add(&itr, "CDCVM (Contactless)");
}
if (trmd[0] & EMV_TRMD_BYTE1_PLAINTEXT_PIN_OFFLINE_CONTACTLESS) {
emv_str_list_add(&itr, "Plaintext PIN verification performed by ICC (Contactless)");
}
if (trmd[0] & EMV_TRMD_BYTE1_PRESENT_AND_HOLD_SUPPORTED) {
// NOTE: EMV Contactless Book C-8 v1.1, Annex A.1.129 does not define
// this bit and it avoids confusion if no string is provided when this
// bit is unset
emv_str_list_add(&itr, "Present and Hold supported");
}

// Terminal Risk Management Data (field 9F1D) byte 2
// See EMV Contactless Book C-2 v2.11, Annex A.1.161
// See EMV Contactless Book C-8 v1.1, Annex A.1.129
// See M/Chip Requirements for Contact and Contactless, 28 November 2023, Chapter 5, Terminal Risk Management Data
if (trmd[1] & EMV_TRMD_BYTE2_CVM_LIMIT_EXCEEDED) {
emv_str_list_add(&itr, "CVM Limit exceeded");
}
if (trmd[1] & EMV_TRMD_BYTE2_ENCIPHERED_PIN_ONLINE_CONTACT) {
emv_str_list_add(&itr, "Enciphered PIN verified online (Contact)");
}
if (trmd[1] & EMV_TRMD_BYTE2_SIGNATURE_CONTACT) {
emv_str_list_add(&itr, "Signature (paper) (Contact)");
}
if (trmd[1] & EMV_TRMD_BYTE2_ENCIPHERED_PIN_OFFLINE_CONTACT) {
emv_str_list_add(&itr, "Enciphered PIN verification performed by ICC (Contact)");
}
if (trmd[1] & EMV_TRMD_BYTE2_NO_CVM_CONTACT) {
emv_str_list_add(&itr, "No CVM required (Contact)");
}
if (trmd[1] & EMV_TRMD_BYTE2_CDCVM_CONTACT) {
emv_str_list_add(&itr, "CDCVM (Contact)");
}
if (trmd[1] & EMV_TRMD_BYTE2_PLAINTEXT_PIN_OFFLINE_CONTACT) {
emv_str_list_add(&itr, "Plaintext PIN verification performed by ICC (Contact)");
}

// Terminal Risk Management Data (field 9F1D) byte 3
// See EMV Contactless Book C-2 v2.11, Annex A.1.161
// See M/Chip Requirements for Contact and Contactless, 28 November 2023, Chapter 5, Terminal Risk Management Data
if (trmd[2] & EMV_TRMD_BYTE3_MAGSTRIPE_MODE_CONTACTLESS_NOT_SUPPORTED) {
// NOTE: EMV Contactless Book C-8 v1.1, Annex A.1.129 does not define
// this bit and it avoids confusion if no string is provided when this
// bit is unset
emv_str_list_add(&itr, "Mag-stripe mode contactless transactions not supported");
}
if (trmd[2] & EMV_TRMD_BYTE3_EMV_MODE_CONTACTLESS_NOT_SUPPORTED) {
// NOTE: EMV Contactless Book C-8 v1.1, Annex A.1.129 does not define
// this bit and it avoids confusion if no string is provided when this
// bit is unset
emv_str_list_add(&itr, "EMV mode contactless transactions not supported");
}
if (trmd[2] & EMV_TRMD_BYTE3_CDCVM_WITHOUT_CDA_SUPPORTED) {
// NOTE: EMV Contactless Book C-8 v1.1, Annex A.1.129 does not define
// this bit and it avoids confusion if no string is provided when this
// bit is unset
emv_str_list_add(&itr, "CDCVM without CDA supported");
}

// Terminal Risk Management Data (field 9F1D) byte 4
// See EMV Contactless Book C-2 v2.11, Annex A.1.161
// See EMV Contactless Book C-8 v1.1, Annex A.1.129
if (trmd[3] & EMV_TRMD_BYTE4_CDCVM_BYPASS_REQUESTED) {
emv_str_list_add(&itr, "CDCVM bypass requested");
}
if (trmd[3] & EMV_TRMD_BYTE4_SCA_EXEMPT) {
emv_str_list_add(&itr, "SCA exempt");
}

// Terminal Risk Management Data (field 9F1D) RFU bits
if ((trmd[1] & EMV_TRMD_BYTE2_RFU) ||
(trmd[2] & EMV_TRMD_BYTE3_RFU) ||
(trmd[3] & EMV_TRMD_BYTE4_RFU) ||
trmd[4] || trmd[5] || trmd[6] || trmd[7]
) {
emv_str_list_add(&itr, "RFU");
}

return 0;
}

int emv_ttq_get_string_list(
const uint8_t* ttq,
size_t ttq_len,
Expand Down
16 changes: 16 additions & 0 deletions src/emv_strings.h
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,22 @@ int emv_iad_get_string_list(
size_t str_len
);

/**
* Stringify Terminal Risk Management Data (field 9F1D)
* @note Strings in output buffer are delimited using "\n", including the last string
* @param trmd Terminal Risk Management Data field. Must be 8 bytes.
* @param trmd_len Length of Terminal Risk Management Data field. Must be 8 bytes.
* @param str String buffer output
* @param str_len Length of string buffer in bytes
* @return Zero for success. Less than zero for internal error. Greater than zero for parse error.
*/
int emv_terminal_risk_management_data_get_string_list(
const uint8_t* trmd,
size_t trmd_len,
char* str,
size_t str_len
);

/**
* Stringify Terminal Transaction Qualifiers (field 9F66)
* @note Strings in output buffer are delimited using "\n", including the last string
Expand Down
6 changes: 6 additions & 0 deletions src/emv_tags.h
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,12 @@ __BEGIN_DECLS
/// @remark Also used as the Card Acceptor Terminal Identification by ISO 8583. See EMV 4.4 Book 4, Annex C, Table 40
#define EMV_TAG_9F1C_TERMINAL_IDENTIFICATION (0x9F1C)

/// EMV tag 9F1D Terminal Risk Management Data
/// @remark See EMV Contactless Book C-2 v2.11, Annex A.1.161
/// @remark See EMV Contactless Book C-8 v1.1, Annex A.1.129
/// @remark See M/Chip Requirements for Contact and Contactless, 28 November 2023, Chapter 5, Terminal Risk Management Data
#define EMV_TAG_9F1D_TERMINAL_RISK_MANAGEMENT_DATA (0x9F1D)

/// EMV tag 9F1E Interface Device (IFD) Serial Number
#define EMV_TAG_9F1E_IFD_SERIAL_NUMBER (0x9F1E)

Expand Down
41 changes: 41 additions & 0 deletions tools/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,47 @@ if(TARGET emv-decode AND BUILD_TESTING)
PASS_REGULAR_EXPRESSION ${emv_decode_IAD_test5_regex}
)

add_test(NAME emv_decode_9F1D_test1
COMMAND emv-decode --terminal-risk-management-data 6C7A800000000000
--mcc-json ${MCC_JSON_BUILD_PATH}
)
string(CONCAT emv_decode_9F1D_test1_regex
"^Enciphered PIN verified online \\(Contactless\\)[\r\n]"
"Signature \\(paper\\) \\(Contactless\\)[\r\n]"
"No CVM required \\(Contactless\\)[\r\n]"
"CDCVM \\(Contactless\\)[\r\n]"
"Enciphered PIN verified online \\(Contact\\)[\r\n]"
"Signature \\(paper\\) \\(Contact\\)[\r\n]"
"Enciphered PIN verification performed by ICC \\(Contact\\)[\r\n]"
"No CVM required \\(Contact\\)[\r\n]"
"Plaintext PIN verification performed by ICC \\(Contact\\)[\r\n]"
"Mag-stripe mode contactless transactions not supported[\r\n]"
)
set_tests_properties(emv_decode_9F1D_test1
PROPERTIES
PASS_REGULAR_EXPRESSION ${emv_decode_9F1D_test1_regex}
)

add_test(NAME emv_decode_9F1D_test2
COMMAND emv-decode --9F1D 4C7A800000000000
--mcc-json ${MCC_JSON_BUILD_PATH}
)
string(CONCAT emv_decode_9F1D_test2_regex
"^Enciphered PIN verified online \\(Contactless\\)[\r\n]"
"No CVM required \\(Contactless\\)[\r\n]"
"CDCVM \\(Contactless\\)[\r\n]"
"Enciphered PIN verified online \\(Contact\\)[\r\n]"
"Signature \\(paper\\) \\(Contact\\)[\r\n]"
"Enciphered PIN verification performed by ICC \\(Contact\\)[\r\n]"
"No CVM required \\(Contact\\)[\r\n]"
"Plaintext PIN verification performed by ICC \\(Contact\\)[\r\n]"
"Mag-stripe mode contactless transactions not supported[\r\n]"
)
set_tests_properties(emv_decode_9F1D_test2
PROPERTIES
PASS_REGULAR_EXPRESSION ${emv_decode_9F1D_test2_regex}
)

if(WIN32)
# Ensure that tests can find required DLLs (if any)
# Assume that the PATH already contains the compiler runtime DLLs
Expand Down
22 changes: 22 additions & 0 deletions tools/emv-decode.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ enum emv_decode_mode_t {
EMV_DECODE_MASTERCARD_THIRD_PARTY_DATA,
EMV_DECODE_FFI,
EMV_DECODE_AMEX_ENH_CL_READER_CAPS,
EMV_DECODE_TERMINAL_RISK_MANAGEMENT_DATA,
EMV_DECODE_ISO3166_1,
EMV_DECODE_ISO4217,
EMV_DECODE_ISO639,
Expand Down Expand Up @@ -135,6 +136,8 @@ static struct argp_option argp_options[] = {
{ "mastercard-third-party-data", EMV_DECODE_MASTERCARD_THIRD_PARTY_DATA, NULL, 0, "Decode Mastercard Third Party Data (field 9F6E)" },
{ "visa-ffi", EMV_DECODE_FFI, NULL, 0, "Decode Visa Form Factor Indicator (field 9F6E)" },
{ "amex-enh-cl-reader-caps", EMV_DECODE_AMEX_ENH_CL_READER_CAPS, NULL, 0, "Decode Amex Enhanced Contactless Reader Capabilities (field 9F6E)" },
{ "terminal-risk-management-data", EMV_DECODE_TERMINAL_RISK_MANAGEMENT_DATA, NULL, 0, "Decode Terminal Risk Management Data (field 9F1D)" },
{ "9F1D", EMV_DECODE_TERMINAL_RISK_MANAGEMENT_DATA, NULL, OPTION_ALIAS },

{ NULL, 0, NULL, 0, "Other:", 4 },
{ "country", EMV_DECODE_ISO3166_1, NULL, 0, "Lookup country name by ISO 3166-1 alpha-2, alpha-3 or numeric code" },
Expand Down Expand Up @@ -255,6 +258,7 @@ static error_t argp_parser_helper(int key, char* arg, struct argp_state* state)
case EMV_DECODE_MASTERCARD_THIRD_PARTY_DATA:
case EMV_DECODE_FFI:
case EMV_DECODE_AMEX_ENH_CL_READER_CAPS:
case EMV_DECODE_TERMINAL_RISK_MANAGEMENT_DATA:
case EMV_DECODE_ISO3166_1:
case EMV_DECODE_ISO4217:
case EMV_DECODE_ISO639:
Expand Down Expand Up @@ -739,6 +743,24 @@ int main(int argc, char** argv)
break;
}

case EMV_DECODE_TERMINAL_RISK_MANAGEMENT_DATA: {
char str[2048];

if (data_len != 8) {
fprintf(stderr, "Terminal Risk Management Data (field 9F1D) must be exactly 8 bytes\n");
break;
}

r = emv_terminal_risk_management_data_get_string_list(data, data_len, str, sizeof(str));
if (r) {
fprintf(stderr, "Failed to parse Terminal Risk Management Data (field 9F1D)\n");
break;
}
printf("%s", str); // No \n required for string list

break;
}

case EMV_DECODE_ISO3166_1: {
const char* country;

Expand Down

0 comments on commit ddb3d60

Please sign in to comment.