renamed 'hf mfdes readdata, writedata' to 'read/write'
[RRG-proxmark3.git] / client / src / cmdhfemrtd.c
blob7791233dc56ce44cf9685f27f8d74ae3eed08e9f
1 //-----------------------------------------------------------------------------
2 // Copyright (C) 2020 A. Ozkal
3 //
4 // This code is licensed to you under the terms of the GNU GPL, version 2 or,
5 // at your option, any later version. See the LICENSE.txt file for the text of
6 // the license.
7 //-----------------------------------------------------------------------------
8 // High frequency Electronic Machine Readable Travel Document commands
9 //-----------------------------------------------------------------------------
11 // This code is heavily based on mrpkey.py of RFIDIOt
13 #include "cmdhfemrtd.h"
14 #include <ctype.h>
15 #include "fileutils.h" // saveFile
16 #include "cmdparser.h" // command_t
17 #include "cmdtrace.h" // CmdTraceList
18 #include "cliparser.h" // CLIParserContext etc
19 #include "protocols.h" // definitions of ISO14A/7816 protocol
20 #include "iso7816/apduinfo.h" // GetAPDUCodeDescription
21 #include "iso7816/iso7816core.h" // Iso7816ExchangeEx etc
22 #include "crypto/libpcrypto.h" // Hash calculation (sha1, sha256, sha512)
23 #include "mifare/desfire_crypto.h" // des_encrypt/des_decrypt
24 #include "des.h" // mbedtls_des_key_set_parity
25 #include "crapto1/crapto1.h" // prng_successor
26 #include "commonutil.h" // num_to_bytes
27 #include "util_posix.h" // msclock
29 // Max file size in bytes. Used in several places.
30 // Average EF_DG2 seems to be around 20-25kB or so, but ICAO doesn't set an upper limit
31 // Iris data seems to be suggested to be around 35kB per eye (Presumably bumping up the file size to around 70kB)
32 // but as we cannot read that until we implement PACE, 35k seems to be a safe point.
33 #define EMRTD_MAX_FILE_SIZE 35000
35 // ISO7816 commands
36 #define EMRTD_SELECT 0xA4
37 #define EMRTD_EXTERNAL_AUTHENTICATE 0x82
38 #define EMRTD_GET_CHALLENGE 0x84
39 #define EMRTD_READ_BINARY 0xB0
40 #define EMRTD_P1_SELECT_BY_EF 0x02
41 #define EMRTD_P1_SELECT_BY_NAME 0x04
42 #define EMRTD_P2_PROPRIETARY 0x0C
44 // App IDs
45 #define EMRTD_AID_MRTD {0xA0, 0x00, 0x00, 0x02, 0x47, 0x10, 0x01}
47 // DESKey Types
48 const uint8_t KENC_type[4] = {0x00, 0x00, 0x00, 0x01};
49 const uint8_t KMAC_type[4] = {0x00, 0x00, 0x00, 0x02};
51 static int emrtd_dump_ef_dg2(uint8_t *file_contents, size_t file_length, const char *path);
52 static int emrtd_dump_ef_dg5(uint8_t *file_contents, size_t file_length, const char *path);
53 static int emrtd_dump_ef_dg7(uint8_t *file_contents, size_t file_length, const char *path);
54 static int emrtd_dump_ef_sod(uint8_t *file_contents, size_t file_length, const char *path);
55 static int emrtd_print_ef_com_info(uint8_t *data, size_t datalen);
56 static int emrtd_print_ef_dg1_info(uint8_t *data, size_t datalen);
57 static int emrtd_print_ef_dg11_info(uint8_t *data, size_t datalen);
58 static int emrtd_print_ef_dg12_info(uint8_t *data, size_t datalen);
59 static int emrtd_print_ef_cardaccess_info(uint8_t *data, size_t datalen);
61 typedef enum { // list must match dg_table
62 EF_COM = 0,
63 EF_DG1,
64 EF_DG2,
65 EF_DG3,
66 EF_DG4,
67 EF_DG5,
68 EF_DG6,
69 EF_DG7,
70 EF_DG8,
71 EF_DG9,
72 EF_DG10,
73 EF_DG11,
74 EF_DG12,
75 EF_DG13,
76 EF_DG14,
77 EF_DG15,
78 EF_DG16,
79 EF_SOD,
80 EF_CardAccess,
81 EF_CardSecurity,
82 } emrtd_dg_enum;
84 static emrtd_dg_t dg_table[] = {
85 // tag dg# fileid filename desc pace eac req fast parser dumper
86 {0x60, 0, 0x011E, "EF_COM", "Header and Data Group Presence Information", false, false, true, true, emrtd_print_ef_com_info, NULL},
87 {0x61, 1, 0x0101, "EF_DG1", "Details recorded in MRZ", false, false, true, true, emrtd_print_ef_dg1_info, NULL},
88 {0x75, 2, 0x0102, "EF_DG2", "Encoded Face", false, false, true, false, NULL, emrtd_dump_ef_dg2},
89 {0x63, 3, 0x0103, "EF_DG3", "Encoded Finger(s)", false, true, false, false, NULL, NULL},
90 {0x76, 4, 0x0104, "EF_DG4", "Encoded Eye(s)", false, true, false, false, NULL, NULL},
91 {0x65, 5, 0x0105, "EF_DG5", "Displayed Portrait", false, false, false, false, NULL, emrtd_dump_ef_dg5},
92 {0x66, 6, 0x0106, "EF_DG6", "Reserved for Future Use", false, false, false, false, NULL, NULL},
93 {0x67, 7, 0x0107, "EF_DG7", "Displayed Signature or Usual Mark", false, false, false, false, NULL, emrtd_dump_ef_dg7},
94 {0x68, 8, 0x0108, "EF_DG8", "Data Feature(s)", false, false, false, true, NULL, NULL},
95 {0x69, 9, 0x0109, "EF_DG9", "Structure Feature(s)", false, false, false, true, NULL, NULL},
96 {0x6a, 10, 0x010A, "EF_DG10", "Substance Feature(s)", false, false, false, true, NULL, NULL},
97 {0x6b, 11, 0x010B, "EF_DG11", "Additional Personal Detail(s)", false, false, false, true, emrtd_print_ef_dg11_info, NULL},
98 {0x6c, 12, 0x010C, "EF_DG12", "Additional Document Detail(s)", false, false, false, true, emrtd_print_ef_dg12_info, NULL},
99 {0x6d, 13, 0x010D, "EF_DG13", "Optional Detail(s)", false, false, false, true, NULL, NULL},
100 {0x6e, 14, 0x010E, "EF_DG14", "Security Options", false, false, false, true, NULL, NULL},
101 {0x6f, 15, 0x010F, "EF_DG15", "Active Authentication Public Key Info", false, false, false, true, NULL, NULL},
102 {0x70, 16, 0x0110, "EF_DG16", "Person(s) to Notify", false, false, false, true, NULL, NULL},
103 {0x77, 0, 0x011D, "EF_SOD", "Document Security Object", false, false, false, false, NULL, emrtd_dump_ef_sod},
104 {0xff, 0, 0x011C, "EF_CardAccess", "PACE SecurityInfos", true, false, true, true, emrtd_print_ef_cardaccess_info, NULL},
105 {0xff, 0, 0x011D, "EF_CardSecurity", "PACE SecurityInfos for Chip Authentication Mapping", true, false, false, true, NULL, NULL},
106 {0x00, 0, 0, NULL, NULL, false, false, false, false, NULL, NULL}
109 // https://security.stackexchange.com/questions/131241/where-do-magic-constants-for-signature-algorithms-come-from
110 // https://tools.ietf.org/html/rfc3447#page-43
111 static emrtd_hashalg_t hashalg_table[] = {
112 // name hash func len len descriptor
113 {"SHA-1", sha1hash, 20, 7, {0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A}},
114 {"SHA-256", sha256hash, 32, 11, {0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01}},
115 {"SHA-512", sha512hash, 64, 11, {0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03}},
116 {NULL, NULL, 0, 0, {}}
119 static emrtd_pacealg_t pacealg_table[] = {
120 // name keygen descriptor
121 {"DH, Generic Mapping, 3DES-CBC-CBC", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x01, 0x01}},
122 {"DH, Generic Mapping, AES-CMAC-128", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x01, 0x02}},
123 {"DH, Generic Mapping, AES-CMAC-192", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x01, 0x03}},
124 {"DH, Generic Mapping, AES-CMAC-256", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x01, 0x04}},
125 {"ECDH, Generic Mapping, 3DES-CBC-CBC", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x02, 0x01}},
126 {"ECDH, Generic Mapping, AES-CMAC-128", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x02, 0x02}},
127 {"ECDH, Generic Mapping, AES-CMAC-192", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x02, 0x03}},
128 {"ECDH, Generic Mapping, AES-CMAC-256", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x02, 0x04}},
129 {"DH, Integrated Mapping, 3DES-CBC-CBC", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x03, 0x01}},
130 {"DH, Integrated Mapping, AES-CMAC-128", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x03, 0x02}},
131 {"DH, Integrated Mapping, AES-CMAC-192", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x03, 0x03}},
132 {"DH, Integrated Mapping, AES-CMAC-256", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x03, 0x04}},
133 {"ECDH, Integrated Mapping, 3DES-CBC-CBC", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x04, 0x01}},
134 {"ECDH, Integrated Mapping, AES-CMAC-128", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x04, 0x02}},
135 {"ECDH, Integrated Mapping, AES-CMAC-192", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x04, 0x03}},
136 {"ECDH, Integrated Mapping, AES-CMAC-256", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x04, 0x04}},
137 {NULL, NULL, {}}
140 static emrtd_pacesdp_t pacesdp_table[] = {
141 // id name size
142 {0, "1024-bit MODP Group with 160-bit Prime Order Subgroup", 1024},
143 {1, "2048-bit MODP Group with 224-bit Prime Order Subgroup", 2048},
144 {2, "2048-bit MODP Group with 256-bit Prime Order Subgroup", 2048},
145 {8, "NIST P-192 (secp192r1)", 192},
146 {10, "NIST P-224 (secp224r1)", 224},
147 {12, "NIST P-256 (secp256r1)", 256},
148 {15, "NIST P-384 (secp384r1)", 384},
149 {18, "NIST P-521 (secp521r1)", 521},
150 {9, "BrainpoolP192r1", 192},
151 {11, "BrainpoolP224r1", 224},
152 {13, "BrainpoolP256r1", 256},
153 {14, "BrainpoolP320r1", 320},
154 {16, "BrainpoolP384r1", 384},
155 {17, "BrainpoolP521r1", 521},
156 {32, NULL, 0}
159 static emrtd_dg_t *emrtd_tag_to_dg(uint8_t tag) {
160 for (int dgi = 0; dg_table[dgi].filename != NULL; dgi++) {
161 if (dg_table[dgi].tag == tag) {
162 return &dg_table[dgi];
165 return NULL;
167 static emrtd_dg_t *emrtd_fileid_to_dg(uint16_t file_id) {
168 for (int dgi = 0; dg_table[dgi].filename != NULL; dgi++) {
169 if (dg_table[dgi].fileid == file_id) {
170 return &dg_table[dgi];
173 return NULL;
176 static int CmdHelp(const char *Cmd);
178 static bool emrtd_exchange_commands(sAPDU apdu, bool include_le, uint16_t le, uint8_t *dataout, size_t maxdataoutlen, size_t *dataoutlen, bool activate_field, bool keep_field_on) {
179 uint16_t sw;
180 int res = Iso7816ExchangeEx(CC_CONTACTLESS, activate_field, keep_field_on, apdu, include_le, le, dataout, maxdataoutlen, dataoutlen, &sw);
182 if (res != PM3_SUCCESS) {
183 return false;
186 if (sw != 0x9000) {
187 PrintAndLogEx(DEBUG, "Command failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
188 return false;
190 return true;
193 static int emrtd_exchange_commands_noout(sAPDU apdu, bool activate_field, bool keep_field_on) {
194 uint8_t response[PM3_CMD_DATA_SIZE];
195 size_t resplen = 0;
197 return emrtd_exchange_commands(apdu, false, 0, response, 0, &resplen, activate_field, keep_field_on);
200 static char emrtd_calculate_check_digit(char *data) {
201 int mrz_weight[] = {7, 3, 1};
202 int value, cd = 0;
204 for (int i = 0; i < strlen(data); i++) {
205 char d = data[i];
206 if ('A' <= d && d <= 'Z') {
207 value = d - 55;
208 } else if ('a' <= d && d <= 'z') {
209 value = d - 87;
210 } else if (d == '<') {
211 value = 0;
212 } else { // Numbers
213 value = d - 48;
215 cd += value * mrz_weight[i % 3];
217 return cd % 10;
220 static int emrtd_get_asn1_data_length(uint8_t *datain, int datainlen, int offset) {
221 PrintAndLogEx(DEBUG, "asn1 datalength, datain: %s", sprint_hex_inrow(datain, datainlen));
222 int lenfield = (int) * (datain + offset);
223 PrintAndLogEx(DEBUG, "asn1 datalength, lenfield: %02X", lenfield);
224 if (lenfield <= 0x7f) {
225 return lenfield;
226 } else if (lenfield == 0x80) {
227 // TODO: 0x80 means indeterminate, and this impl is a workaround.
228 // Giving rest of the file is a workaround, nothing more, nothing less.
229 // https://wf.lavatech.top/ave-but-random/emrtd-data-quirks#EF_SOD
230 return datainlen;
231 } else if (lenfield == 0x81) {
232 int tmp = (*(datain + offset + 1));
233 return tmp;
234 //return ((int) * (datain + offset + 1));
235 } else if (lenfield == 0x82) {
236 int tmp = (*(datain + offset + 1) << 8);
237 tmp |= *(datain + offset + 2);
238 return tmp;
239 //return ((int) * (datain + offset + 1) << 8) | ((int) * (datain + offset + 2));
240 } else if (lenfield == 0x83) {
241 int tmp = (*(datain + offset + 1) << 16);
242 tmp |= (*(datain + offset + 2) << 8);
243 tmp |= *(datain + offset + 3);
244 return tmp;
245 //return (((int) * (datain + offset + 1) << 16) | ((int) * (datain + offset + 2)) << 8) | ((int) * (datain + offset + 3));
247 return 0;
250 static int emrtd_get_asn1_field_length(uint8_t *datain, int datainlen, int offset) {
251 PrintAndLogEx(DEBUG, "asn1 fieldlength, datain: %s", sprint_hex_inrow(datain, datainlen));
252 int lenfield = (int) * (datain + offset);
253 PrintAndLogEx(DEBUG, "asn1 fieldlength, lenfield: %02X", lenfield);
254 if (lenfield <= 0x80) {
255 return 1;
256 } else if (lenfield == 0x81) {
257 return 2;
258 } else if (lenfield == 0x82) {
259 return 3;
260 } else if (lenfield == 0x83) {
261 return 4;
263 return 0;
266 static void des_encrypt_ecb(uint8_t *key, uint8_t *input, uint8_t *output) {
267 mbedtls_des_context ctx_enc;
268 mbedtls_des_setkey_enc(&ctx_enc, key);
269 mbedtls_des_crypt_ecb(&ctx_enc, input, output);
270 mbedtls_des_free(&ctx_enc);
273 static void des_decrypt_ecb(uint8_t *key, uint8_t *input, uint8_t *output) {
274 mbedtls_des_context ctx_dec;
275 mbedtls_des_setkey_dec(&ctx_dec, key);
276 mbedtls_des_crypt_ecb(&ctx_dec, input, output);
277 mbedtls_des_free(&ctx_dec);
280 static void des3_encrypt_cbc(uint8_t *iv, uint8_t *key, uint8_t *input, int inputlen, uint8_t *output) {
281 mbedtls_des3_context ctx;
282 mbedtls_des3_set2key_enc(&ctx, key);
284 mbedtls_des3_crypt_cbc(&ctx // des3_context
285 , MBEDTLS_DES_ENCRYPT // int mode
286 , inputlen // length
287 , iv // iv[8]
288 , input // input
289 , output // output
291 mbedtls_des3_free(&ctx);
294 static void des3_decrypt_cbc(uint8_t *iv, uint8_t *key, uint8_t *input, int inputlen, uint8_t *output) {
295 mbedtls_des3_context ctx;
296 mbedtls_des3_set2key_dec(&ctx, key);
298 mbedtls_des3_crypt_cbc(&ctx // des3_context
299 , MBEDTLS_DES_DECRYPT // int mode
300 , inputlen // length
301 , iv // iv[8]
302 , input // input
303 , output // output
305 mbedtls_des3_free(&ctx);
308 static int pad_block(uint8_t *input, int inputlen, uint8_t *output) {
309 uint8_t padding[8] = {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
311 memcpy(output, input, inputlen);
313 int to_pad = (8 - (inputlen % 8));
315 for (int i = 0; i < to_pad; i++) {
316 output[inputlen + i] = padding[i];
319 return inputlen + to_pad;
322 static void retail_mac(uint8_t *key, uint8_t *input, int inputlen, uint8_t *output) {
323 // This code assumes blocklength (n) = 8, and input len of up to 240 or so chars
324 // This code takes inspirations from https://github.com/devinvenable/iso9797algorithm3
325 uint8_t k0[8];
326 uint8_t k1[8];
327 uint8_t intermediate[8] = {0x00};
328 uint8_t intermediate_des[256];
329 uint8_t block[8];
330 uint8_t message[256];
332 // Populate keys
333 memcpy(k0, key, 8);
334 memcpy(k1, key + 8, 8);
336 // Prepare message
337 int blocksize = pad_block(input, inputlen, message);
339 // Do chaining and encryption
340 for (int i = 0; i < (blocksize / 8); i++) {
341 memcpy(block, message + (i * 8), 8);
343 // XOR
344 for (int x = 0; x < 8; x++) {
345 intermediate[x] = intermediate[x] ^ block[x];
348 des_encrypt_ecb(k0, intermediate, intermediate_des);
349 memcpy(intermediate, intermediate_des, 8);
353 des_decrypt_ecb(k1, intermediate, intermediate_des);
354 memcpy(intermediate, intermediate_des, 8);
356 des_encrypt_ecb(k0, intermediate, intermediate_des);
357 memcpy(output, intermediate_des, 8);
360 static void emrtd_deskey(uint8_t *seed, const uint8_t *type, int length, uint8_t *dataout) {
361 PrintAndLogEx(DEBUG, "seed.............. %s", sprint_hex_inrow(seed, 16));
363 // combine seed and type
364 uint8_t data[50];
365 memcpy(data, seed, length);
366 memcpy(data + length, type, 4);
367 PrintAndLogEx(DEBUG, "data.............. %s", sprint_hex_inrow(data, length + 4));
369 // SHA1 the key
370 unsigned char key[64];
371 sha1hash(data, length + 4, key);
372 PrintAndLogEx(DEBUG, "key............... %s", sprint_hex_inrow(key, length + 4));
374 // Set parity bits
375 for (int i = 0; i < ((length + 4) / 8); i++) {
376 mbedtls_des_key_set_parity(key + (i * 8));
378 PrintAndLogEx(DEBUG, "post-parity key... %s", sprint_hex_inrow(key, 20));
380 memcpy(dataout, &key, length);
383 static void _emrtd_convert_fileid(uint16_t file, uint8_t *dataout) {
384 dataout[0] = file >> 8;
385 dataout[1] = file & 0xFF;
388 static int emrtd_select_file_by_name(uint8_t namelen, uint8_t *name) {
389 return emrtd_exchange_commands_noout((sAPDU) {0, EMRTD_SELECT, EMRTD_P1_SELECT_BY_NAME, 0x0C, namelen, name}, false, true);
392 static int emrtd_select_file_by_ef(uint16_t file_id) {
393 uint8_t data[2];
394 _emrtd_convert_fileid(file_id, data);
395 return emrtd_exchange_commands_noout((sAPDU) {0, EMRTD_SELECT, EMRTD_P1_SELECT_BY_EF, 0x0C, sizeof(data), data}, false, true);
398 static int emrtd_get_challenge(int length, uint8_t *dataout, size_t maxdataoutlen, size_t *dataoutlen) {
399 return emrtd_exchange_commands((sAPDU) {0, EMRTD_GET_CHALLENGE, 0, 0, 0, NULL}, true, length, dataout, maxdataoutlen, dataoutlen, false, true);
402 static int emrtd_external_authenticate(uint8_t *data, int length, uint8_t *dataout, size_t maxdataoutlen, size_t *dataoutlen) {
403 return emrtd_exchange_commands((sAPDU) {0, EMRTD_EXTERNAL_AUTHENTICATE, 0, 0, length, data}, true, length, dataout, maxdataoutlen, dataoutlen, false, true);
406 static int _emrtd_read_binary(int offset, int bytes_to_read, uint8_t *dataout, size_t maxdataoutlen, size_t *dataoutlen) {
407 return emrtd_exchange_commands((sAPDU) {0, EMRTD_READ_BINARY, offset >> 8, offset & 0xFF, 0, NULL}, true, bytes_to_read, dataout, maxdataoutlen, dataoutlen, false, true);
410 static void emrtd_bump_ssc(uint8_t *ssc) {
411 PrintAndLogEx(DEBUG, "ssc-b: %s", sprint_hex_inrow(ssc, 8));
412 for (int i = 7; i > 0; i--) {
413 if ((*(ssc + i)) == 0xFF) {
414 // Set anything already FF to 0, we'll do + 1 on num to left anyways
415 (*(ssc + i)) = 0;
416 } else {
417 (*(ssc + i)) += 1;
418 PrintAndLogEx(DEBUG, "ssc-a: %s", sprint_hex_inrow(ssc, 8));
419 return;
424 static bool emrtd_check_cc(uint8_t *ssc, uint8_t *key, uint8_t *rapdu, int rapdulength) {
425 // https://elixi.re/i/clarkson.png
426 uint8_t k[500];
427 uint8_t cc[500];
429 emrtd_bump_ssc(ssc);
431 memcpy(k, ssc, 8);
432 int length = 0;
433 int length2 = 0;
435 if (*(rapdu) == 0x87) {
436 length += 2 + (*(rapdu + 1));
437 memcpy(k + 8, rapdu, length);
438 PrintAndLogEx(DEBUG, "len1: %i", length);
441 if ((*(rapdu + length)) == 0x99) {
442 length2 += 2 + (*(rapdu + (length + 1)));
443 memcpy(k + length + 8, rapdu + length, length2);
444 PrintAndLogEx(DEBUG, "len2: %i", length2);
447 int klength = length + length2 + 8;
449 retail_mac(key, k, klength, cc);
450 PrintAndLogEx(DEBUG, "cc: %s", sprint_hex_inrow(cc, 8));
451 PrintAndLogEx(DEBUG, "rapdu: %s", sprint_hex_inrow(rapdu, rapdulength));
452 PrintAndLogEx(DEBUG, "rapdu cut: %s", sprint_hex_inrow(rapdu + (rapdulength - 8), 8));
453 PrintAndLogEx(DEBUG, "k: %s", sprint_hex_inrow(k, klength));
455 return memcmp(cc, rapdu + (rapdulength - 8), 8) == 0;
458 static bool emrtd_secure_select_file_by_ef(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, uint16_t file) {
459 uint8_t response[PM3_CMD_DATA_SIZE];
460 size_t resplen = 0;
462 // convert fileid to bytes
463 uint8_t file_id[2];
464 _emrtd_convert_fileid(file, file_id);
466 uint8_t iv[8] = { 0x00 };
467 uint8_t cmd[8];
468 uint8_t data[21];
469 uint8_t temp[8] = {0x0c, 0xa4, EMRTD_P1_SELECT_BY_EF, 0x0c};
471 int cmdlen = pad_block(temp, 4, cmd);
472 int datalen = pad_block(file_id, 2, data);
473 PrintAndLogEx(DEBUG, "cmd: %s", sprint_hex_inrow(cmd, cmdlen));
474 PrintAndLogEx(DEBUG, "data: %s", sprint_hex_inrow(data, datalen));
476 des3_encrypt_cbc(iv, kenc, data, datalen, temp);
477 PrintAndLogEx(DEBUG, "temp: %s", sprint_hex_inrow(temp, datalen));
478 uint8_t do87[11] = {0x87, 0x09, 0x01};
479 memcpy(do87 + 3, temp, datalen);
480 PrintAndLogEx(DEBUG, "do87: %s", sprint_hex_inrow(do87, datalen + 3));
482 uint8_t m[19];
483 memcpy(m, cmd, cmdlen);
484 memcpy(m + cmdlen, do87, (datalen + 3));
485 PrintAndLogEx(DEBUG, "m: %s", sprint_hex_inrow(m, datalen + cmdlen + 3));
487 emrtd_bump_ssc(ssc);
489 uint8_t n[27];
490 memcpy(n, ssc, 8);
491 memcpy(n + 8, m, (cmdlen + datalen + 3));
492 PrintAndLogEx(DEBUG, "n: %s", sprint_hex_inrow(n, (cmdlen + datalen + 11)));
494 uint8_t cc[8];
495 retail_mac(kmac, n, (cmdlen + datalen + 11), cc);
496 PrintAndLogEx(DEBUG, "cc: %s", sprint_hex_inrow(cc, 8));
498 uint8_t do8e[10] = {0x8E, 0x08};
499 memcpy(do8e + 2, cc, 8);
500 PrintAndLogEx(DEBUG, "do8e: %s", sprint_hex_inrow(do8e, 10));
502 int lc = datalen + 3 + 10;
503 PrintAndLogEx(DEBUG, "lc: %i", lc);
505 memcpy(data, do87, datalen + 3);
506 memcpy(data + (datalen + 3), do8e, 10);
507 PrintAndLogEx(DEBUG, "data: %s", sprint_hex_inrow(data, lc));
509 if (emrtd_exchange_commands((sAPDU) {0x0C, EMRTD_SELECT, EMRTD_P1_SELECT_BY_EF, 0x0C, lc, data}, true, 0, response, sizeof(response), &resplen, false, true) == false) {
510 return false;
513 return emrtd_check_cc(ssc, kmac, response, resplen);
516 static bool _emrtd_secure_read_binary(uint8_t *kmac, uint8_t *ssc, int offset, int bytes_to_read, uint8_t *dataout, size_t maxdataoutlen, size_t *dataoutlen) {
517 uint8_t cmd[8];
518 uint8_t data[21];
519 uint8_t temp[8] = {0x0c, 0xb0};
521 PrintAndLogEx(DEBUG, "kmac: %s", sprint_hex_inrow(kmac, 20));
523 // Set p1 and p2
524 temp[2] = (uint8_t)(offset >> 8);
525 temp[3] = (uint8_t)(offset >> 0);
527 int cmdlen = pad_block(temp, 4, cmd);
528 PrintAndLogEx(DEBUG, "cmd: %s", sprint_hex_inrow(cmd, cmdlen));
530 uint8_t do97[3] = {0x97, 0x01, bytes_to_read};
532 uint8_t m[11];
533 memcpy(m, cmd, 8);
534 memcpy(m + 8, do97, 3);
536 emrtd_bump_ssc(ssc);
538 uint8_t n[19];
539 memcpy(n, ssc, 8);
540 memcpy(n + 8, m, 11);
541 PrintAndLogEx(DEBUG, "n: %s", sprint_hex_inrow(n, 19));
543 uint8_t cc[8];
544 retail_mac(kmac, n, 19, cc);
545 PrintAndLogEx(DEBUG, "cc: %s", sprint_hex_inrow(cc, 8));
547 uint8_t do8e[10] = {0x8E, 0x08};
548 memcpy(do8e + 2, cc, 8);
549 PrintAndLogEx(DEBUG, "do8e: %s", sprint_hex_inrow(do8e, 10));
551 int lc = 13;
552 PrintAndLogEx(DEBUG, "lc: %i", lc);
554 memcpy(data, do97, 3);
555 memcpy(data + 3, do8e, 10);
556 PrintAndLogEx(DEBUG, "data: %s", sprint_hex_inrow(data, lc));
558 if (emrtd_exchange_commands((sAPDU) {0x0C, EMRTD_READ_BINARY, offset >> 8, offset & 0xFF, lc, data}, true, 0, dataout, maxdataoutlen, dataoutlen, false, true) == false) {
559 return false;
562 return emrtd_check_cc(ssc, kmac, dataout, *dataoutlen);
565 static bool _emrtd_secure_read_binary_decrypt(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, int offset, int bytes_to_read, uint8_t *dataout, size_t *dataoutlen) {
566 uint8_t response[500];
567 uint8_t temp[500];
568 size_t resplen, cutat = 0;
569 uint8_t iv[8] = { 0x00 };
571 if (_emrtd_secure_read_binary(kmac, ssc, offset, bytes_to_read, response, sizeof(response), &resplen) == false) {
572 return false;
575 PrintAndLogEx(DEBUG, "secreadbindec, offset %i on read %i: encrypted: %s", offset, bytes_to_read, sprint_hex_inrow(response, resplen));
577 cutat = ((int) response[1]) - 1;
579 des3_decrypt_cbc(iv, kenc, response + 3, cutat, temp);
580 memcpy(dataout, temp, bytes_to_read);
581 PrintAndLogEx(DEBUG, "secreadbindec, offset %i on read %i: decrypted: %s", offset, bytes_to_read, sprint_hex_inrow(temp, cutat));
582 PrintAndLogEx(DEBUG, "secreadbindec, offset %i on read %i: decrypted and cut: %s", offset, bytes_to_read, sprint_hex_inrow(dataout, bytes_to_read));
583 *dataoutlen = bytes_to_read;
584 return true;
587 static int emrtd_read_file(uint8_t *dataout, size_t *dataoutlen, uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, bool use_secure) {
588 uint8_t response[EMRTD_MAX_FILE_SIZE];
589 size_t resplen = 0;
590 uint8_t tempresponse[500];
591 size_t tempresplen = 0;
592 int toread = 4;
593 int offset = 0;
595 if (use_secure) {
596 if (_emrtd_secure_read_binary_decrypt(kenc, kmac, ssc, offset, toread, response, &resplen) == false) {
597 return false;
599 } else {
600 if (_emrtd_read_binary(offset, toread, response, sizeof(response), &resplen) == false) {
601 return false;
605 int datalen = emrtd_get_asn1_data_length(response, resplen, 1);
606 int readlen = datalen - (3 - emrtd_get_asn1_field_length(response, resplen, 1));
607 offset = 4;
609 uint8_t lnbreak = 32;
610 PrintAndLogEx(INFO, "." NOLF);
611 while (readlen > 0) {
612 toread = readlen;
613 if (readlen > 118) {
614 toread = 118;
617 if (use_secure) {
618 if (_emrtd_secure_read_binary_decrypt(kenc, kmac, ssc, offset, toread, tempresponse, &tempresplen) == false) {
619 PrintAndLogEx(NORMAL, "");
620 return false;
622 } else {
623 if (_emrtd_read_binary(offset, toread, tempresponse, sizeof(tempresponse), &tempresplen) == false) {
624 PrintAndLogEx(NORMAL, "");
625 return false;
629 memcpy(response + resplen, tempresponse, tempresplen);
630 offset += toread;
631 readlen -= toread;
632 resplen += tempresplen;
634 PrintAndLogEx(NORMAL, "." NOLF);
635 fflush(stdout);
636 lnbreak--;
637 if (lnbreak == 0) {
638 PrintAndLogEx(NORMAL, "");
639 PrintAndLogEx(INFO, "." NOLF);
640 lnbreak = 32;
643 PrintAndLogEx(NORMAL, "");
645 memcpy(dataout, &response, resplen);
646 *dataoutlen = resplen;
647 return true;
650 static int emrtd_lds_determine_tag_length(uint8_t tag) {
651 if ((tag == 0x5F) || (tag == 0x7F)) {
652 return 2;
654 return 1;
657 static bool emrtd_lds_get_data_by_tag(uint8_t *datain, size_t datainlen, uint8_t *dataout, size_t *dataoutlen, int tag1, int tag2, bool twobytetag, bool entertoptag, size_t skiptagcount) {
658 int offset = 0;
659 int skipcounter = 0;
661 if (entertoptag) {
662 offset += emrtd_lds_determine_tag_length(*datain);
663 offset += emrtd_get_asn1_field_length(datain, datainlen, offset);
666 while (offset < datainlen) {
667 PrintAndLogEx(DEBUG, "emrtd_lds_get_data_by_tag, offset: %i, data: %X", offset, *(datain + offset));
668 // Determine element ID length to set as offset on asn1datalength
669 int e_idlen = emrtd_lds_determine_tag_length(*(datain + offset));
671 // Get the length of the element
672 int e_datalen = emrtd_get_asn1_data_length(datain + offset, datainlen - offset, e_idlen);
674 // Get the length of the element's length
675 int e_fieldlen = emrtd_get_asn1_field_length(datain + offset, datainlen - offset, e_idlen);
677 PrintAndLogEx(DEBUG, "emrtd_lds_get_data_by_tag, e_idlen: %02X, e_datalen: %02X, e_fieldlen: %02X", e_idlen, e_datalen, e_fieldlen);
679 // If the element is what we're looking for, get the data and return true
680 if (*(datain + offset) == tag1 && (!twobytetag || *(datain + offset + 1) == tag2)) {
681 if (skipcounter < skiptagcount) {
682 skipcounter += 1;
683 } else if (datainlen > e_datalen) {
684 *dataoutlen = e_datalen;
685 memcpy(dataout, datain + offset + e_idlen + e_fieldlen, e_datalen);
686 return true;
687 } else {
688 PrintAndLogEx(ERR, "error (emrtd_lds_get_data_by_tag) e_datalen out-of-bounds");
689 return false;
692 offset += e_idlen + e_datalen + e_fieldlen;
694 // Return false if we can't find the relevant element
695 return false;
698 static bool emrtd_select_and_read(uint8_t *dataout, size_t *dataoutlen, uint16_t file, uint8_t *ks_enc, uint8_t *ks_mac, uint8_t *ssc, bool use_secure) {
699 if (use_secure) {
700 if (emrtd_secure_select_file_by_ef(ks_enc, ks_mac, ssc, file) == false) {
701 PrintAndLogEx(ERR, "Failed to secure select %04X", file);
702 return false;
704 } else {
705 if (emrtd_select_file_by_ef(file) == false) {
706 PrintAndLogEx(ERR, "Failed to select %04X", file);
707 return false;
711 if (emrtd_read_file(dataout, dataoutlen, ks_enc, ks_mac, ssc, use_secure) == false) {
712 PrintAndLogEx(ERR, "Failed to read %04X", file);
713 return false;
715 return true;
718 const uint8_t jpeg_header[4] = { 0xFF, 0xD8, 0xFF, 0xE0 };
719 const uint8_t jpeg2k_header[6] = { 0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50 };
721 static int emrtd_dump_ef_dg2(uint8_t *file_contents, size_t file_length, const char *path) {
722 int offset, datalen = 0;
724 // This is a hacky impl that just looks for the image header. I'll improve it eventually.
725 // based on mrpkey.py
726 // Note: Doing file_length - 6 to account for the longest data we're checking.
727 // Checks first byte before the rest to reduce overhead
728 for (offset = 0; offset < file_length - 6; offset++) {
729 if ((file_contents[offset] == 0xFF && memcmp(jpeg_header, file_contents + offset, 4) == 0) ||
730 (file_contents[offset] == 0x00 && memcmp(jpeg2k_header, file_contents + offset, 6) == 0)) {
731 datalen = file_length - offset;
732 break;
736 // If we didn't get any data, return false.
737 if (datalen == 0) {
738 return PM3_ESOFT;
741 char *filepath = calloc(strlen(path) + 100, sizeof(char));
742 if (filepath == NULL)
743 return PM3_EMALLOC;
744 strcpy(filepath, path);
745 strncat(filepath, PATHSEP, 2);
746 strcat(filepath, dg_table[EF_DG2].filename);
748 saveFile(filepath, file_contents[offset] == 0xFF ? ".jpg" : ".jp2", file_contents + offset, datalen);
750 free(filepath);
751 return PM3_SUCCESS;
754 static int emrtd_dump_ef_dg5(uint8_t *file_contents, size_t file_length, const char *path) {
755 uint8_t data[EMRTD_MAX_FILE_SIZE];
756 size_t datalen = 0;
758 // If we can't find image in EF_DG5, return false.
759 if (emrtd_lds_get_data_by_tag(file_contents, file_length, data, &datalen, 0x5F, 0x40, true, true, 0) == false) {
760 return PM3_ESOFT;
763 if (datalen < EMRTD_MAX_FILE_SIZE) {
764 char *filepath = calloc(strlen(path) + 100, sizeof(char));
765 if (filepath == NULL)
766 return PM3_EMALLOC;
767 strcpy(filepath, path);
768 strncat(filepath, PATHSEP, 2);
769 strcat(filepath, dg_table[EF_DG5].filename);
771 saveFile(filepath, data[0] == 0xFF ? ".jpg" : ".jp2", data, datalen);
773 free(filepath);
774 } else {
775 PrintAndLogEx(ERR, "error (emrtd_dump_ef_dg5) datalen out-of-bounds");
776 return PM3_ESOFT;
778 return PM3_SUCCESS;
781 static int emrtd_dump_ef_dg7(uint8_t *file_contents, size_t file_length, const char *path) {
782 uint8_t data[EMRTD_MAX_FILE_SIZE];
783 size_t datalen = 0;
785 // If we can't find image in EF_DG7, return false.
786 if (emrtd_lds_get_data_by_tag(file_contents, file_length, data, &datalen, 0x5F, 0x42, true, true, 0) == false) {
787 return PM3_ESOFT;
790 if (datalen < EMRTD_MAX_FILE_SIZE) {
791 char *filepath = calloc(strlen(path) + 100, sizeof(char));
792 if (filepath == NULL)
793 return PM3_EMALLOC;
794 strcpy(filepath, path);
795 strncat(filepath, PATHSEP, 2);
796 strcat(filepath, dg_table[EF_DG7].filename);
798 saveFile(filepath, data[0] == 0xFF ? ".jpg" : ".jp2", data, datalen);
800 free(filepath);
801 } else {
802 PrintAndLogEx(ERR, "error (emrtd_dump_ef_dg7) datalen out-of-bounds");
803 return PM3_ESOFT;
805 return PM3_SUCCESS;
808 static int emrtd_dump_ef_sod(uint8_t *file_contents, size_t file_length, const char *path) {
809 int fieldlen = emrtd_get_asn1_field_length(file_contents, file_length, 1);
810 int datalen = emrtd_get_asn1_data_length(file_contents, file_length, 1);
812 if (fieldlen + 1 > EMRTD_MAX_FILE_SIZE) {
813 PrintAndLogEx(ERR, "error (emrtd_dump_ef_sod) fieldlen out-of-bounds");
814 return PM3_EOUTOFBOUND;
817 char *filepath = calloc(strlen(path) + 100, sizeof(char));
818 if (filepath == NULL)
819 return PM3_EMALLOC;
821 strcpy(filepath, path);
822 strncat(filepath, PATHSEP, 2);
823 strcat(filepath, dg_table[EF_SOD].filename);
825 saveFile(filepath, ".p7b", file_contents + fieldlen + 1, datalen);
826 free(filepath);
827 return PM3_ESOFT;
830 static bool emrtd_dump_file(uint8_t *ks_enc, uint8_t *ks_mac, uint8_t *ssc, uint16_t file, const char *name, bool use_secure, const char *path) {
831 uint8_t response[EMRTD_MAX_FILE_SIZE];
832 size_t resplen = 0;
834 if (emrtd_select_and_read(response, &resplen, file, ks_enc, ks_mac, ssc, use_secure) == false) {
835 return false;
838 char *filepath = calloc(strlen(path) + 100, sizeof(char));
839 if (filepath == NULL)
840 return false;
842 strcpy(filepath, path);
843 strncat(filepath, PATHSEP, 2);
844 strcat(filepath, name);
846 PrintAndLogEx(INFO, "Read " _YELLOW_("%s") " , len %zu", name, resplen);
847 PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars)");
848 PrintAndLogEx(DEBUG, "------------------------------------------");
849 PrintAndLogEx(DEBUG, "%s", sprint_hex_inrow(response, resplen));
850 PrintAndLogEx(DEBUG, "------------------------------------------");
851 saveFile(filepath, ".BIN", response, resplen);
853 emrtd_dg_t *dg = emrtd_fileid_to_dg(file);
854 if ((dg != NULL) && (dg->dumper != NULL)) {
855 dg->dumper(response, resplen, path);
858 free(filepath);
859 return true;
862 static void rng(int length, uint8_t *dataout) {
863 // Do very very secure prng operations
864 //for (int i = 0; i < (length / 4); i++) {
865 // num_to_bytes(prng_successor(msclock() + i, 32), 4, &dataout[i * 4]);
867 memset(dataout, 0x00, length);
870 static bool emrtd_do_bac(char *documentnumber, char *dob, char *expiry, uint8_t *ssc, uint8_t *ks_enc, uint8_t *ks_mac) {
871 uint8_t response[EMRTD_MAX_FILE_SIZE] = { 0x00 };
872 size_t resplen = 0;
874 uint8_t rnd_ic[10] = { 0x00 }; // 8 + SW
875 uint8_t kenc[50] = { 0x00 };
876 uint8_t kmac[50] = { 0x00 };
877 uint8_t k_icc[16] = { 0x00 };
878 uint8_t S[32] = { 0x00 };
880 uint8_t rnd_ifd[8], k_ifd[16];
881 rng(8, rnd_ifd);
882 rng(16, k_ifd);
884 PrintAndLogEx(DEBUG, "doc............... " _GREEN_("%s"), documentnumber);
885 PrintAndLogEx(DEBUG, "dob............... " _GREEN_("%s"), dob);
886 PrintAndLogEx(DEBUG, "exp............... " _GREEN_("%s"), expiry);
888 char documentnumbercd = emrtd_calculate_check_digit(documentnumber);
889 char dobcd = emrtd_calculate_check_digit(dob);
890 char expirycd = emrtd_calculate_check_digit(expiry);
892 char kmrz[25];
893 sprintf(kmrz, "%s%i%s%i%s%i", documentnumber, documentnumbercd, dob, dobcd, expiry, expirycd);
894 PrintAndLogEx(DEBUG, "kmrz.............. " _GREEN_("%s"), kmrz);
896 uint8_t kseed[20] = { 0x00 };
897 sha1hash((unsigned char *)kmrz, strlen(kmrz), kseed);
898 PrintAndLogEx(DEBUG, "kseed (sha1)...... %s ", sprint_hex_inrow(kseed, 16));
900 emrtd_deskey(kseed, KENC_type, 16, kenc);
901 emrtd_deskey(kseed, KMAC_type, 16, kmac);
902 PrintAndLogEx(DEBUG, "kenc.............. %s", sprint_hex_inrow(kenc, 16));
903 PrintAndLogEx(DEBUG, "kmac.............. %s", sprint_hex_inrow(kmac, 16));
905 // Get Challenge
906 if (emrtd_get_challenge(8, rnd_ic, sizeof(rnd_ic), &resplen) == false) {
907 PrintAndLogEx(ERR, "Couldn't get challenge.");
908 return false;
910 PrintAndLogEx(DEBUG, "rnd_ic............ %s", sprint_hex_inrow(rnd_ic, 8));
912 memcpy(S, rnd_ifd, 8);
913 memcpy(S + 8, rnd_ic, 8);
914 memcpy(S + 16, k_ifd, 16);
916 PrintAndLogEx(DEBUG, "S................. %s", sprint_hex_inrow(S, 32));
918 uint8_t iv[8] = { 0x00 };
919 uint8_t e_ifd[32] = { 0x00 };
921 des3_encrypt_cbc(iv, kenc, S, sizeof(S), e_ifd);
922 PrintAndLogEx(DEBUG, "e_ifd............. %s", sprint_hex_inrow(e_ifd, 32));
924 uint8_t m_ifd[8] = { 0x00 };
926 retail_mac(kmac, e_ifd, 32, m_ifd);
927 PrintAndLogEx(DEBUG, "m_ifd............. %s", sprint_hex_inrow(m_ifd, 8));
929 uint8_t cmd_data[40];
930 memcpy(cmd_data, e_ifd, 32);
931 memcpy(cmd_data + 32, m_ifd, 8);
933 // Do external authentication
934 if (emrtd_external_authenticate(cmd_data, sizeof(cmd_data), response, sizeof(response), &resplen) == false) {
935 PrintAndLogEx(ERR, "Couldn't do external authentication. Did you supply the correct MRZ info?");
936 return false;
938 PrintAndLogEx(INFO, "External authentication with BAC successful.");
940 uint8_t dec_output[32] = { 0x00 };
941 des3_decrypt_cbc(iv, kenc, response, 32, dec_output);
942 PrintAndLogEx(DEBUG, "dec_output........ %s", sprint_hex_inrow(dec_output, 32));
944 if (memcmp(rnd_ifd, dec_output + 8, 8) != 0) {
945 PrintAndLogEx(ERR, "Challenge failed, rnd_ifd does not match.");
946 return false;
949 memcpy(k_icc, dec_output + 16, 16);
951 // Calculate session keys
952 for (int x = 0; x < 16; x++) {
953 kseed[x] = k_ifd[x] ^ k_icc[x];
956 PrintAndLogEx(DEBUG, "kseed............ %s", sprint_hex_inrow(kseed, 16));
958 emrtd_deskey(kseed, KENC_type, 16, ks_enc);
959 emrtd_deskey(kseed, KMAC_type, 16, ks_mac);
961 PrintAndLogEx(DEBUG, "ks_enc........ %s", sprint_hex_inrow(ks_enc, 16));
962 PrintAndLogEx(DEBUG, "ks_mac........ %s", sprint_hex_inrow(ks_mac, 16));
964 memcpy(ssc, rnd_ic + 4, 4);
965 memcpy(ssc + 4, rnd_ifd + 4, 4);
967 PrintAndLogEx(DEBUG, "ssc........... %s", sprint_hex_inrow(ssc, 8));
969 return true;
972 static bool emrtd_connect(void) {
973 int res = Iso7816Connect(CC_CONTACTLESS);
974 return res == PM3_SUCCESS;
977 static bool emrtd_do_auth(char *documentnumber, char *dob, char *expiry, bool BAC_available, bool *BAC, uint8_t *ssc, uint8_t *ks_enc, uint8_t *ks_mac) {
979 // Select MRTD applet
980 uint8_t aid[] = EMRTD_AID_MRTD;
981 if (emrtd_select_file_by_name(sizeof(aid), aid) == false) {
982 PrintAndLogEx(ERR, "Couldn't select the MRTD application.");
983 return false;
986 // Select EF_COM
987 if (emrtd_select_file_by_ef(dg_table[EF_COM].fileid) == false) {
988 *BAC = true;
989 PrintAndLogEx(INFO, "Authentication is enforced. Will attempt external authentication.");
990 } else {
991 *BAC = false;
992 // Select EF_DG1
993 emrtd_select_file_by_ef(dg_table[EF_DG1].fileid);
995 size_t resplen = 0;
996 uint8_t response[EMRTD_MAX_FILE_SIZE] = { 0x00 };
997 if (emrtd_read_file(response, &resplen, NULL, NULL, NULL, false) == false) {
998 *BAC = true;
999 PrintAndLogEx(INFO, "Authentication is enforced. Will attempt external authentication.");
1000 } else {
1001 *BAC = false;
1005 // Do Basic Access Control
1006 if (*BAC) {
1007 // If BAC isn't available, exit out and warn user.
1008 if (!BAC_available) {
1009 PrintAndLogEx(ERR, "This eMRTD enforces authentication, but you didn't supply MRZ data. Cannot proceed.");
1010 PrintAndLogEx(HINT, "Check out hf emrtd info/dump --help, supply data with -n -d and -e.");
1011 return false;
1014 if (emrtd_do_bac(documentnumber, dob, expiry, ssc, ks_enc, ks_mac) == false) {
1015 return false;
1018 return true;
1021 int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available, const char *path) {
1022 uint8_t response[EMRTD_MAX_FILE_SIZE] = { 0x00 };
1023 size_t resplen = 0;
1024 uint8_t ssc[8] = { 0x00 };
1025 uint8_t ks_enc[16] = { 0x00 };
1026 uint8_t ks_mac[16] = { 0x00 };
1027 bool BAC = false;
1029 // Select the eMRTD
1030 if (emrtd_connect() == false) {
1031 DropField();
1032 return PM3_ESOFT;
1035 // Dump EF_CardAccess (if available)
1036 if (!emrtd_dump_file(ks_enc, ks_mac, ssc, dg_table[EF_CardAccess].fileid, dg_table[EF_CardAccess].filename, BAC, path)) {
1037 PrintAndLogEx(INFO, "Couldn't dump EF_CardAccess, card does not support PACE");
1038 PrintAndLogEx(HINT, "This is expected behavior for cards without PACE, and isn't something to be worried about");
1041 // Authenticate with the eMRTD
1042 if (!emrtd_do_auth(documentnumber, dob, expiry, BAC_available, &BAC, ssc, ks_enc, ks_mac)) {
1043 DropField();
1044 return PM3_ESOFT;
1047 // Select EF_COM
1048 if (!emrtd_select_and_read(response, &resplen, dg_table[EF_COM].fileid, ks_enc, ks_mac, ssc, BAC)) {
1049 PrintAndLogEx(ERR, "Failed to read EF_COM");
1050 DropField();
1051 return PM3_ESOFT;
1055 char *filepath = calloc(strlen(path) + 100, sizeof(char));
1056 if (filepath == NULL)
1057 return PM3_EMALLOC;
1059 strcpy(filepath, path);
1060 strncat(filepath, PATHSEP, 2);
1061 strcat(filepath, dg_table[EF_COM].filename);
1063 PrintAndLogEx(INFO, "Read EF_COM, len: %zu", resplen);
1064 PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars): %s", sprint_hex_inrow(response, resplen));
1065 saveFile(filepath, ".BIN", response, resplen);
1067 free(filepath);
1069 uint8_t filelist[50];
1070 size_t filelistlen = 0;
1072 if (emrtd_lds_get_data_by_tag(response, resplen, filelist, &filelistlen, 0x5c, 0x00, false, true, 0) == false) {
1073 PrintAndLogEx(ERR, "Failed to read file list from EF_COM");
1074 DropField();
1075 return PM3_ESOFT;
1078 PrintAndLogEx(DEBUG, "File List: %s", sprint_hex_inrow(filelist, filelistlen));
1079 // Add EF_SOD to the list
1080 filelist[filelistlen++] = 0x77;
1081 // Dump all files in the file list
1082 for (int i = 0; i < filelistlen; i++) {
1083 emrtd_dg_t *dg = emrtd_tag_to_dg(filelist[i]);
1084 if (dg == NULL) {
1085 PrintAndLogEx(INFO, "File tag not found, skipping: %02X", filelist[i]);
1086 continue;
1088 PrintAndLogEx(DEBUG, "Current file: %s", dg->filename);
1089 if (!dg->pace && !dg->eac) {
1090 emrtd_dump_file(ks_enc, ks_mac, ssc, dg->fileid, dg->filename, BAC, path);
1093 DropField();
1094 return PM3_SUCCESS;
1097 static bool emrtd_compare_check_digit(char *datain, int datalen, char expected_check_digit) {
1098 char tempdata[90] = { 0x00 };
1099 memcpy(tempdata, datain, datalen);
1101 uint8_t check_digit = emrtd_calculate_check_digit(tempdata) + 0x30;
1102 bool res = check_digit == expected_check_digit;
1103 PrintAndLogEx(DEBUG, "emrtd_compare_check_digit, expected %c == %c calculated ( %s )"
1104 , expected_check_digit
1105 , check_digit
1106 , (res) ? _GREEN_("ok") : _RED_("fail"));
1107 return res;
1110 static bool emrtd_mrz_verify_check_digit(char *mrz, int offset, int datalen) {
1111 char tempdata[90] = { 0x00 };
1112 memcpy(tempdata, mrz + offset, datalen);
1113 return emrtd_compare_check_digit(tempdata, datalen, mrz[offset + datalen]);
1116 static void emrtd_print_legal_sex(char *legal_sex) {
1117 char sex[12] = { 0x00 };
1118 switch (*legal_sex) {
1119 case 'M':
1120 strncpy(sex, "Male", 5);
1121 break;
1122 case 'F':
1123 strncpy(sex, "Female", 7);
1124 break;
1125 case '<':
1126 strncpy(sex, "Unspecified", 12);
1127 break;
1129 PrintAndLogEx(SUCCESS, "Legal Sex Marker......: " _YELLOW_("%s"), sex);
1132 static int emrtd_mrz_determine_length(char *mrz, int offset, int max_length) {
1133 int i;
1134 for (i = max_length; i >= 1; i--) {
1135 if (mrz[offset + i - 1] != '<') {
1136 return i;
1140 return 0;
1143 static int emrtd_mrz_determine_separator(char *mrz, int offset, int max_length) {
1144 // Note: this function does not account for len=0
1145 int i;
1146 for (i = max_length - 1; i > 0; i--) {
1147 if (mrz[offset + i] == '<' && mrz[offset + i + 1] == '<') {
1148 break;
1151 return i;
1154 static void emrtd_mrz_replace_pad(char *data, int datalen, char newchar) {
1155 for (int i = 0; i < datalen; i++) {
1156 if (data[i] == '<') {
1157 data[i] = newchar;
1162 static void emrtd_print_optional_elements(char *mrz, int offset, int length, bool verify_check_digit) {
1163 int i = emrtd_mrz_determine_length(mrz, offset, length);
1164 if (i == 0) {
1165 return;
1168 PrintAndLogEx(SUCCESS, "Optional elements.....: " _YELLOW_("%.*s"), i, mrz + offset);
1170 if (verify_check_digit && !emrtd_mrz_verify_check_digit(mrz, offset, length)) {
1171 PrintAndLogEx(SUCCESS, _RED_("Optional element check digit is invalid."));
1175 static void emrtd_print_document_number(char *mrz, int offset) {
1176 int i = emrtd_mrz_determine_length(mrz, offset, 9);
1177 if (i == 0) {
1178 return;
1181 PrintAndLogEx(SUCCESS, "Document Number.......: " _YELLOW_("%.*s"), i, mrz + offset);
1183 if (!emrtd_mrz_verify_check_digit(mrz, offset, 9)) {
1184 PrintAndLogEx(SUCCESS, _RED_("Document number check digit is invalid."));
1188 static void emrtd_print_name(char *mrz, int offset, int max_length, bool localized) {
1189 char final_name[100] = { 0x00 };
1190 int namelen = emrtd_mrz_determine_length(mrz, offset, max_length);
1191 if (namelen == 0) {
1192 return;
1194 int sep = emrtd_mrz_determine_separator(mrz, offset, namelen);
1196 // Account for mononyms
1197 if (sep != 0) {
1198 int firstnamelen = (namelen - (sep + 2));
1200 memcpy(final_name, mrz + offset + sep + 2, firstnamelen);
1201 final_name[firstnamelen] = ' ';
1202 memcpy(final_name + firstnamelen + 1, mrz + offset, sep);
1203 } else {
1204 memcpy(final_name, mrz + offset, namelen);
1207 // Replace < characters with spaces
1208 emrtd_mrz_replace_pad(final_name, namelen, ' ');
1210 if (localized) {
1211 PrintAndLogEx(SUCCESS, "Legal Name (Localized): " _YELLOW_("%s"), final_name);
1212 } else {
1213 PrintAndLogEx(SUCCESS, "Legal Name............: " _YELLOW_("%s"), final_name);
1217 static void emrtd_mrz_convert_date(char *mrz, int offset, char *final_date, bool is_expiry, bool is_full, bool is_ascii) {
1218 char work_date[9] = { 0x00 };
1219 int len = is_full ? 8 : 6;
1221 // Copy the data to a working array in the right format
1222 if (!is_ascii) {
1223 memcpy(work_date, sprint_hex_inrow((uint8_t *)mrz + offset, len / 2), len);
1224 } else {
1225 memcpy(work_date, mrz + offset, len);
1228 // Set offset to 0 as we've now copied data.
1229 offset = 0;
1231 if (is_full) {
1232 // If we get the full date, use the first two characters from that for year
1233 memcpy(final_date, work_date, 2);
1234 // and do + 2 on offset so that rest of code uses the right data
1235 offset += 2;
1236 } else {
1237 char temp_year[3] = { 0x00 };
1238 memcpy(temp_year, work_date, 2);
1239 // If it's > 20, assume 19xx.
1240 if (strtol(temp_year, NULL, 10) < 20 || is_expiry) {
1241 final_date[0] = '2';
1242 final_date[1] = '0';
1243 } else {
1244 final_date[0] = '1';
1245 final_date[1] = '9';
1249 memcpy(final_date + 2, work_date + offset, 2);
1250 final_date[4] = '-';
1251 memcpy(final_date + 5, work_date + offset + 2, 2);
1252 final_date[7] = '-';
1253 memcpy(final_date + 8, work_date + offset + 4, 2);
1256 static void emrtd_print_dob(char *mrz, int offset, bool full, bool ascii) {
1257 char final_date[12] = { 0x00 };
1258 emrtd_mrz_convert_date(mrz, offset, final_date, false, full, ascii);
1260 PrintAndLogEx(SUCCESS, "Date of birth.........: " _YELLOW_("%s"), final_date);
1262 if (!full && !emrtd_mrz_verify_check_digit(mrz, offset, 6)) {
1263 PrintAndLogEx(SUCCESS, _RED_("Date of Birth check digit is invalid."));
1267 static void emrtd_print_expiry(char *mrz, int offset) {
1268 char final_date[12] = { 0x00 };
1269 emrtd_mrz_convert_date(mrz, offset, final_date, true, false, true);
1271 PrintAndLogEx(SUCCESS, "Date of expiry........: " _YELLOW_("%s"), final_date);
1273 if (!emrtd_mrz_verify_check_digit(mrz, offset, 6)) {
1274 PrintAndLogEx(SUCCESS, _RED_("Date of expiry check digit is invalid."));
1278 static void emrtd_print_issuance(char *data, bool ascii) {
1279 char final_date[12] = { 0x00 };
1280 emrtd_mrz_convert_date(data, 0, final_date, true, true, ascii);
1282 PrintAndLogEx(SUCCESS, "Date of issue.........: " _YELLOW_("%s"), final_date);
1285 static void emrtd_print_personalization_timestamp(uint8_t *data) {
1286 char str_date[0x0F] = { 0x00 };
1287 strcpy(str_date, sprint_hex_inrow(data, 0x0E));
1288 char final_date[20] = { 0x00 };
1289 sprintf(final_date, "%.4s-%.2s-%.2s %.2s:%.2s:%.2s", str_date, str_date + 4, str_date + 6, str_date + 8, str_date + 10, str_date + 12);
1291 PrintAndLogEx(SUCCESS, "Personalization at....: " _YELLOW_("%s"), final_date);
1294 static void emrtd_print_unknown_timestamp_5f85(uint8_t *data) {
1295 char final_date[20] = { 0x00 };
1296 sprintf(final_date, "%.4s-%.2s-%.2s %.2s:%.2s:%.2s", data, data + 4, data + 6, data + 8, data + 10, data + 12);
1298 PrintAndLogEx(SUCCESS, "Unknown timestamp 5F85: " _YELLOW_("%s"), final_date);
1299 PrintAndLogEx(HINT, "This is very likely the personalization timestamp, but it is using an undocumented tag.");
1302 static int emrtd_print_ef_com_info(uint8_t *data, size_t datalen) {
1303 uint8_t filelist[50];
1304 size_t filelistlen = 0;
1305 bool res = emrtd_lds_get_data_by_tag(data, datalen, filelist, &filelistlen, 0x5c, 0x00, false, true, 0);
1306 if (res == false) {
1307 PrintAndLogEx(ERR, "Failed to read file list from EF_COM.");
1308 return PM3_ESOFT;
1311 // List files in the file list
1312 PrintAndLogEx(NORMAL, "");
1313 PrintAndLogEx(INFO, "-------------------- " _CYAN_("EF_COM") " --------------------");
1314 for (int i = 0; i < filelistlen; i++) {
1315 emrtd_dg_t *dg = emrtd_tag_to_dg(filelist[i]);
1316 if (dg == NULL) {
1317 PrintAndLogEx(INFO, "File tag not found, skipping: %02X", filelist[i]);
1318 continue;
1320 PrintAndLogEx(SUCCESS, "%-7s...............: " _YELLOW_("%s"), dg->filename, dg->desc);
1322 return PM3_SUCCESS;
1325 static int emrtd_print_ef_dg1_info(uint8_t *data, size_t datalen) {
1326 int td_variant = 0;
1328 PrintAndLogEx(NORMAL, "");
1329 PrintAndLogEx(INFO, "-------------------- " _CYAN_("EF_DG1") " --------------------");
1331 // MRZ on TD1 is 90 characters, 30 on each row.
1332 // MRZ on TD3 is 88 characters, 44 on each row.
1333 char mrz[90] = { 0x00 };
1334 size_t mrzlen = 0;
1336 if (emrtd_lds_get_data_by_tag(data, datalen, (uint8_t *) mrz, &mrzlen, 0x5f, 0x1f, true, true, 0) == false) {
1337 PrintAndLogEx(ERR, "Failed to read MRZ from EF_DG1.");
1338 return PM3_ESOFT;
1341 // Determine and print the document type
1342 if (mrz[0] == 'I' && mrz[1] == 'P') {
1343 PrintAndLogEx(SUCCESS, "Document Type.........: " _YELLOW_("Passport Card"));
1344 } else if (mrz[0] == 'I') {
1345 PrintAndLogEx(SUCCESS, "Document Type.........: " _YELLOW_("ID Card"));
1346 } else if (mrz[0] == 'P') {
1347 PrintAndLogEx(SUCCESS, "Document Type.........: " _YELLOW_("Passport"));
1348 } else if (mrz[0] == 'A') {
1349 PrintAndLogEx(SUCCESS, "Document Type.........: " _YELLOW_("German Residency Permit"));
1350 } else {
1351 PrintAndLogEx(SUCCESS, "Document Type.........: " _YELLOW_("Unknown"));
1354 if (mrzlen == 90) {
1355 td_variant = 1;
1356 } else if (mrzlen == 88) {
1357 td_variant = 3;
1358 } else {
1359 PrintAndLogEx(ERR, "MRZ length (%zu) is wrong.", mrzlen);
1360 return PM3_ESOFT;
1363 PrintAndLogEx(SUCCESS, "Document Form Factor..: " _YELLOW_("TD%i"), td_variant);
1365 // Print the MRZ
1366 if (td_variant == 1) {
1367 PrintAndLogEx(DEBUG, "MRZ Row 1: " _YELLOW_("%.30s"), mrz);
1368 PrintAndLogEx(DEBUG, "MRZ Row 2: " _YELLOW_("%.30s"), mrz + 30);
1369 PrintAndLogEx(DEBUG, "MRZ Row 3: " _YELLOW_("%.30s"), mrz + 60);
1370 } else if (td_variant == 3) {
1371 PrintAndLogEx(DEBUG, "MRZ Row 1: " _YELLOW_("%.44s"), mrz);
1372 PrintAndLogEx(DEBUG, "MRZ Row 2: " _YELLOW_("%.44s"), mrz + 44);
1375 PrintAndLogEx(SUCCESS, "Issuing state.........: " _YELLOW_("%.3s"), mrz + 2);
1377 if (td_variant == 3) {
1378 // Passport form factor
1379 PrintAndLogEx(SUCCESS, "Nationality...........: " _YELLOW_("%.3s"), mrz + 44 + 10);
1380 emrtd_print_name(mrz, 5, 38, false);
1381 emrtd_print_document_number(mrz, 44);
1382 emrtd_print_dob(mrz, 44 + 13, false, true);
1383 emrtd_print_legal_sex(&mrz[44 + 20]);
1384 emrtd_print_expiry(mrz, 44 + 21);
1385 emrtd_print_optional_elements(mrz, 44 + 28, 14, true);
1387 // Calculate and verify composite check digit
1388 char composite_check_data[50] = { 0x00 };
1389 memcpy(composite_check_data, mrz + 44, 10);
1390 memcpy(composite_check_data + 10, mrz + 44 + 13, 7);
1391 memcpy(composite_check_data + 17, mrz + 44 + 21, 23);
1393 if (!emrtd_compare_check_digit(composite_check_data, 39, mrz[87])) {
1394 PrintAndLogEx(SUCCESS, _RED_("Composite check digit is invalid."));
1396 } else if (td_variant == 1) {
1397 // ID form factor
1398 PrintAndLogEx(SUCCESS, "Nationality...........: " _YELLOW_("%.3s"), mrz + 30 + 15);
1399 emrtd_print_name(mrz, 60, 30, false);
1400 emrtd_print_document_number(mrz, 5);
1401 emrtd_print_dob(mrz, 30, false, true);
1402 emrtd_print_legal_sex(&mrz[30 + 7]);
1403 emrtd_print_expiry(mrz, 30 + 8);
1404 emrtd_print_optional_elements(mrz, 15, 15, false);
1405 emrtd_print_optional_elements(mrz, 30 + 18, 11, false);
1407 // Calculate and verify composite check digit
1408 if (!emrtd_compare_check_digit(mrz, 59, mrz[59])) {
1409 PrintAndLogEx(SUCCESS, _RED_("Composite check digit is invalid."));
1413 return PM3_SUCCESS;
1416 static int emrtd_print_ef_dg11_info(uint8_t *data, size_t datalen) {
1417 uint8_t taglist[100] = { 0x00 };
1418 size_t taglistlen = 0;
1419 uint8_t tagdata[1000] = { 0x00 };
1420 size_t tagdatalen = 0;
1422 PrintAndLogEx(NORMAL, "");
1423 PrintAndLogEx(INFO, "-------------------- " _CYAN_("EF_DG11") " -------------------");
1425 if (emrtd_lds_get_data_by_tag(data, datalen, taglist, &taglistlen, 0x5c, 0x00, false, true, 0) == false) {
1426 PrintAndLogEx(ERR, "Failed to read file list from EF_DG11.");
1427 return PM3_ESOFT;
1430 for (int i = 0; i < taglistlen; i++) {
1431 bool res = emrtd_lds_get_data_by_tag(data, datalen, tagdata, &tagdatalen, taglist[i], taglist[i + 1], taglist[i] == 0x5f, true, 0);
1432 (void)res;
1433 // Don't bother with empty tags
1434 if (tagdatalen == 0) {
1435 continue;
1437 // Special behavior for two char tags
1438 if (taglist[i] == 0x5f) {
1439 switch (taglist[i + 1]) {
1440 case 0x0e:
1441 emrtd_print_name((char *) tagdata, 0, tagdatalen, true);
1442 break;
1443 case 0x0f:
1444 emrtd_print_name((char *) tagdata, 0, tagdatalen, false);
1445 break;
1446 case 0x10:
1447 PrintAndLogEx(SUCCESS, "Personal Number.......: " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
1448 break;
1449 case 0x11:
1450 // TODO: acc for < separation
1451 PrintAndLogEx(SUCCESS, "Place of Birth........: " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
1452 break;
1453 case 0x42:
1454 // TODO: acc for < separation
1455 PrintAndLogEx(SUCCESS, "Permanent Address.....: " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
1456 break;
1457 case 0x12:
1458 PrintAndLogEx(SUCCESS, "Telephone.............: " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
1459 break;
1460 case 0x13:
1461 PrintAndLogEx(SUCCESS, "Profession............: " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
1462 break;
1463 case 0x14:
1464 PrintAndLogEx(SUCCESS, "Title.................: " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
1465 break;
1466 case 0x15:
1467 PrintAndLogEx(SUCCESS, "Personal Summary......: " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
1468 break;
1469 case 0x16:
1470 saveFile("ProofOfCitizenship", tagdata[0] == 0xFF ? ".jpg" : ".jp2", tagdata, tagdatalen);
1471 break;
1472 case 0x17:
1473 // TODO: acc for < separation
1474 PrintAndLogEx(SUCCESS, "Other valid TDs nums..: " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
1475 break;
1476 case 0x18:
1477 PrintAndLogEx(SUCCESS, "Custody Information...: " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
1478 break;
1479 case 0x2b:
1480 emrtd_print_dob((char *) tagdata, 0, true, tagdatalen != 4);
1481 break;
1482 default:
1483 PrintAndLogEx(SUCCESS, "Unknown Field %02X%02X....: %s", taglist[i], taglist[i + 1], sprint_hex_inrow(tagdata, tagdatalen));
1484 break;
1487 i += 1;
1488 } else {
1489 // TODO: Account for A0
1490 PrintAndLogEx(SUCCESS, "Unknown Field %02X......: %s", taglist[i], sprint_hex_inrow(tagdata, tagdatalen));
1493 return PM3_SUCCESS;
1496 static int emrtd_print_ef_dg12_info(uint8_t *data, size_t datalen) {
1497 uint8_t taglist[100] = { 0x00 };
1498 size_t taglistlen = 0;
1499 uint8_t tagdata[1000] = { 0x00 };
1500 size_t tagdatalen = 0;
1502 PrintAndLogEx(NORMAL, "");
1503 PrintAndLogEx(INFO, "-------------------- " _CYAN_("EF_DG12") " -------------------");
1505 if (emrtd_lds_get_data_by_tag(data, datalen, taglist, &taglistlen, 0x5c, 0x00, false, true, 0) == false) {
1506 PrintAndLogEx(ERR, "Failed to read file list from EF_DG12.");
1507 return PM3_ESOFT;
1510 for (int i = 0; i < taglistlen; i++) {
1511 bool res = emrtd_lds_get_data_by_tag(data, datalen, tagdata, &tagdatalen, taglist[i], taglist[i + 1], taglist[i] == 0x5f, true, 0);
1512 (void)res;
1513 // Don't bother with empty tags
1514 if (tagdatalen == 0) {
1515 continue;
1517 // Special behavior for two char tags
1518 if (taglist[i] == 0x5f) {
1519 // Several things here are longer than the rest but I can't think of a way to shorten them
1520 // ...and I doubt many states are using them.
1521 switch (taglist[i + 1]) {
1522 case 0x19:
1523 PrintAndLogEx(SUCCESS, "Issuing Authority.....: " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
1524 break;
1525 case 0x26:
1526 emrtd_print_issuance((char *) tagdata, tagdatalen != 4);
1527 break;
1528 case 0x1b:
1529 PrintAndLogEx(SUCCESS, "Endorsements & Observations: " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
1530 break;
1531 case 0x1c:
1532 PrintAndLogEx(SUCCESS, "Tax/Exit Requirements.: " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
1533 break;
1534 case 0x1d:
1535 saveFile("FrontOfDocument", tagdata[0] == 0xFF ? ".jpg" : ".jp2", tagdata, tagdatalen);
1536 break;
1537 case 0x1e:
1538 saveFile("BackOfDocument", tagdata[0] == 0xFF ? ".jpg" : ".jp2", tagdata, tagdatalen);
1539 break;
1540 case 0x55:
1541 emrtd_print_personalization_timestamp(tagdata);
1542 break;
1543 case 0x56:
1544 PrintAndLogEx(SUCCESS, "Serial of Personalization System: " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
1545 break;
1546 case 0x85:
1547 emrtd_print_unknown_timestamp_5f85(tagdata);
1548 break;
1549 default:
1550 PrintAndLogEx(SUCCESS, "Unknown Field %02X%02X....: %s", taglist[i], taglist[i + 1], sprint_hex_inrow(tagdata, tagdatalen));
1551 break;
1554 i += 1;
1555 } else {
1556 // TODO: Account for A0
1557 PrintAndLogEx(SUCCESS, "Unknown Field %02X......: %s", taglist[i], sprint_hex_inrow(tagdata, tagdatalen));
1560 return PM3_SUCCESS;
1563 static int emrtd_ef_sod_extract_signatures(uint8_t *data, size_t datalen, uint8_t *dataout, size_t *dataoutlen) {
1564 uint8_t top[EMRTD_MAX_FILE_SIZE] = { 0x00 };
1565 uint8_t signeddata[EMRTD_MAX_FILE_SIZE] = { 0x00 };
1566 uint8_t emrtdsigcontainer[EMRTD_MAX_FILE_SIZE] = { 0x00 };
1567 uint8_t emrtdsig[EMRTD_MAX_FILE_SIZE] = { 0x00 };
1568 uint8_t emrtdsigtext[EMRTD_MAX_FILE_SIZE] = { 0x00 };
1569 size_t toplen, signeddatalen, emrtdsigcontainerlen, emrtdsiglen, emrtdsigtextlen = 0;
1571 if (emrtd_lds_get_data_by_tag(data, datalen, top, &toplen, 0x30, 0x00, false, true, 0) == false) {
1572 PrintAndLogEx(ERR, "Failed to read top from EF_SOD.");
1573 return false;
1576 PrintAndLogEx(DEBUG, "top: %s.", sprint_hex_inrow(top, toplen));
1578 if (emrtd_lds_get_data_by_tag(top, toplen, signeddata, &signeddatalen, 0xA0, 0x00, false, false, 0) == false) {
1579 PrintAndLogEx(ERR, "Failed to read signedData from EF_SOD.");
1580 return false;
1583 PrintAndLogEx(DEBUG, "signeddata: %s.", sprint_hex_inrow(signeddata, signeddatalen));
1585 // Do true on reading into the tag as it's a "sequence"
1586 if (emrtd_lds_get_data_by_tag(signeddata, signeddatalen, emrtdsigcontainer, &emrtdsigcontainerlen, 0x30, 0x00, false, true, 0) == false) {
1587 PrintAndLogEx(ERR, "Failed to read eMRTDSignature container from EF_SOD.");
1588 return false;
1591 PrintAndLogEx(DEBUG, "emrtdsigcontainer: %s.", sprint_hex_inrow(emrtdsigcontainer, emrtdsigcontainerlen));
1593 if (emrtd_lds_get_data_by_tag(emrtdsigcontainer, emrtdsigcontainerlen, emrtdsig, &emrtdsiglen, 0xA0, 0x00, false, false, 0) == false) {
1594 PrintAndLogEx(ERR, "Failed to read eMRTDSignature from EF_SOD.");
1595 return false;
1598 PrintAndLogEx(DEBUG, "emrtdsig: %s.", sprint_hex_inrow(emrtdsig, emrtdsiglen));
1600 // TODO: Not doing memcpy here, it didn't work, fix it somehow
1601 if (emrtd_lds_get_data_by_tag(emrtdsig, emrtdsiglen, emrtdsigtext, &emrtdsigtextlen, 0x04, 0x00, false, false, 0) == false) {
1602 PrintAndLogEx(ERR, "Failed to read eMRTDSignature (text) from EF_SOD.");
1603 return false;
1605 memcpy(dataout, emrtdsigtext, emrtdsigtextlen);
1606 *dataoutlen = emrtdsigtextlen;
1607 return PM3_SUCCESS;
1610 static int emrtd_parse_ef_sod_hash_algo(uint8_t *data, size_t datalen, int *hashalgo) {
1611 uint8_t hashalgoset[64] = { 0x00 };
1612 size_t hashalgosetlen = 0;
1614 // We'll return hash algo -1 if we can't find anything
1615 *hashalgo = -1;
1617 if (emrtd_lds_get_data_by_tag(data, datalen, hashalgoset, &hashalgosetlen, 0x30, 0x00, false, true, 0) == false) {
1618 PrintAndLogEx(ERR, "Failed to read hash algo set from EF_SOD.");
1619 return false;
1622 PrintAndLogEx(DEBUG, "hash algo set: %s", sprint_hex_inrow(hashalgoset, hashalgosetlen));
1624 // If last two bytes are 05 00, ignore them.
1625 // https://wf.lavatech.top/ave-but-random/emrtd-data-quirks#EF_SOD
1626 if (hashalgoset[hashalgosetlen - 2] == 0x05 && hashalgoset[hashalgosetlen - 1] == 0x00) {
1627 hashalgosetlen -= 2;
1630 for (int hashi = 0; hashalg_table[hashi].name != NULL; hashi++) {
1631 PrintAndLogEx(DEBUG, "trying: %s", hashalg_table[hashi].name);
1632 // We're only interested in checking if the length matches to avoid memory shenanigans
1633 if (hashalg_table[hashi].descriptorlen != hashalgosetlen) {
1634 PrintAndLogEx(DEBUG, "len mismatch: %zu", hashalgosetlen);
1635 continue;
1638 if (memcmp(hashalg_table[hashi].descriptor, hashalgoset, hashalgosetlen) == 0) {
1639 *hashalgo = hashi;
1640 return PM3_SUCCESS;
1644 PrintAndLogEx(ERR, "Failed to parse hash list (Unknown algo: %s). Hash verification won't be available.", sprint_hex_inrow(hashalgoset, hashalgosetlen));
1645 return PM3_ESOFT;
1648 static int emrtd_parse_ef_sod_hashes(uint8_t *data, size_t datalen, uint8_t *hashes, int *hashalgo) {
1649 uint8_t emrtdsig[EMRTD_MAX_FILE_SIZE] = { 0x00 };
1650 uint8_t hashlist[EMRTD_MAX_FILE_SIZE] = { 0x00 };
1651 uint8_t hash[64] = { 0x00 };
1652 size_t hashlen = 0;
1654 uint8_t hashidstr[4] = { 0x00 };
1655 size_t hashidstrlen = 0;
1657 size_t emrtdsiglen = 0;
1658 size_t hashlistlen = 0;
1659 size_t offset = 0;
1661 if (emrtd_ef_sod_extract_signatures(data, datalen, emrtdsig, &emrtdsiglen) != PM3_SUCCESS) {
1662 return false;
1665 PrintAndLogEx(DEBUG, "hash data: %s", sprint_hex_inrow(emrtdsig, emrtdsiglen));
1667 emrtd_parse_ef_sod_hash_algo(emrtdsig, emrtdsiglen, hashalgo);
1669 if (emrtd_lds_get_data_by_tag(emrtdsig, emrtdsiglen, hashlist, &hashlistlen, 0x30, 0x00, false, true, 1) == false) {
1670 PrintAndLogEx(ERR, "Failed to read hash list from EF_SOD.");
1671 return false;
1674 PrintAndLogEx(DEBUG, "hash list: %s", sprint_hex_inrow(hashlist, hashlistlen));
1676 while (offset < hashlistlen) {
1677 // Get the length of the element
1678 int e_datalen = emrtd_get_asn1_data_length(hashlist + offset, hashlistlen - offset, 1);
1680 // Get the length of the element's length
1681 int e_fieldlen = emrtd_get_asn1_field_length(hashlist + offset, hashlistlen - offset, 1);
1683 switch (hashlist[offset]) {
1684 case 0x30: {
1685 // iceman: if these two calls fails, feels like we should have a better check in place
1686 bool res = emrtd_lds_get_data_by_tag(hashlist + offset + e_fieldlen + 1, e_datalen, hashidstr, &hashidstrlen, 0x02, 0x00, false, false, 0);
1687 (void)res;
1688 res = emrtd_lds_get_data_by_tag(hashlist + offset + e_fieldlen + 1, e_datalen, hash, &hashlen, 0x04, 0x00, false, false, 0);
1689 (void)res;
1690 if (hashlen <= 64) {
1691 memcpy(hashes + (hashidstr[0] * 64), hash, hashlen);
1692 } else {
1693 PrintAndLogEx(ERR, "error (emrtd_parse_ef_sod_hashes) hashlen out-of-bounds");
1695 break;
1698 // + 1 for length of ID
1699 offset += 1 + e_datalen + e_fieldlen;
1702 return PM3_SUCCESS;
1705 static int emrtd_print_ef_sod_info(uint8_t *dg_hashes_calc, uint8_t *dg_hashes_sod, int hash_algo) {
1706 PrintAndLogEx(NORMAL, "");
1707 PrintAndLogEx(INFO, "-------------------- " _CYAN_("EF_SOD") " --------------------");
1709 if (hash_algo == -1) {
1710 PrintAndLogEx(SUCCESS, "Hash algorithm: " _YELLOW_("Unknown"));
1711 } else {
1712 PrintAndLogEx(SUCCESS, "Hash algorithm: " _YELLOW_("%s"), hashalg_table[hash_algo].name);
1714 uint8_t all_zeroes[64] = { 0x00 };
1715 for (int i = 1; i <= 16; i++) {
1716 bool calc_all_zero = (memcmp(dg_hashes_calc + (i * 64), all_zeroes, hashalg_table[hash_algo].hashlen) == 0);
1717 bool sod_all_zero = (memcmp(dg_hashes_sod + (i * 64), all_zeroes, hashalg_table[hash_algo].hashlen) == 0);
1718 bool hash_matches = (memcmp(dg_hashes_sod + (i * 64), dg_hashes_calc + (i * 64), hashalg_table[hash_algo].hashlen) == 0);
1719 // Ignore files we don't haven't read and lack hashes to
1720 if (calc_all_zero == true && sod_all_zero == true) {
1721 continue;
1722 } else if (calc_all_zero == true) {
1723 PrintAndLogEx(SUCCESS, "EF_DG%i: " _YELLOW_("File couldn't be read, but is in EF_SOD."), i);
1724 } else if (sod_all_zero == true) {
1725 PrintAndLogEx(SUCCESS, "EF_DG%i: " _YELLOW_("File is not in EF_SOD."), i);
1726 } else if (hash_matches == false) {
1727 PrintAndLogEx(SUCCESS, "EF_DG%i: " _RED_("Invalid"), i);
1728 } else {
1729 PrintAndLogEx(SUCCESS, "EF_DG%i: " _GREEN_("Valid"), i);
1734 return PM3_SUCCESS;
1737 static int emrtd_print_ef_cardaccess_info(uint8_t *data, size_t datalen) {
1738 uint8_t dataset[100] = { 0x00 };
1739 size_t datasetlen = 0;
1740 uint8_t datafromtag[100] = { 0x00 };
1741 size_t datafromtaglen = 0;
1742 uint8_t parsednum = 0;
1744 PrintAndLogEx(NORMAL, "");
1745 PrintAndLogEx(INFO, "----------------- " _CYAN_("EF_CardAccess") " ----------------");
1747 if (emrtd_lds_get_data_by_tag(data, datalen, dataset, &datasetlen, 0x30, 0x00, false, true, 0) == false) {
1748 PrintAndLogEx(ERR, "Failed to read set from EF_CardAccess.");
1749 return PM3_ESOFT;
1752 // Get PACE version
1753 if (emrtd_lds_get_data_by_tag(dataset, datasetlen, datafromtag, &datafromtaglen, 0x02, 0x00, false, false, 0) == false) {
1754 PrintAndLogEx(ERR, "Failed to read PACE version from EF_CardAccess.");
1755 return PM3_ESOFT;
1757 // TODO: hack!!!
1758 memcpy(&parsednum, datafromtag, datafromtaglen);
1759 PrintAndLogEx(SUCCESS, "PACE version..........: " _YELLOW_("%i"), parsednum);
1761 // Get PACE algorithm
1762 if (emrtd_lds_get_data_by_tag(dataset, datasetlen, datafromtag, &datafromtaglen, 0x06, 0x00, false, false, 0) == false) {
1763 PrintAndLogEx(ERR, "Failed to read PACE algorithm from EF_CardAccess.");
1764 return PM3_ESOFT;
1767 for (int pacei = 0; pacealg_table[pacei].name != NULL; pacei++) {
1768 PrintAndLogEx(DEBUG, "Trying: %s", pacealg_table[pacei].name);
1770 if (memcmp(pacealg_table[pacei].descriptor, datafromtag, datafromtaglen) == 0) {
1771 PrintAndLogEx(SUCCESS, "PACE algorithm........: " _YELLOW_("%s"), pacealg_table[pacei].name);
1775 // Get PACE parameter ID
1776 if (emrtd_lds_get_data_by_tag(dataset, datasetlen, datafromtag, &datafromtaglen, 0x02, 0x00, false, false, 1) == false) {
1777 PrintAndLogEx(ERR, "Failed to read PACE parameter ID from EF_CardAccess.");
1778 return PM3_ESOFT;
1781 // TODO: hack!!!
1782 memcpy(&parsednum, datafromtag, datafromtaglen);
1783 for (int pacepari = 0; pacesdp_table[pacepari].id != 32; pacepari++) {
1784 PrintAndLogEx(DEBUG, "Trying: %s", pacesdp_table[pacepari].name);
1786 if (pacesdp_table[pacepari].id == parsednum) {
1787 PrintAndLogEx(SUCCESS, "PACE parameter........: " _YELLOW_("%s"), pacesdp_table[pacepari].name);
1789 // TODO: account for RFU
1792 return PM3_SUCCESS;
1795 int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available) {
1796 uint8_t response[EMRTD_MAX_FILE_SIZE] = { 0x00 };
1797 size_t resplen = 0;
1798 uint8_t ssc[8] = { 0x00 };
1799 uint8_t ks_enc[16] = { 0x00 };
1800 uint8_t ks_mac[16] = { 0x00 };
1801 bool BAC = false;
1802 bool PACE_available = true;
1804 // Select the eMRTD
1805 if (emrtd_connect() == false) {
1806 DropField();
1807 return PM3_ESOFT;
1809 bool use14b = GetISODEPState() == ISODEP_NFCB;
1811 // Read EF_CardAccess
1812 if (!emrtd_select_and_read(response, &resplen, dg_table[EF_CardAccess].fileid, ks_enc, ks_mac, ssc, BAC)) {
1813 PACE_available = false;
1814 PrintAndLogEx(HINT, "The error above this is normal. It just means that your eMRTD lacks PACE.");
1817 // Select and authenticate with the eMRTD
1818 bool auth_result = emrtd_do_auth(documentnumber, dob, expiry, BAC_available, &BAC, ssc, ks_enc, ks_mac);
1820 PrintAndLogEx(NORMAL, "");
1821 PrintAndLogEx(INFO, "------------------ " _CYAN_("Basic Info") " ------------------");
1822 PrintAndLogEx(SUCCESS, "Communication standard: %s", use14b ? _YELLOW_("ISO/IEC 14443(B)") : _YELLOW_("ISO/IEC 14443(A)"));
1823 PrintAndLogEx(SUCCESS, "Authentication........: %s", BAC ? _GREEN_("Enforced") : _RED_("Not enforced"));
1824 PrintAndLogEx(SUCCESS, "PACE..................: %s", PACE_available ? _GREEN_("Available") : _YELLOW_("Not available"));
1825 PrintAndLogEx(SUCCESS, "Authentication result.: %s", auth_result ? _GREEN_("Successful") : _RED_("Failed"));
1827 if (PACE_available) {
1828 emrtd_print_ef_cardaccess_info(response, resplen);
1831 if (!auth_result) {
1832 DropField();
1833 return PM3_ESOFT;
1836 // Read EF_COM to get file list
1837 if (!emrtd_select_and_read(response, &resplen, dg_table[EF_COM].fileid, ks_enc, ks_mac, ssc, BAC)) {
1838 PrintAndLogEx(ERR, "Failed to read EF_COM.");
1839 DropField();
1840 return PM3_ESOFT;
1843 int res = emrtd_print_ef_com_info(response, resplen);
1844 if (res != PM3_SUCCESS) {
1845 DropField();
1846 return res;
1849 uint8_t filelist[50];
1850 size_t filelistlen = 0;
1852 if (emrtd_lds_get_data_by_tag(response, resplen, filelist, &filelistlen, 0x5c, 0x00, false, true, 0) == false) {
1853 PrintAndLogEx(ERR, "Failed to read file list from EF_COM.");
1854 DropField();
1855 return PM3_ESOFT;
1858 // Grab the hash list from EF_SOD
1859 uint8_t dg_hashes_sod[17][64] = { { 0 } };
1860 uint8_t dg_hashes_calc[17][64] = { { 0 } };
1861 int hash_algo = 0;
1863 if (!emrtd_select_and_read(response, &resplen, dg_table[EF_SOD].fileid, ks_enc, ks_mac, ssc, BAC)) {
1864 PrintAndLogEx(ERR, "Failed to read EF_SOD.");
1865 DropField();
1866 return PM3_ESOFT;
1869 res = emrtd_parse_ef_sod_hashes(response, resplen, *dg_hashes_sod, &hash_algo);
1870 if (res != PM3_SUCCESS) {
1871 PrintAndLogEx(ERR, "Failed to read hash list from EF_SOD. Hash checks will fail.");
1874 // Dump all files in the file list
1875 for (int i = 0; i < filelistlen; i++) {
1876 emrtd_dg_t *dg = emrtd_tag_to_dg(filelist[i]);
1877 if (dg == NULL) {
1878 PrintAndLogEx(INFO, "File tag not found, skipping: %02X", filelist[i]);
1879 continue;
1881 if (dg->fastdump && !dg->pace && !dg->eac) {
1882 if (emrtd_select_and_read(response, &resplen, dg->fileid, ks_enc, ks_mac, ssc, BAC)) {
1883 if (dg->parser != NULL)
1884 dg->parser(response, resplen);
1886 PrintAndLogEx(DEBUG, "EF_DG%i hash algo: %i", dg->dgnum, hash_algo);
1887 // Check file hash
1888 if (hash_algo != -1) {
1889 PrintAndLogEx(DEBUG, "EF_DG%i hash on EF_SOD: %s", dg->dgnum, sprint_hex_inrow(dg_hashes_sod[dg->dgnum], hashalg_table[hash_algo].hashlen));
1890 hashalg_table[hash_algo].hasher(response, resplen, dg_hashes_calc[dg->dgnum]);
1891 PrintAndLogEx(DEBUG, "EF_DG%i hash calc: %s", dg->dgnum, sprint_hex_inrow(dg_hashes_calc[dg->dgnum], hashalg_table[hash_algo].hashlen));
1896 DropField();
1898 emrtd_print_ef_sod_info(*dg_hashes_calc, *dg_hashes_sod, hash_algo);
1900 return PM3_SUCCESS;
1903 int infoHF_EMRTD_offline(const char *path) {
1904 uint8_t *data;
1905 size_t datalen = 0;
1906 char *filepath = calloc(strlen(path) + 100, sizeof(char));
1907 if (filepath == NULL)
1908 return PM3_EMALLOC;
1909 strcpy(filepath, path);
1910 strncat(filepath, PATHSEP, 2);
1911 strcat(filepath, dg_table[EF_COM].filename);
1913 if (loadFile_safeEx(filepath, ".BIN", (void **)&data, (size_t *)&datalen, false) != PM3_SUCCESS) {
1914 PrintAndLogEx(ERR, "Failed to read EF_COM.");
1915 free(filepath);
1916 return PM3_ESOFT;
1919 int res = emrtd_print_ef_com_info(data, datalen);
1920 if (res != PM3_SUCCESS) {
1921 free(data);
1922 free(filepath);
1923 return res;
1926 uint8_t filelist[50];
1927 size_t filelistlen = 0;
1928 res = emrtd_lds_get_data_by_tag(data, datalen, filelist, &filelistlen, 0x5c, 0x00, false, true, 0);
1929 if (res == false) {
1930 PrintAndLogEx(ERR, "Failed to read file list from EF_COM.");
1931 free(data);
1932 free(filepath);
1933 return PM3_ESOFT;
1935 free(data);
1937 // Grab the hash list
1938 uint8_t dg_hashes_sod[17][64] = { { 0 } };
1939 uint8_t dg_hashes_calc[17][64] = { { 0 } };
1940 int hash_algo = 0;
1942 strcpy(filepath, path);
1943 strncat(filepath, PATHSEP, 2);
1944 strcat(filepath, dg_table[EF_CardAccess].filename);
1946 if (loadFile_safeEx(filepath, ".BIN", (void **)&data, (size_t *)&datalen, false) == PM3_SUCCESS) {
1947 emrtd_print_ef_cardaccess_info(data, datalen);
1948 } else {
1949 PrintAndLogEx(HINT, "The error above this is normal. It just means that your eMRTD lacks PACE.");
1952 strcpy(filepath, path);
1953 strncat(filepath, PATHSEP, 2);
1954 strcat(filepath, dg_table[EF_SOD].filename);
1956 if (loadFile_safeEx(filepath, ".BIN", (void **)&data, (size_t *)&datalen, false) != PM3_SUCCESS) {
1957 PrintAndLogEx(ERR, "Failed to read EF_SOD.");
1958 free(filepath);
1959 return PM3_ESOFT;
1962 res = emrtd_parse_ef_sod_hashes(data, datalen, *dg_hashes_sod, &hash_algo);
1963 if (res != PM3_SUCCESS) {
1964 PrintAndLogEx(ERR, "Failed to read hash list from EF_SOD. Hash checks will fail.");
1966 free(data);
1968 // Read files in the file list
1969 for (int i = 0; i < filelistlen; i++) {
1970 emrtd_dg_t *dg = emrtd_tag_to_dg(filelist[i]);
1971 if (dg == NULL) {
1972 PrintAndLogEx(INFO, "File tag not found, skipping: %02X", filelist[i]);
1973 continue;
1975 if (!dg->pace && !dg->eac) {
1976 strcpy(filepath, path);
1977 strncat(filepath, PATHSEP, 2);
1978 strcat(filepath, dg->filename);
1979 if (loadFile_safeEx(filepath, ".BIN", (void **)&data, (size_t *)&datalen, false) == PM3_SUCCESS) {
1980 // we won't halt on parsing errors
1981 if (dg->parser != NULL)
1982 dg->parser(data, datalen);
1984 PrintAndLogEx(DEBUG, "EF_DG%i hash algo: %i", dg->dgnum, hash_algo);
1985 // Check file hash
1986 if (hash_algo != -1) {
1987 PrintAndLogEx(DEBUG, "EF_DG%i hash on EF_SOD: %s", dg->dgnum, sprint_hex_inrow(dg_hashes_sod[dg->dgnum], hashalg_table[hash_algo].hashlen));
1988 hashalg_table[hash_algo].hasher(data, datalen, dg_hashes_calc[dg->dgnum]);
1989 PrintAndLogEx(DEBUG, "EF_DG%i hash calc: %s", dg->dgnum, sprint_hex_inrow(dg_hashes_calc[dg->dgnum], hashalg_table[hash_algo].hashlen));
1991 free(data);
1995 free(filepath);
1997 emrtd_print_ef_sod_info(*dg_hashes_calc, *dg_hashes_sod, hash_algo);
1999 return PM3_SUCCESS;
2002 static void text_to_upper(uint8_t *data, int datalen) {
2003 // Loop over text to make lowercase text uppercase
2004 for (int i = 0; i < datalen; i++) {
2005 data[i] = toupper(data[i]);
2009 static bool validate_date(uint8_t *data, int datalen) {
2010 // Date has to be 6 chars
2011 if (datalen != 6) {
2012 return false;
2015 // Check for valid date and month numbers
2016 char temp[4] = { 0x00 };
2017 memcpy(temp, data + 2, 2);
2018 int month = (int) strtol(temp, NULL, 10);
2019 memcpy(temp, data + 4, 2);
2020 int day = (int) strtol(temp, NULL, 10);
2022 return !(day <= 0 || day > 31 || month <= 0 || month > 12);
2025 static int CmdHFeMRTDDump(const char *Cmd) {
2026 CLIParserContext *ctx;
2027 CLIParserInit(&ctx, "hf emrtd dump",
2028 "Dump all files on an eMRTD",
2029 "hf emrtd dump"
2032 void *argtable[] = {
2033 arg_param_begin,
2034 arg_str0("n", "documentnumber", "<alphanum>", "document number, up to 9 chars"),
2035 arg_str0("d", "dateofbirth", "<YYMMDD>", "date of birth in YYMMDD format"),
2036 arg_str0("e", "expiry", "<YYMMDD>", "expiry in YYMMDD format"),
2037 arg_str0("m", "mrz", "<[0-9A-Z<]>", "2nd line of MRZ, 44 chars"),
2038 arg_str0(NULL, "path", "<dirpath>", "save dump to the given dirpath"),
2039 arg_param_end
2041 CLIExecWithReturn(ctx, Cmd, argtable, true);
2043 uint8_t mrz[45] = { 0x00 };
2044 uint8_t docnum[10] = { 0x00 };
2045 uint8_t dob[7] = { 0x00 };
2046 uint8_t expiry[7] = { 0x00 };
2047 bool BAC = true;
2048 bool error = false;
2049 int slen = 0;
2050 // Go through all args, if even one isn't supplied, mark BAC as unavailable
2051 if (CLIParamStrToBuf(arg_get_str(ctx, 1), docnum, 9, &slen) != 0 || slen == 0) {
2052 BAC = false;
2053 } else {
2054 text_to_upper(docnum, slen);
2055 if (slen != 9) {
2056 // Pad to 9 with <
2057 memset(docnum + slen, '<', 9 - slen);
2061 if (CLIParamStrToBuf(arg_get_str(ctx, 2), dob, 6, &slen) != 0 || slen == 0) {
2062 BAC = false;
2063 } else {
2064 if (!validate_date(dob, slen)) {
2065 PrintAndLogEx(ERR, "Date of birth date format is incorrect, cannot continue.");
2066 PrintAndLogEx(HINT, "Use the format YYMMDD.");
2067 error = true;
2071 if (CLIParamStrToBuf(arg_get_str(ctx, 3), expiry, 6, &slen) != 0 || slen == 0) {
2072 BAC = false;
2073 } else {
2074 if (!validate_date(expiry, slen)) {
2075 PrintAndLogEx(ERR, "Expiry date format is incorrect, cannot continue.");
2076 PrintAndLogEx(HINT, "Use the format YYMMDD.");
2077 error = true;
2081 if (CLIParamStrToBuf(arg_get_str(ctx, 4), mrz, 44, &slen) == 0 && slen != 0) {
2082 if (slen != 44) {
2083 PrintAndLogEx(ERR, "MRZ length is incorrect, it should be 44, not %i", slen);
2084 error = true;
2085 } else {
2086 BAC = true;
2087 text_to_upper(mrz, slen);
2088 memcpy(docnum, &mrz[0], 9);
2089 memcpy(dob, &mrz[13], 6);
2090 memcpy(expiry, &mrz[21], 6);
2091 // TODO check MRZ checksums?
2092 if (!validate_date(dob, 6)) {
2093 PrintAndLogEx(ERR, "Date of birth date format is incorrect, cannot continue.");
2094 PrintAndLogEx(HINT, "Use the format YYMMDD.");
2095 error = true;
2097 if (!validate_date(expiry, 6)) {
2098 PrintAndLogEx(ERR, "Expiry date format is incorrect, cannot continue.");
2099 PrintAndLogEx(HINT, "Use the format YYMMDD.");
2100 error = true;
2105 uint8_t path[FILENAME_MAX] = { 0x00 };
2106 if (CLIParamStrToBuf(arg_get_str(ctx, 5), path, sizeof(path), &slen) != 0 || slen == 0) {
2107 path[0] = '.';
2110 CLIParserFree(ctx);
2111 if (error) {
2112 return PM3_ESOFT;
2114 bool restore_apdu_logging = GetAPDULogging();
2115 if (g_debugMode >= 2) {
2116 SetAPDULogging(true);
2118 int res = dumpHF_EMRTD((char *)docnum, (char *)dob, (char *)expiry, BAC, (const char *)path);
2119 SetAPDULogging(restore_apdu_logging);
2120 return res;
2123 static int CmdHFeMRTDInfo(const char *Cmd) {
2124 CLIParserContext *ctx;
2125 CLIParserInit(&ctx, "hf emrtd info",
2126 "Display info about an eMRTD",
2127 "hf emrtd info"
2130 void *argtable[] = {
2131 arg_param_begin,
2132 arg_str0("n", "documentnumber", "<alphanum>", "document number, up to 9 chars"),
2133 arg_str0("d", "dateofbirth", "<YYMMDD>", "date of birth in YYMMDD format"),
2134 arg_str0("e", "expiry", "<YYMMDD>", "expiry in YYMMDD format"),
2135 arg_str0("m", "mrz", "<[0-9A-Z<]>", "2nd line of MRZ, 44 chars (passports only)"),
2136 arg_str0(NULL, "path", "<dirpath>", "display info from offline dump stored in dirpath"),
2137 arg_param_end
2139 CLIExecWithReturn(ctx, Cmd, argtable, true);
2141 uint8_t mrz[45] = { 0x00 };
2142 uint8_t docnum[10] = { 0x00 };
2143 uint8_t dob[7] = { 0x00 };
2144 uint8_t expiry[7] = { 0x00 };
2145 bool BAC = true;
2146 bool error = false;
2147 int slen = 0;
2148 // Go through all args, if even one isn't supplied, mark BAC as unavailable
2149 if (CLIParamStrToBuf(arg_get_str(ctx, 1), docnum, 9, &slen) != 0 || slen == 0) {
2150 BAC = false;
2151 } else {
2152 text_to_upper(docnum, slen);
2153 if (slen != 9) {
2154 memset(docnum + slen, '<', 9 - slen);
2158 if (CLIParamStrToBuf(arg_get_str(ctx, 2), dob, 6, &slen) != 0 || slen == 0) {
2159 BAC = false;
2160 } else {
2161 if (!validate_date(dob, slen)) {
2162 PrintAndLogEx(ERR, "Date of birth date format is incorrect, cannot continue.");
2163 PrintAndLogEx(HINT, "Use the format YYMMDD.");
2164 error = true;
2168 if (CLIParamStrToBuf(arg_get_str(ctx, 3), expiry, 6, &slen) != 0 || slen == 0) {
2169 BAC = false;
2170 } else {
2171 if (!validate_date(expiry, slen)) {
2172 PrintAndLogEx(ERR, "Expiry date format is incorrect, cannot continue.");
2173 PrintAndLogEx(HINT, "Use the format YYMMDD.");
2174 error = true;
2178 if (CLIParamStrToBuf(arg_get_str(ctx, 4), mrz, 44, &slen) == 0 && slen != 0) {
2179 if (slen != 44) {
2180 PrintAndLogEx(ERR, "MRZ length is incorrect, it should be 44, not %i", slen);
2181 error = true;
2182 } else {
2183 BAC = true;
2184 text_to_upper(mrz, slen);
2185 memcpy(docnum, &mrz[0], 9);
2186 memcpy(dob, &mrz[13], 6);
2187 memcpy(expiry, &mrz[21], 6);
2188 // TODO check MRZ checksums?
2189 if (!validate_date(dob, 6)) {
2190 PrintAndLogEx(ERR, "Date of birth date format is incorrect, cannot continue.");
2191 PrintAndLogEx(HINT, "Use the format YYMMDD.");
2192 error = true;
2194 if (!validate_date(expiry, 6)) {
2195 PrintAndLogEx(ERR, "Expiry date format is incorrect, cannot continue.");
2196 PrintAndLogEx(HINT, "Use the format YYMMDD.");
2197 error = true;
2201 uint8_t path[FILENAME_MAX] = { 0x00 };
2202 bool offline = CLIParamStrToBuf(arg_get_str(ctx, 5), path, sizeof(path), &slen) == 0 && slen > 0;
2203 CLIParserFree(ctx);
2204 if (error) {
2205 return PM3_ESOFT;
2207 if (offline) {
2208 return infoHF_EMRTD_offline((const char *)path);
2209 } else {
2210 bool restore_apdu_logging = GetAPDULogging();
2211 if (g_debugMode >= 2) {
2212 SetAPDULogging(true);
2214 int res = infoHF_EMRTD((char *)docnum, (char *)dob, (char *)expiry, BAC);
2215 SetAPDULogging(restore_apdu_logging);
2216 return res;
2220 static int CmdHFeMRTDList(const char *Cmd) {
2221 return CmdTraceListAlias(Cmd, "hf emrtd", "7816");
2224 static command_t CommandTable[] = {
2225 {"help", CmdHelp, AlwaysAvailable, "This help"},
2226 {"dump", CmdHFeMRTDDump, IfPm3Iso14443, "Dump eMRTD files to binary files"},
2227 {"info", CmdHFeMRTDInfo, AlwaysAvailable, "Display info about an eMRTD"},
2228 {"list", CmdHFeMRTDList, AlwaysAvailable, "List ISO 14443A/7816 history"},
2229 {NULL, NULL, NULL, NULL}
2232 static int CmdHelp(const char *Cmd) {
2233 (void)Cmd; // Cmd is not used so far
2234 CmdsHelp(CommandTable);
2235 return PM3_SUCCESS;
2238 int CmdHFeMRTD(const char *Cmd) {
2239 clearCommandBuffer();
2240 return CmdsParse(CommandTable, Cmd);