Skip to content

Commit bba3e30

Browse files
committed
Stringify ISO 7816 PUT DATA command
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().
1 parent 106a5e4 commit bba3e30

File tree

2 files changed

+96
-4
lines changed

2 files changed

+96
-4
lines changed

src/iso7816_strings.c

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,98 @@ static const char* iso7816_capdu_read_record_get_string(
310310
}
311311
}
312312

313+
static const char* iso7816_capdu_put_data_get_string(
314+
const uint8_t* c_apdu,
315+
size_t c_apdu_len,
316+
char* str,
317+
size_t str_len
318+
)
319+
{
320+
enum iso7816_apdu_case_t apdu_case;
321+
struct iso7816_apdu_case_3s_t* apdu_case_3s;
322+
uint8_t P1;
323+
uint8_t P2;
324+
325+
if ((c_apdu[0] & ISO7816_CLA_PROPRIETARY) != 0 ||
326+
(c_apdu[1] != 0xDA && c_apdu[1] != 0xDB)
327+
) {
328+
// Not PUT DATA
329+
return NULL;
330+
}
331+
332+
// PUT DATA must be APDU case 3S/3E but only support 3S for now
333+
// See ISO 7816-4:2005, 7.4.3, table 64
334+
apdu_case = iso7816_apdu_case(c_apdu, c_apdu_len);
335+
switch (apdu_case) {
336+
case ISO7816_APDU_CASE_3S:
337+
apdu_case_3s = (void*)c_apdu;
338+
break;
339+
340+
case ISO7816_APDU_CASE_3E:
341+
// Unsupported APDU case for PUT DATA
342+
snprintf(str, str_len, "PUT DATA");
343+
return str;
344+
345+
default:
346+
// Invalid APDU case for PUT DATA
347+
return NULL;
348+
}
349+
350+
// Extract P1 and P2 for convenience
351+
P1 = apdu_case_3s->P1;
352+
P2 = apdu_case_3s->P2;
353+
354+
// Decode P1 and P2
355+
// See ISO 7816-4:2005, 7.4.1, table 62
356+
if ((c_apdu[1] & 0x01) == 0) {
357+
// Even INS code
358+
if (P1 == 0 && P2 >= 0x40 && P2 <= 0xFE) {
359+
// P2 is 1-byte BER-TLV tag
360+
snprintf(str, str_len, "PUT DATA for BER-TLV field %02X", P2);
361+
return str;
362+
} else if (P1 == 1) {
363+
// P2 is proprietary
364+
snprintf(str, str_len, "PUT DATA for proprietary identifier %02X", P2);
365+
return str;
366+
} else if (P1 == 2 && P2 >= 0x01 && P2 <= 0xFE) {
367+
// P2 is 1-byte SIMPLE-TLV tag
368+
snprintf(str, str_len, "PUT DATA for SIMPLE-TLV field %02X", P2);
369+
return str;
370+
} else if (P1 >= 0x40) {
371+
// P1-P2 is 2-byte BER-TLV tag
372+
snprintf(str, str_len, "PUT DATA for BER-TLV field %02X%02X", P1, P2);
373+
return str;
374+
}
375+
} else {
376+
// Odd INS code
377+
if (P1 == 0x00 && (P2 & 0xE0) == 0x00 && // Highest 11 bits of P1-P2 are unset
378+
(P2 & 0x1F) != 0x00 && (P2 & 0x1F) != 0x1F // Lowest 5 bits are not equal
379+
) {
380+
// P2 is short EF identifier
381+
snprintf(str, str_len, "PUT DATA for short EF %u", P2);
382+
return str;
383+
} else if (P1 == 0x3F && P2 == 0xFF) { // P1-P2 is 3FFF
384+
// P1-P2 is current DF
385+
snprintf(str, str_len, "PUT DATA for current DF");
386+
return str;
387+
} else if (P1 == 0x00 && P2 == 0x00 && // P1-P2 is 0000
388+
apdu_case_3s->Lc == 0 // No command data
389+
) {
390+
// P1-P2 is current EF
391+
snprintf(str, str_len, "PUT DATA for current EF");
392+
return str;
393+
} else if (P1 != 0x00 || P2 != 0x00) { // P1-P2 is not 0000
394+
// P1-P2 is file identifier
395+
snprintf(str, str_len, "PUT DATA for file %02X%02X", P1, P2);
396+
return str;
397+
}
398+
}
399+
400+
// Unknown P1 and P2
401+
snprintf(str, str_len, "PUT DATA");
402+
return str;
403+
}
404+
313405
const char* iso7816_capdu_get_string(
314406
const void* c_apdu,
315407
size_t c_apdu_len,
@@ -386,7 +478,7 @@ const char* iso7816_capdu_get_string(
386478
case 0xD6:
387479
case 0xD7: ins_str = "UPDATE BINARY"; break;
388480
case 0xDA:
389-
case 0xDB: ins_str = "PUT DATA"; break;
481+
case 0xDB: return iso7816_capdu_put_data_get_string(c_apdu, c_apdu_len, str, str_len);
390482
case 0xDC:
391483
case 0xDD: ins_str = "UPDATE RECORD"; break;
392484
case 0xE0: ins_str = "CREATE FILE"; break;

tools/CMakeLists.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -607,7 +607,7 @@ if(TARGET emv-decode AND BUILD_TESTING)
607607
string(CONCAT emv_decode_issuer_response_test2_regex
608608
"^89 \\| Authorisation Code : \\[6\\] 36 32 37 31 32 33 \"627123\"[\r\n]"
609609
"72 \\| Issuer Script Template 2 : \\[17\\][\r\n]"
610-
" 86 \\| Issuer Script Command : \\[15\\] 04 DA 9F 6B 0A 00 00 00 05 00 00 91 98 63 52 \\(PUT DATA\\)[\r\n]"
610+
" 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]"
611611
"8A \\| Authorisation Response Code : \\[2\\] 30 30 \"00 - Approved or completed successfully\"[\r\n]"
612612
"91 \\| Issuer Authentication Data : \\[8\\] A5 84 F9 49 00 82 00 00[\r\n]"
613613
" - Authorisation Response Cryptogram \\(ARPC\\): A584F949[\r\n]"
@@ -644,7 +644,7 @@ if(TARGET emv-decode AND BUILD_TESTING)
644644
"^72 \\| Issuer Script Template 2 : \\[69\\][\r\n]"
645645
" 9F18 \\| Issuer Script Identifier : \\[4\\] 80 00 00 00[\r\n]"
646646
" 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]"
647-
" 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]"
647+
" 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]"
648648
)
649649
set_tests_properties(emv_decode_issuer_response_test4
650650
PROPERTIES
@@ -659,7 +659,7 @@ if(TARGET emv-decode AND BUILD_TESTING)
659659
string(CONCAT emv_decode_issuer_response_test5_regex
660660
"^72 \\| Issuer Script Template 2 : \\[23\\][\r\n]"
661661
" 9F18 \\| Issuer Script Identifier : \\[4\\] 00 00 40 00[\r\n]"
662-
" 86 \\| Issuer Script Command : \\[14\\] 04 DA 9F 58 09 00 C7 35 62 86 E3 77 98 89 \\(PUT DATA\\)[\r\n]"
662+
" 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]"
663663
)
664664
set_tests_properties(emv_decode_issuer_response_test5
665665
PROPERTIES

0 commit comments

Comments
 (0)