Skip to content

Commit 0c94724

Browse files
committed
Implement initial EMV Terminal Risk Management
* emv_terminal_risk_management() is responsible for ensuring that the mandatory configuration fields and mandatory transaction parameters are present before proceeding with the risk management checks. * emv_tal_get_data() is responsible for indicating that the terminal may continue the card session if sw1-sw2 is not 9000. This differs from most other TAL APDU commands. * Note that Random Transaction Selection is not yet implemented.
1 parent 720d59d commit 0c94724

File tree

5 files changed

+831
-1
lines changed

5 files changed

+831
-1
lines changed

src/emv.c

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1199,6 +1199,250 @@ int emv_processing_restrictions(struct emv_ctx_t* ctx)
11991199
return 0;
12001200
}
12011201

1202+
int emv_terminal_risk_management(struct emv_ctx_t* ctx)
1203+
{
1204+
int r;
1205+
const struct emv_tlv_t* term_floor_limit;
1206+
const struct emv_tlv_t* txn_amount;
1207+
uint32_t floor_limit_value;
1208+
uint32_t amount_value;
1209+
const struct emv_tlv_t* lower_offline_limit;
1210+
const struct emv_tlv_t* upper_offline_limit;
1211+
struct emv_tlv_list_t get_data_list = EMV_TLV_LIST_INIT;
1212+
1213+
if (!ctx) {
1214+
emv_debug_trace_msg("ctx=%p", ctx);
1215+
emv_debug_error("Invalid parameter");
1216+
return EMV_ERROR_INVALID_PARAMETER;
1217+
}
1218+
if (!ctx->tvr || !ctx->tsi) {
1219+
emv_debug_trace_msg("tvr=%p, tsi=%p", ctx->tvr, ctx->tsi);
1220+
emv_debug_error("Invalid context variable");
1221+
return EMV_ERROR_INVALID_PARAMETER;
1222+
}
1223+
1224+
// Ensure mandatory configuration fields are present and have valid length
1225+
term_floor_limit = emv_tlv_list_find_const(&ctx->config, EMV_TAG_9F1B_TERMINAL_FLOOR_LIMIT);
1226+
if (!term_floor_limit || term_floor_limit->length != 4) {
1227+
emv_debug_trace_msg("term_floor_limit=%p, term_floor_limit->length=%u",
1228+
term_floor_limit, term_floor_limit ? term_floor_limit->length : 0);
1229+
emv_debug_error("Terminal Floor Limit (9F1B) not found or invalid");
1230+
return EMV_ERROR_INVALID_CONFIG;
1231+
}
1232+
1233+
// Ensure mandatory transaction parameters are present and have valid length
1234+
txn_amount = emv_tlv_list_find_const(&ctx->params, EMV_TAG_81_AMOUNT_AUTHORISED_BINARY);
1235+
if (!txn_amount || txn_amount->length != 4) {
1236+
emv_debug_trace_msg("txn_amount=%p, txn_amount->length=%u",
1237+
txn_amount, txn_amount ? txn_amount->length : 0);
1238+
emv_debug_error("Amount, Authorised - Binary (81) not found or invalid");
1239+
return EMV_ERROR_INVALID_PARAMETER;
1240+
}
1241+
1242+
// Mandatory fields are present for terminal risk management to proceed
1243+
ctx->tsi->value[0] |= EMV_TSI_TERMINAL_RISK_MANAGEMENT_PERFORMED;
1244+
1245+
// Floor Limits
1246+
// See EMV 4.4 Book 3, 10.6.1
1247+
r = emv_format_b_to_uint(
1248+
term_floor_limit->value,
1249+
term_floor_limit->length,
1250+
&floor_limit_value
1251+
);
1252+
if (r) {
1253+
emv_debug_trace_msg("emv_format_b_to_uint() failed; r=%d", r);
1254+
1255+
// Internal error; terminate session
1256+
emv_debug_error("Internal error");
1257+
return EMV_ERROR_INTERNAL;
1258+
}
1259+
emv_debug_trace_msg("Terminal Floor Limit value is %u", (unsigned int)floor_limit_value);
1260+
r = emv_format_b_to_uint(
1261+
txn_amount->value,
1262+
txn_amount->length,
1263+
&amount_value
1264+
);
1265+
if (r) {
1266+
emv_debug_trace_msg("emv_format_b_to_uint() failed; r=%d", r);
1267+
1268+
// Internal error; terminate session
1269+
emv_debug_error("Internal error");
1270+
return EMV_ERROR_INTERNAL;
1271+
}
1272+
emv_debug_trace_msg("Amount, Authorised (Binary) value is %u", (unsigned int)amount_value);
1273+
if (amount_value >= floor_limit_value) {
1274+
emv_debug_info("Floor limit exceeded");
1275+
ctx->tvr->value[3] |= EMV_TVR_TXN_FLOOR_LIMIT_EXCEEDED;
1276+
} else {
1277+
emv_debug_info("Floor limit not exceeded");
1278+
ctx->tvr->value[3] &= ~EMV_TVR_TXN_FLOOR_LIMIT_EXCEEDED;
1279+
}
1280+
1281+
// Velocity Checking
1282+
// See EMV 4.4 Book 3, 10.6.3
1283+
lower_offline_limit = emv_tlv_list_find_const(&ctx->icc, EMV_TAG_9F14_LOWER_CONSECUTIVE_OFFLINE_LIMIT);
1284+
upper_offline_limit = emv_tlv_list_find_const(&ctx->icc, EMV_TAG_9F23_UPPER_CONSECUTIVE_OFFLINE_LIMIT);
1285+
if (lower_offline_limit && lower_offline_limit->length == 1 &&
1286+
upper_offline_limit && upper_offline_limit->length == 1
1287+
) {
1288+
const struct emv_tlv_t* atc;
1289+
uint32_t atc_value;
1290+
const struct emv_tlv_t* last_online_atc;
1291+
uint32_t last_online_atc_value;
1292+
1293+
// Retrieve Application Transaction Counter (9F36)
1294+
r = emv_tal_get_data(
1295+
ctx->ttl,
1296+
EMV_TAG_9F36_APPLICATION_TRANSACTION_COUNTER,
1297+
&get_data_list
1298+
);
1299+
if (r) {
1300+
emv_debug_trace_msg("emv_tal_get_data() failed; r=%d", r);
1301+
emv_debug_error("Failed to retrieve Application Transaction Counter (9F36)");
1302+
1303+
if (r < 0) {
1304+
if (r == EMV_TAL_ERROR_INTERNAL || r == EMV_TAL_ERROR_INVALID_PARAMETER) {
1305+
r = EMV_ERROR_INTERNAL;
1306+
} else {
1307+
// All other GET DATA errors are card errors
1308+
r = EMV_OUTCOME_CARD_ERROR;
1309+
}
1310+
goto exit;
1311+
}
1312+
1313+
// Otherwise continue processing
1314+
}
1315+
atc = emv_tlv_list_find_const(&get_data_list, EMV_TAG_9F36_APPLICATION_TRANSACTION_COUNTER);
1316+
if (atc) {
1317+
r = emv_format_b_to_uint(
1318+
atc->value,
1319+
atc->length,
1320+
&atc_value
1321+
);
1322+
if (r) {
1323+
emv_debug_trace_msg("emv_format_b_to_uint() failed; r=%d", r);
1324+
1325+
// Internal error; terminate session
1326+
emv_debug_error("Internal error");
1327+
r = EMV_ERROR_INTERNAL;
1328+
goto exit;
1329+
}
1330+
1331+
emv_debug_trace_msg("ATC value is %u", atc_value);
1332+
}
1333+
1334+
// Retrieve Last Online ATC Register (9F13)
1335+
r = emv_tal_get_data(
1336+
ctx->ttl,
1337+
EMV_TAG_9F13_LAST_ONLINE_ATC_REGISTER,
1338+
&get_data_list
1339+
);
1340+
if (r) {
1341+
emv_debug_trace_msg("emv_tal_get_data() failed; r=%d", r);
1342+
emv_debug_error("Failed to retrieve Last Online ATC Register (9F13)");
1343+
1344+
if (r < 0) {
1345+
if (r == EMV_TAL_ERROR_INTERNAL || r == EMV_TAL_ERROR_INVALID_PARAMETER) {
1346+
r = EMV_ERROR_INTERNAL;
1347+
} else {
1348+
// All other GET DATA errors are card errors
1349+
r = EMV_OUTCOME_CARD_ERROR;
1350+
}
1351+
goto exit;
1352+
}
1353+
1354+
// Otherwise continue processing
1355+
}
1356+
last_online_atc = emv_tlv_list_find_const(&get_data_list, EMV_TAG_9F13_LAST_ONLINE_ATC_REGISTER);
1357+
if (last_online_atc) {
1358+
r = emv_format_b_to_uint(
1359+
last_online_atc->value,
1360+
last_online_atc->length,
1361+
&last_online_atc_value
1362+
);
1363+
if (r) {
1364+
emv_debug_trace_msg("emv_format_b_to_uint() failed; r=%d", r);
1365+
1366+
// Internal error; terminate session
1367+
emv_debug_error("Internal error");
1368+
r = EMV_ERROR_INTERNAL;
1369+
goto exit;
1370+
}
1371+
1372+
emv_debug_trace_msg("Last Online ATC value is %u", last_online_atc_value);
1373+
}
1374+
1375+
// If both ATC and Last Online ATC are available, and offline
1376+
// transaction attempts have happened since the previous online
1377+
// authorisation, apply issuer velocity limits
1378+
if (atc && last_online_atc &&
1379+
atc_value > last_online_atc_value
1380+
) {
1381+
// Check velocity limits
1382+
// See EMV 4.4 Book 3, 10.6.3
1383+
if (atc_value - last_online_atc_value > lower_offline_limit->value[0]) {
1384+
emv_debug_info("Lower Consecutive Offline Limits exceeded");
1385+
ctx->tvr->value[3] |= EMV_TVR_LOWER_CONSECUTIVE_OFFLINE_LIMIT_EXCEEDED;
1386+
} else {
1387+
emv_debug_info("Lower Consecutive Offline Limits not exceeded");
1388+
ctx->tvr->value[3] &= ~EMV_TVR_LOWER_CONSECUTIVE_OFFLINE_LIMIT_EXCEEDED;
1389+
}
1390+
if (atc_value - last_online_atc_value > upper_offline_limit->value[0]) {
1391+
emv_debug_info("Upper Consecutive Offline Limits exceeded");
1392+
ctx->tvr->value[3] |= EMV_TVR_UPPER_CONSECUTIVE_OFFLINE_LIMIT_EXCEEDED;
1393+
} else {
1394+
emv_debug_info("Upper Consecutive Offline Limits not exceeded");
1395+
ctx->tvr->value[3] &= ~EMV_TVR_UPPER_CONSECUTIVE_OFFLINE_LIMIT_EXCEEDED;
1396+
}
1397+
1398+
} else {
1399+
// Unable to apply velocity checking
1400+
// See EMV 4.4 Book 3, 10.6.3
1401+
emv_debug_info("Velocity checking not possible. Assume both Consecutive Offline Limits exceeded.");
1402+
ctx->tvr->value[3] |= EMV_TVR_LOWER_CONSECUTIVE_OFFLINE_LIMIT_EXCEEDED;
1403+
ctx->tvr->value[3] |= EMV_TVR_UPPER_CONSECUTIVE_OFFLINE_LIMIT_EXCEEDED;
1404+
}
1405+
1406+
// Check for new card
1407+
// See EMV 4.4 Book 3, 10.6.3
1408+
if (last_online_atc && last_online_atc_value == 0) {
1409+
emv_debug_info("New card");
1410+
ctx->tvr->value[1] |= EMV_TVR_NEW_CARD;
1411+
} else {
1412+
ctx->tvr->value[1] &= ~EMV_TVR_NEW_CARD;
1413+
}
1414+
1415+
} else {
1416+
// If not present, skip velocity checking
1417+
// See EMV 4.4 Book 3, 10.6.3
1418+
// See EMV 4.4 Book 3, 7.3
1419+
emv_debug_trace_msg("One or both Consecutive Offline Limits (9F14 or 9F23) not found");
1420+
emv_debug_info("ICC does not support velocity checking");
1421+
ctx->tvr->value[1] &= ~EMV_TVR_NEW_CARD;
1422+
ctx->tvr->value[3] &= ~EMV_TVR_LOWER_CONSECUTIVE_OFFLINE_LIMIT_EXCEEDED;
1423+
ctx->tvr->value[3] &= ~EMV_TVR_UPPER_CONSECUTIVE_OFFLINE_LIMIT_EXCEEDED;
1424+
}
1425+
1426+
// Append GET DATA output to ICC data list
1427+
r = emv_tlv_list_append(&ctx->icc, &get_data_list);
1428+
if (r) {
1429+
emv_debug_trace_msg("emv_tlv_list_append() failed; r=%d", r);
1430+
1431+
// Internal error; terminate session
1432+
emv_debug_error("Internal error");
1433+
r = EMV_ERROR_INTERNAL;
1434+
goto exit;
1435+
}
1436+
1437+
// Success
1438+
r = 0;
1439+
goto exit;
1440+
1441+
exit:
1442+
emv_tlv_list_clear(&get_data_list);
1443+
return r;
1444+
}
1445+
12021446
int emv_card_action_analysis(struct emv_ctx_t* ctx)
12031447
{
12041448
int r;

src/emv.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,32 @@ int emv_offline_data_authentication(struct emv_ctx_t* ctx);
401401
*/
402402
int emv_processing_restrictions(struct emv_ctx_t* ctx);
403403

404+
/**
405+
* Perform EMV Terminal Risk Management to identify risks to be considered for
406+
* online authorisation. This function will perform terminal risk management
407+
* regardless of whether the ICC indicates that it is mandatory in
408+
* @ref EMV_TAG_82_APPLICATION_INTERCHANGE_PROFILE.
409+
*
410+
* Terminal risk management consists of:
411+
* - Floor limit checking
412+
* - Random transaction selection
413+
* - Velocity checking
414+
*
415+
* While performing terminal risk management, this function will update
416+
* @ref EMV_TAG_95_TERMINAL_VERIFICATION_RESULTS to reflect the outcomes and
417+
* update @ref EMV_TAG_9B_TRANSACTION_STATUS_INFORMATION to indicate that it
418+
* has been performed.
419+
*
420+
* @remark See EMV 4.4 Book 3, 10.6
421+
*
422+
* @param ctx EMV processing context
423+
*
424+
* @return Zero for success
425+
* @return Less than zero for errors. See @ref emv_error_t
426+
* @return Greater than zero for EMV processing outcome. See @ref emv_outcome_t
427+
*/
428+
int emv_terminal_risk_management(struct emv_ctx_t* ctx);
429+
404430
/**
405431
* Perform EMV Card Action Analysis to determined the risk management decision
406432
* by the ICC as indicated in the response from GENERATE APPLICATION CRYPTOGRAM.

tests/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ if (BUILD_TESTING)
8686
target_link_libraries(emv_processing_restrictions_test PRIVATE print_helpers emv)
8787
add_test(emv_processing_restrictions_test emv_processing_restrictions_test)
8888

89+
add_executable(emv_terminal_risk_management_test emv_terminal_risk_management_test.c)
90+
target_link_libraries(emv_terminal_risk_management_test PRIVATE emv_cardreader_emul print_helpers emv)
91+
add_test(emv_terminal_risk_management_test emv_terminal_risk_management_test)
92+
8993
add_executable(isocodes_test isocodes_test.c)
9094
find_package(Intl)
9195
if(Intl_FOUND)

0 commit comments

Comments
 (0)