Skip to content

Commit

Permalink
Update Terminal Transport Layer (TTL) to latest EMV specifications
Browse files Browse the repository at this point in the history
* Update comments to EMV Contact Interface Specification v1.0 as well as
  EMV 4.4
* Apply EMV bulletin 114 to TPDU/APDU warning processing
  • Loading branch information
leonlynch committed Mar 17, 2024
1 parent ad683b9 commit c16ceb3
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 95 deletions.
2 changes: 1 addition & 1 deletion src/emv.c
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ int emv_atr_parse(const void* atr, size_t atr_len)
// For T=1, reject 2^CWI < (N + 1)
// - if N==0xFF, consider N to be -1
// - if N==0x00, consider CWI to be 1
// See EMV Level 1 Contact Interface v1.0, 8.3.3.1
// See EMV Level 1 Contact Interface v1.0, 8.3.3.10
int N = (atr_info.global.N != 0xFF) ? atr_info.global.N : -1;
unsigned int CWI = atr_info.global.N ? atr_info.protocol_T1.CWI : 1;
unsigned int pow_2_CWI = 1 << CWI;
Expand Down
84 changes: 39 additions & 45 deletions src/emv_ttl.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ int emv_ttl_trx(

// Determine the APDU case
// See ISO 7816-3:2006, 12.1.3, table 13
// See EMV 4.3 Book 1, 9.3.1.1
// See EMV 4.3 Book 1, Annex A
// See EMV Contact Interface Specification v1.0, 9.3.1.1
// See EMV Contact Interface Specification v1.0, Annex A

// APDU cases:
// Case 1: CLA INS P1 P2
Expand Down Expand Up @@ -169,7 +169,7 @@ int emv_ttl_trx(

// For APDU case 1, the R-APDU should only be SW1-SW2; no further action
// See ISO 7816-3:2006, 12.2.1, table 14
// See EMV 4.3 Book 1, 9.3.1.1.1
// See EMV Contact Interface Specification v1.0, 9.3.1.1.1
if (apdu_case == ISO7816_APDU_CASE_1) {
if (rx_len != 2) {
// Unexpected response length for APDU case 1
Expand All @@ -191,7 +191,7 @@ int emv_ttl_trx(

// Process response containing single procedure byte
// See ISO 7816-3:2006, 10.3.3
// See EMV 4.3 Book 1, 9.2.2.3.1, table 25
// See EMV Contact Interface Specification v1.0, 9.2.2.3.1, table 25
if (rx_len == 1) {
uint8_t procedure_byte = *((uint8_t*)rx_buf);

Expand All @@ -205,7 +205,6 @@ int emv_ttl_trx(
}

// ACK: Send remaining data bytes
// See EMV 4.3 Book 1, Annex A
if (procedure_byte == INS) {
if (!c_tpdu_data) {
// Unexpected procedure byte if no data available
Expand Down Expand Up @@ -268,10 +267,11 @@ int emv_ttl_trx(
// Process status bytes
// See ISO 7816-3:2006, 12.2.1, table 14
// See ISO 7816-4:2005, 5.1.3, table 6
// See EMV 4.3 Book 1, 9.2.2.3.1, table 25
// See EMV 4.3 Book 1, 9.2.2.3.2
// See EMV 4.3 Book 1, 9.3.1.2
// See EMV 4.3 Book 1, Annex A for examples
// See EMV Contact Interface Specification v1.0, 9.2.2.3.1, table 25
// See EMV Contact Interface Specification v1.0, 9.2.2.3.2
// See EMV Contact Interface Specification v1.0, 9.3.1.1
// See EMV Contact Interface Specification v1.0, 9.3.1.2
// See EMV Contact Interface Specification v1.0, Annex A for examples
switch (SW1) {
case 0x61: // Normal processing: SW2 encodes the number of available bytes

Expand Down Expand Up @@ -303,16 +303,19 @@ int emv_ttl_trx(

default:
// Terminal Application Layer (TAL) should receive the SW1-SW2
// value of the initial command and not the subsequent
// GET RESPONSE command.
// See EMV 4.3 Book 1, Annex A7 "Case 4 Command with Warning Condition"
// value for all remaining cases, including normal completion
// and completion with a warning. Note that warning processing
// may occur before the response data is received.
// See EMV Contact Interface Specification v1.0, 9.3.1.1
if (!*sw1sw2) {
// Output status bytes SW1-SW2 in host endianness
*sw1sw2 = ((uint16_t)SW1 << 8) | SW2;

// For APDU case 4, warning processing is followed by GET RESPONSE
// See EMV 4.3 Book 1, Annex A7 "Case 4 Command with Warning Condition"
// For APDU case 4, if warning processing occurs without
// response data, it is followed by GET RESPONSE
// See EMV Contact Interface Specification v1.0, Annex A7 "Case 4 Command with Warning Condition"
if (apdu_case == ISO7816_APDU_CASE_4S &&
rx_len == 2 &&
iso7816_sw1sw2_is_warning(SW1, SW2)
) {
tx_get_response = true;
Expand Down Expand Up @@ -397,7 +400,7 @@ int emv_ttl_trx(

// Build GET RESPONSE for next transmission
// See ISO 7816-4:2005, 7.6.1
// See EMV 4.3 Book 1, 9.3.1.3
// See EMV Contact Interface Specification v1.0, 9.3.1.3
c_tpdu_header[0] = 0x00; // CLA
c_tpdu_header[1] = 0xC0; // INS: GET RESPONSE
c_tpdu_header[2] = 0x00; // P1
Expand All @@ -412,7 +415,7 @@ int emv_ttl_trx(

if (tx_update_le) {
// Update Le for next transmission
// See EMV 4.3 Book 1, 9.2.2.3.1, table 25
// See EMV Contact Interface Specification v1.0, 9.2.2.3.1, table 25
memcpy(c_tpdu_header, tx_buf, 4);
c_tpdu_header[4] = SW2; // P3 = Le
tx_buf = c_tpdu_header;
Expand All @@ -422,24 +425,15 @@ int emv_ttl_trx(
continue;
}

// If SW1-SW2 indicates success or error, finalise the R-APDU
// Only warning processing may proceed
if (iso7816_sw1sw2_is_success(SW1, SW2) ||
iso7816_sw1sw2_is_error(SW1, SW2)
) {
// Finalise R-APDU using initial SW1-SW2
r_apdu_ptr[0] = *sw1sw2 >> 8;
r_apdu_ptr[1] = *sw1sw2 & 0xFF;
r_apdu_ptr += 2;
*r_apdu_len = (void*)r_apdu_ptr - r_apdu;

// Let Terminal Application Layer (TAL) process the response
emv_debug_rapdu(r_apdu, *r_apdu_len);
return 0;
}
// Finalise R-APDU using current SW1-SW2
r_apdu_ptr[0] = *sw1sw2 >> 8;
r_apdu_ptr[1] = *sw1sw2 & 0xFF;
r_apdu_ptr += 2;
*r_apdu_len = (void*)r_apdu_ptr - r_apdu;

// This should never happen
return -100;
// Let Terminal Application Layer (TAL) process the response
emv_debug_rapdu(r_apdu, *r_apdu_len);
return 0;

} while (true);
}
Expand All @@ -466,19 +460,19 @@ int emv_ttl_select_by_df_name(
}

// For SELECT, ensure that Lc is from 0x05 to 0x10
// See EMV 4.3 Book 1, 11.3.2, table 40
// See EMV 4.4 Book 1, 11.3.2, table 5
if (df_name_len < 0x05 || df_name_len > 0x10) {
return -3;
}

// Build SELECT command
c_apdu.CLA = 0x00; // See EMV 4.3 Book 3, 6.3.2
c_apdu.INS = 0xA4; // See EMV 4.3 Book 1, 11.3.2, table 40
c_apdu.P1 = 0x04; // See EMV 4.3 Book 1, 11.3.2, table 41
c_apdu.P2 = 0x00; // See EMV 4.3 Book 1, 11.3.2, table 42
c_apdu.CLA = 0x00; // See EMV 4.4 Book 3, 6.3.2
c_apdu.INS = 0xA4; // See EMV 4.4 Book 1, 11.3.2, table 5
c_apdu.P1 = 0x04; // See EMV 4.4 Book 1, 11.3.2, table 6
c_apdu.P2 = 0x00; // See EMV 4.4 Book 1, 11.3.2, table 7
c_apdu.Lc = df_name_len;
memcpy(c_apdu.data, df_name, c_apdu.Lc);
c_apdu.data[c_apdu.Lc] = 0x00; // See EMV 4.3 Book 1, 11.3.2, table 40
c_apdu.data[c_apdu.Lc] = 0x00; // See EMV 4.4 Book 1, 11.3.2, table 5

r = emv_ttl_trx(
ctx,
Expand Down Expand Up @@ -527,19 +521,19 @@ int emv_ttl_select_by_df_name_next(
}

// For SELECT, ensure that Lc is from 0x05 to 0x10
// See EMV 4.3 Book 1, 11.3.2, table 40
// See EMV 4.4 Book 1, 11.3.2, table 5
if (df_name_len < 0x05 || df_name_len > 0x10) {
return -3;
}

// Build SELECT command
c_apdu.CLA = 0x00; // See EMV 4.3 Book 3, 6.3.2
c_apdu.INS = 0xA4; // See EMV 4.3 Book 1, 11.3.2, table 40
c_apdu.P1 = 0x04; // See EMV 4.3 Book 1, 11.3.2, table 41
c_apdu.P2 = 0x02; // See EMV 4.3 Book 1, 11.3.2, table 42
c_apdu.CLA = 0x00; // See EMV 4.4 Book 3, 6.3.2
c_apdu.INS = 0xA4; // See EMV 4.4 Book 1, 11.3.2, table 5
c_apdu.P1 = 0x04; // See EMV 4.4 Book 1, 11.3.2, table 6
c_apdu.P2 = 0x02; // See EMV 4.4 Book 1, 11.3.2, table 7
c_apdu.Lc = df_name_len;
memcpy(c_apdu.data, df_name, c_apdu.Lc);
c_apdu.data[c_apdu.Lc] = 0x00; // See EMV 4.3 Book 1, 11.3.2, table 40
c_apdu.data[c_apdu.Lc] = 0x00; // See EMV 4.4 Book 1, 11.3.2, table 5

r = emv_ttl_trx(
ctx,
Expand Down
4 changes: 2 additions & 2 deletions src/emv_ttl.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ int emv_ttl_trx(
/**
* SELECT (0xA4) the first or only application by Dedicated File (DF) name
* and provide File Control Information (FCI) template.
* @remark See EMV 4.3 Book 1, 11.3
* @remark See EMV 4.4 Book 1, 11.3
* @remark See ISO 7816-4:2005, 7.1.1
*
* @param ctx EMV Terminal Transport Layer context
Expand All @@ -119,7 +119,7 @@ int emv_ttl_select_by_df_name(
/**
* SELECT (0xA4) the next application by Dedicated File (DF) name and
* provide File Control Information (FCI) template.
* @remark See EMV 4.3 Book 1, 11.3
* @remark See EMV 4.4 Book 1, 11.3
* @remark See ISO 7816-4:2005, 7.1.1
*
* @param ctx EMV Terminal Transport Layer context
Expand Down
4 changes: 2 additions & 2 deletions src/iso7816.c
Original file line number Diff line number Diff line change
Expand Up @@ -678,7 +678,7 @@ static void iso7816_compute_wt(struct iso7816_atr_info_t* atr_info)
// WT = WI x 960 x D ETU
// Which is the same conclusion that EMV comes to below...

// From EMV Book 1, version 4.3 (Nov 2011), section 9.2.2.1:
// From EMV Contact Interface Specification v1.0, 9.2.2.1:
// WWT = 960 x D x WI ETUs (D and WI are returned in TA1 and TC2, respectively)

// And finally, after all that thinking...
Expand All @@ -701,7 +701,7 @@ static void iso7816_compute_wt(struct iso7816_atr_info_t* atr_info)
// BWT = 11etu + (2^BWI x 960 x 372 x D / F)
// Which is the same conclusion that EMV comes to below...

// From EMV Book 1, version 4.3 (Nov 2011), section 9.2.4.2.2:
// From EMV Contact Interface Specification v1.0, 9.2.4.2.2:
// BWT = (((2^BWI x 960 x 372 x D / F) + 11)etu; where D is Di and F is Fi

// And finally, after all that thinking...
Expand Down
2 changes: 1 addition & 1 deletion src/pcsc.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ int pcsc_reader_get_atr(pcsc_reader_ctx_t reader_ctx, uint8_t* atr, size_t* atr_
* @param tx_buf Transmit buffer
* @param tx_buf_len Length of transmit buffer in bytes
* @param rx_buf Receive buffer
* @param rx_buf_len Length of R-APDU buffer in bytes
* @param rx_buf_len Length of receive buffer in bytes
* @return Zero for success. Less than zero for error.
*/
int pcsc_reader_trx(
Expand Down
Loading

0 comments on commit c16ceb3

Please sign in to comment.