Skip to content

Commit

Permalink
Implement ISO 7816 APDU case helper function
Browse files Browse the repository at this point in the history
This helper function can also identify extended APDU cases although EMV
does not support them. The intention is that this helper function can be
used by EMV TTL as well as future C-APDU decoder implementations.
  • Loading branch information
leonlynch committed Mar 31, 2024
1 parent 112f313 commit 4b3d31b
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 23 deletions.
31 changes: 8 additions & 23 deletions src/emv_ttl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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) {
Expand Down
58 changes: 58 additions & 0 deletions src/iso7816_apdu.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#define ISO7816_APDU_H

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

Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit 4b3d31b

Please sign in to comment.