Skip to content

Commit

Permalink
Stringify ISO 7816 PUT DATA command
Browse files Browse the repository at this point in the history
This implementation only stringifies the interpretation of P1-P2 for
PUT DATA, but does not consider the command data at all, to provide a
one-line description to iso7816_capdu_get_string().
  • Loading branch information
leonlynch committed Apr 5, 2024
1 parent 106a5e4 commit bba3e30
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 4 deletions.
94 changes: 93 additions & 1 deletion src/iso7816_strings.c
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,98 @@ static const char* iso7816_capdu_read_record_get_string(
}
}

static const char* iso7816_capdu_put_data_get_string(
const uint8_t* c_apdu,
size_t c_apdu_len,
char* str,
size_t str_len
)
{
enum iso7816_apdu_case_t apdu_case;
struct iso7816_apdu_case_3s_t* apdu_case_3s;
uint8_t P1;
uint8_t P2;

if ((c_apdu[0] & ISO7816_CLA_PROPRIETARY) != 0 ||
(c_apdu[1] != 0xDA && c_apdu[1] != 0xDB)
) {
// Not PUT DATA
return NULL;
}

// PUT DATA must be APDU case 3S/3E but only support 3S for now
// See ISO 7816-4:2005, 7.4.3, table 64
apdu_case = iso7816_apdu_case(c_apdu, c_apdu_len);
switch (apdu_case) {
case ISO7816_APDU_CASE_3S:
apdu_case_3s = (void*)c_apdu;
break;

case ISO7816_APDU_CASE_3E:
// Unsupported APDU case for PUT DATA
snprintf(str, str_len, "PUT DATA");
return str;

default:
// Invalid APDU case for PUT DATA
return NULL;
}

// Extract P1 and P2 for convenience
P1 = apdu_case_3s->P1;
P2 = apdu_case_3s->P2;

// Decode P1 and P2
// See ISO 7816-4:2005, 7.4.1, table 62
if ((c_apdu[1] & 0x01) == 0) {
// Even INS code
if (P1 == 0 && P2 >= 0x40 && P2 <= 0xFE) {
// P2 is 1-byte BER-TLV tag
snprintf(str, str_len, "PUT DATA for BER-TLV field %02X", P2);
return str;
} else if (P1 == 1) {
// P2 is proprietary
snprintf(str, str_len, "PUT DATA for proprietary identifier %02X", P2);
return str;
} else if (P1 == 2 && P2 >= 0x01 && P2 <= 0xFE) {
// P2 is 1-byte SIMPLE-TLV tag
snprintf(str, str_len, "PUT DATA for SIMPLE-TLV field %02X", P2);
return str;
} else if (P1 >= 0x40) {
// P1-P2 is 2-byte BER-TLV tag
snprintf(str, str_len, "PUT DATA for BER-TLV field %02X%02X", P1, P2);
return str;
}
} else {
// Odd INS code
if (P1 == 0x00 && (P2 & 0xE0) == 0x00 && // Highest 11 bits of P1-P2 are unset
(P2 & 0x1F) != 0x00 && (P2 & 0x1F) != 0x1F // Lowest 5 bits are not equal
) {
// P2 is short EF identifier
snprintf(str, str_len, "PUT DATA for short EF %u", P2);
return str;
} else if (P1 == 0x3F && P2 == 0xFF) { // P1-P2 is 3FFF
// P1-P2 is current DF
snprintf(str, str_len, "PUT DATA for current DF");
return str;
} else if (P1 == 0x00 && P2 == 0x00 && // P1-P2 is 0000
apdu_case_3s->Lc == 0 // No command data
) {
// P1-P2 is current EF
snprintf(str, str_len, "PUT DATA for current EF");
return str;
} else if (P1 != 0x00 || P2 != 0x00) { // P1-P2 is not 0000
// P1-P2 is file identifier
snprintf(str, str_len, "PUT DATA for file %02X%02X", P1, P2);
return str;
}
}

// Unknown P1 and P2
snprintf(str, str_len, "PUT DATA");
return str;
}

const char* iso7816_capdu_get_string(
const void* c_apdu,
size_t c_apdu_len,
Expand Down Expand Up @@ -386,7 +478,7 @@ const char* iso7816_capdu_get_string(
case 0xD6:
case 0xD7: ins_str = "UPDATE BINARY"; break;
case 0xDA:
case 0xDB: ins_str = "PUT DATA"; break;
case 0xDB: return iso7816_capdu_put_data_get_string(c_apdu, c_apdu_len, str, str_len);
case 0xDC:
case 0xDD: ins_str = "UPDATE RECORD"; break;
case 0xE0: ins_str = "CREATE FILE"; break;
Expand Down
6 changes: 3 additions & 3 deletions tools/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -607,7 +607,7 @@ if(TARGET emv-decode AND BUILD_TESTING)
string(CONCAT emv_decode_issuer_response_test2_regex
"^89 \\| Authorisation Code : \\[6\\] 36 32 37 31 32 33 \"627123\"[\r\n]"
"72 \\| Issuer Script Template 2 : \\[17\\][\r\n]"
" 86 \\| Issuer Script Command : \\[15\\] 04 DA 9F 6B 0A 00 00 00 05 00 00 91 98 63 52 \\(PUT DATA\\)[\r\n]"
" 86 \\| Issuer Script Command : \\[15\\] 04 DA 9F 6B 0A 00 00 00 05 00 00 91 98 63 52 \\(PUT DATA for BER-TLV field 9F6B\\)[\r\n]"
"8A \\| Authorisation Response Code : \\[2\\] 30 30 \"00 - Approved or completed successfully\"[\r\n]"
"91 \\| Issuer Authentication Data : \\[8\\] A5 84 F9 49 00 82 00 00[\r\n]"
" - Authorisation Response Cryptogram \\(ARPC\\): A584F949[\r\n]"
Expand Down Expand Up @@ -644,7 +644,7 @@ if(TARGET emv-decode AND BUILD_TESTING)
"^72 \\| Issuer Script Template 2 : \\[69\\][\r\n]"
" 9F18 \\| Issuer Script Identifier : \\[4\\] 80 00 00 00[\r\n]"
" 86 \\| Issuer Script Command : \\[21\\] 84 24 00 02 10 FE BF 34 F0 0B 7C E7 70 DC 61 DA 84 7B FB 1E 59 \\(PIN CHANGE/UNBLOCK\\)[\r\n]"
" 86 \\| Issuer Script Command : \\[37\\] 04 DA 8E 00 20 00 00 00 00 00 00 00 00 42 01 41 03 5E 03 1F 02 00 00 00 00 00 00 00 00 AC 7F 4D F1 D6 24 A0 ED \\(PUT DATA\\)[\r\n]"
" 86 \\| Issuer Script Command : \\[37\\] 04 DA 8E 00 20 00 00 00 00 00 00 00 00 42 01 41 03 5E 03 1F 02 00 00 00 00 00 00 00 00 AC 7F 4D F1 D6 24 A0 ED \\(PUT DATA for BER-TLV field 8E00\\)[\r\n]"
)
set_tests_properties(emv_decode_issuer_response_test4
PROPERTIES
Expand All @@ -659,7 +659,7 @@ if(TARGET emv-decode AND BUILD_TESTING)
string(CONCAT emv_decode_issuer_response_test5_regex
"^72 \\| Issuer Script Template 2 : \\[23\\][\r\n]"
" 9F18 \\| Issuer Script Identifier : \\[4\\] 00 00 40 00[\r\n]"
" 86 \\| Issuer Script Command : \\[14\\] 04 DA 9F 58 09 00 C7 35 62 86 E3 77 98 89 \\(PUT DATA\\)[\r\n]"
" 86 \\| Issuer Script Command : \\[14\\] 04 DA 9F 58 09 00 C7 35 62 86 E3 77 98 89 \\(PUT DATA for BER-TLV field 9F58\\)[\r\n]"
)
set_tests_properties(emv_decode_issuer_response_test5
PROPERTIES
Expand Down

0 comments on commit bba3e30

Please sign in to comment.