diff --git a/src/emv_ttl.c b/src/emv_ttl.c index cdefebb..1aca9ff 100644 --- a/src/emv_ttl.c +++ b/src/emv_ttl.c @@ -58,10 +58,6 @@ int emv_ttl_trx( emv_debug_capdu(c_apdu, c_apdu_len); - if (c_apdu_len < 4) { - return -1; - } - // Determine the APDU case // See ISO 7816-3:2006, 12.1.3, table 13 // See EMV Contact Interface Specification v1.0, 9.3.1.1 @@ -73,27 +69,16 @@ int emv_ttl_trx( // Case 3: CLA INS P1 P2 Lc [Data(Lc)] // Case 4: CLA INS P1 P2 Lc [Data(Lc)] Le - if (c_apdu_len == 4) { - apdu_case = ISO7816_APDU_CASE_1; - emv_debug_apdu("APDU case 1"); - } else if (c_apdu_len == 5) { - apdu_case = ISO7816_APDU_CASE_2S; - emv_debug_apdu("APDU case 2S"); - } else { - // Extract byte C5 from header; See ISO 7816-3:2006, 12.1.3 - unsigned int C5 = *(uint8_t*)(c_apdu + 4); - - if (C5 != 0 && C5 + 5 == c_apdu_len) { // If C5 is Lc and Le is absent - apdu_case = ISO7816_APDU_CASE_3S; - emv_debug_apdu("APDU case 3S"); - } else if (C5 != 0 && C5 + 6 == c_apdu_len) { // If C5 is Lc and Le is present - apdu_case = ISO7816_APDU_CASE_4S; - emv_debug_apdu("APDU case 4S"); - } else { - // Unknown APDU case + apdu_case = iso7816_apdu_case(c_apdu, c_apdu_len); + switch (apdu_case) { + case ISO7816_APDU_CASE_1: emv_debug_apdu("APDU case 1"); break; + case ISO7816_APDU_CASE_2S: emv_debug_apdu("APDU case 2S"); break; + case ISO7816_APDU_CASE_3S: emv_debug_apdu("APDU case 3S"); break; + case ISO7816_APDU_CASE_4S: emv_debug_apdu("APDU case 4S"); break; + + default: emv_debug_error("Unknown APDU case"); return -2; - } } if (ctx->cardreader.mode == EMV_CARDREADER_MODE_APDU) { diff --git a/src/iso7816_apdu.h b/src/iso7816_apdu.h index c7e907c..ef2167b 100644 --- a/src/iso7816_apdu.h +++ b/src/iso7816_apdu.h @@ -24,6 +24,7 @@ #define ISO7816_APDU_H #include +#include #include #include @@ -57,6 +58,63 @@ enum iso7816_apdu_case_t { ISO7816_APDU_CASE_4E, ///< ISO 7816 C-APDU case 4 for long Lc/Le fields: CLA, INS, P1, P2, Lc(3), Data(Lc), Le(3) }; +/** + * Determine ISO 7816 Command Application Protocol Data Unit (C-APDU) case + * @remark See ISO 7816-3:2006, 12.1.3 + * @param c_apdu Command Application Protocol Data Unit (C-APDU) buffer + * @param c_apdu_len Length of Application Protocol Data Unit (C-APDU) buffer in bytes + * @return Less than zero for error. Otherwise @ref iso7816_apdu_case_t + */ +static inline int iso7816_apdu_case(const void* c_apdu, size_t c_apdu_len) +{ + if (!c_apdu || c_apdu_len < 4) { + return -1; + } + + if (c_apdu_len == 4) { + return ISO7816_APDU_CASE_1; + } else if (c_apdu_len == 5) { + return ISO7816_APDU_CASE_2S; + } else { + // Extract byte C5 from header + // See ISO 7816-3:2006, 12.1.3, table 13 + unsigned int C5 = *(uint8_t*)(c_apdu + 4); + + if (C5 != 0) { // If C5 is Lc + if (5 + C5 == c_apdu_len) { // Le is absent + return ISO7816_APDU_CASE_3S; + } + if (6 + C5 == c_apdu_len) { // Le is present + return ISO7816_APDU_CASE_4S; + } + } else { // If C5 is zero + if (c_apdu_len == 7) { + return ISO7816_APDU_CASE_2E; + } + + if (c_apdu_len > 7) { + // Extract bytes C6C7 from header + // See ISO 7816-3:2006, 12.1.3, table 13 + unsigned int C6 = *(uint8_t*)(c_apdu + 6); + unsigned int C7 = *(uint8_t*)(c_apdu + 7); + unsigned int C6C7 = (C6 << 8) + C7; + + if (C6C7) { + if (7 + C6C7 == c_apdu_len) { // Le is absent + return ISO7816_APDU_CASE_3E; + } + if (9 + C6C7 == c_apdu_len) { // Le is present + return ISO7816_APDU_CASE_4E; + } + } + } + } + } + + // Unknown C-APDU case + return -2; +} + /// ISO 7816 C-APDU case 1 structure struct iso7816_apdu_case_1_t { uint8_t CLA; ///< Class byte: indicates the type of command (interindustry vs proprietary, command chaining, secure messaging, logical channel)