style
[RRG-proxmark3.git] / client / src / cmdhfemrtd.c
blobbe11aea2b5ac8cc613eedbbfdecfbdc665104c57
1 //-----------------------------------------------------------------------------
2 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // See LICENSE.txt for the text of the license.
15 //-----------------------------------------------------------------------------
16 // High frequency Electronic Machine Readable Travel Document commands
17 //-----------------------------------------------------------------------------
19 // This code is heavily based on mrpkey.py of RFIDIOt
21 #include "cmdhfemrtd.h"
22 #include <ctype.h>
23 #include "fileutils.h" // saveFile
24 #include "cmdparser.h" // command_t
25 #include "cmdtrace.h" // CmdTraceList
26 #include "cliparser.h" // CLIParserContext etc
27 #include "protocols.h" // definitions of ISO14A/7816 protocol
28 #include "iso7816/apduinfo.h" // GetAPDUCodeDescription
29 #include "iso7816/iso7816core.h" // Iso7816ExchangeEx etc
30 #include "crypto/libpcrypto.h" // Hash calculation (sha1, sha256, sha512), des_encrypt/des_decrypt
31 #include "des.h" // mbedtls_des_key_set_parity
32 #include "crapto1/crapto1.h" // prng_successor
33 #include "commonutil.h" // num_to_bytes
34 #include "util_posix.h" // msclock
35 #include "ui.h" // search home directory
36 #include "proxgui.h" // Picture Window
38 // Max file size in bytes. Used in several places.
39 // Average EF_DG2 seems to be around 20-25kB or so, but ICAO doesn't set an upper limit
40 // Iris data seems to be suggested to be around 35kB per eye (Presumably bumping up the file size to around 70kB)
41 // but as we cannot read that until we implement PACE, 35k seems to be a safe point.
42 #define EMRTD_MAX_FILE_SIZE 35000
44 // ISO7816 commands
45 #define EMRTD_P1_SELECT_BY_EF 0x02
46 #define EMRTD_P1_SELECT_BY_NAME 0x04
47 #define EMRTD_P2_PROPRIETARY 0x0C
49 // App IDs
50 #define EMRTD_AID_MRTD {0xA0, 0x00, 0x00, 0x02, 0x47, 0x10, 0x01}
52 #define EMRTD_KMAC_LEN 16
54 // DESKey Types
55 static const uint8_t KENC_type[4] = {0x00, 0x00, 0x00, 0x01};
56 static const uint8_t KMAC_type[4] = {0x00, 0x00, 0x00, 0x02};
59 * BAC = Basic Access Control
60 * PA = Passive Authentication
61 * AA = Active Authentication
62 * EAC = Extended Access Control
63 * SAC = Suppliment Access Control
65 File structures
66 ----------------
67 Mastefile MF
68 -- EF.ATR/INFO (01)
69 -- EF.DIR (1E)
70 -- EF.CardSecurity (1D)
71 -- EF.CardAccess (1C)
72 Data Files DF
73 -- eMRTD Application DF (AID: )
74 -- Travel Records Application DF (AID: A0 00 00 02 47 20 01)
75 - EF.Certificates (1A)
76 - EF.EntryRecords (01)
77 - EF.ExitRecords (02)
78 -- Visa Records Application DF (AID: A0 00 00 02 47 20 02)
79 - EF.Certificates (1A)
80 - EF.ExitRecords (03)
81 -- Additional Biometrics Application DF (AID: ‘A0 00 00 02 47 20 03)
82 - EF.Certificates (011A)
83 - EF.Biometrics1 (0201)
84 - EF.Biometrics2 (0202)
85 - EF.Biometrics64 (0240)
87 eMRTD Application DF
88 -----------------------
89 File names and what they contain
90 EG.COM = Common data
91 EG.DG1 = MRZ data
92 EG.DG2 = Biometric template, Photo
93 EG.DG3 = Biometric template, Fingerprint (EAC / AA)
94 EG.DG4 = Biometric template, Iris (EAC / AA)
95 EG.DG5 = Image template
96 EG.DG6 = Image template
97 EG.DG7 = Image template (Signature?)
98 EG.DG8 = Data Feature
99 EG.DG9 = Structure Feature
100 EG.DG10 = Substance Feature
101 EG.DG11 = Additional personal details
102 EG.DG12 = Additional Document Detail
103 EG.DG13 = Optional Details
104 EG.DG14 = Security Options
105 EG.DG15 = AA public key
106 EG.DG16 = Persons to notify
107 EG.SOD = Security, signatures of all data files
111 const char *pad = ".....................................";
113 static int emrtd_dump_ef_dg2(uint8_t *file_contents, size_t file_length, const char *path);
114 static int emrtd_dump_ef_dg5(uint8_t *file_contents, size_t file_length, const char *path);
115 static int emrtd_dump_ef_dg7(uint8_t *file_contents, size_t file_length, const char *path);
116 static int emrtd_dump_ef_sod(uint8_t *file_contents, size_t file_length, const char *path);
117 static int emrtd_print_ef_com_info(uint8_t *data, size_t datalen);
118 static int emrtd_print_ef_dg1_info(uint8_t *data, size_t datalen);
119 static int emrtd_print_ef_dg2_info(uint8_t *data, size_t datalen);
120 static int emrtd_print_ef_dg5_info(uint8_t *data, size_t datalen);
121 static int emrtd_print_ef_dg7_info(uint8_t *data, size_t datalen);
122 static int emrtd_print_ef_dg11_info(uint8_t *data, size_t datalen);
123 static int emrtd_print_ef_dg12_info(uint8_t *data, size_t datalen);
124 static int emrtd_print_ef_cardaccess_info(uint8_t *data, size_t datalen);
126 typedef enum { // list must match dg_table
127 EF_COM = 0,
128 EF_DG1,
129 EF_DG2,
130 EF_DG3,
131 EF_DG4,
132 EF_DG5,
133 EF_DG6,
134 EF_DG7,
135 EF_DG8,
136 EF_DG9,
137 EF_DG10,
138 EF_DG11,
139 EF_DG12,
140 EF_DG13,
141 EF_DG14,
142 EF_DG15,
143 EF_DG16,
144 EF_SOD,
145 EF_CardAccess,
146 EF_CardSecurity,
147 } emrtd_dg_enum;
149 static emrtd_dg_t dg_table[] = {
150 // tag dg# fileid filename desc pace eac req fast parser dumper
151 {0x60, 0, 0x011E, "EF_COM", "Header and Data Group Presence Information", false, false, true, true, emrtd_print_ef_com_info, NULL},
152 {0x61, 1, 0x0101, "EF_DG1", "Details recorded in MRZ", false, false, true, true, emrtd_print_ef_dg1_info, NULL},
153 {0x75, 2, 0x0102, "EF_DG2", "Encoded Face", false, false, true, false, emrtd_print_ef_dg2_info, emrtd_dump_ef_dg2},
154 {0x63, 3, 0x0103, "EF_DG3", "Encoded Finger(s)", false, true, false, false, NULL, NULL},
155 {0x76, 4, 0x0104, "EF_DG4", "Encoded Eye(s)", false, true, false, false, NULL, NULL},
156 {0x65, 5, 0x0105, "EF_DG5", "Displayed Portrait", false, false, false, false, emrtd_print_ef_dg5_info, emrtd_dump_ef_dg5},
157 {0x66, 6, 0x0106, "EF_DG6", "Reserved for Future Use", false, false, false, false, NULL, NULL},
158 {0x67, 7, 0x0107, "EF_DG7", "Displayed Signature or Usual Mark", false, false, false, false, emrtd_print_ef_dg7_info, emrtd_dump_ef_dg7},
159 {0x68, 8, 0x0108, "EF_DG8", "Data Feature(s)", false, false, false, true, NULL, NULL},
160 {0x69, 9, 0x0109, "EF_DG9", "Structure Feature(s)", false, false, false, true, NULL, NULL},
161 {0x6a, 10, 0x010A, "EF_DG10", "Substance Feature(s)", false, false, false, true, NULL, NULL},
162 {0x6b, 11, 0x010B, "EF_DG11", "Additional Personal Detail(s)", false, false, false, true, emrtd_print_ef_dg11_info, NULL},
163 {0x6c, 12, 0x010C, "EF_DG12", "Additional Document Detail(s)", false, false, false, true, emrtd_print_ef_dg12_info, NULL},
164 {0x6d, 13, 0x010D, "EF_DG13", "Optional Detail(s)", false, false, false, true, NULL, NULL},
165 {0x6e, 14, 0x010E, "EF_DG14", "Security Options", false, false, false, true, NULL, NULL},
166 {0x6f, 15, 0x010F, "EF_DG15", "Active Authentication Public Key Info", false, false, false, true, NULL, NULL},
167 {0x70, 16, 0x0110, "EF_DG16", "Person(s) to Notify", false, false, false, true, NULL, NULL},
168 {0x77, 0, 0x011D, "EF_SOD", "Document Security Object", false, false, false, false, NULL, emrtd_dump_ef_sod},
169 {0xff, 0, 0x011C, "EF_CardAccess", "PACE SecurityInfos", true, false, true, true, emrtd_print_ef_cardaccess_info, NULL},
170 {0xff, 0, 0x011D, "EF_CardSecurity", "PACE SecurityInfos for Chip Authentication Mapping", true, false, false, true, NULL, NULL},
171 {0x00, 0, 0, NULL, NULL, false, false, false, false, NULL, NULL}
174 // https://security.stackexchange.com/questions/131241/where-do-magic-constants-for-signature-algorithms-come-from
175 // https://tools.ietf.org/html/rfc3447#page-43
176 static emrtd_hashalg_t hashalg_table[] = {
177 // name hash func len len descriptor
178 {"SHA-1", sha1hash, 20, 7, {0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A}},
179 {"SHA-256", sha256hash, 32, 11, {0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01}},
180 {"SHA-512", sha512hash, 64, 11, {0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03}},
181 {NULL, NULL, 0, 0, {0}}
184 static emrtd_pacealg_t pacealg_table[] = {
185 // name keygen descriptor
186 {"DH, Generic Mapping, 3DES-CBC-CBC", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x01, 0x01}},
187 {"DH, Generic Mapping, AES-CMAC-128", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x01, 0x02}},
188 {"DH, Generic Mapping, AES-CMAC-192", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x01, 0x03}},
189 {"DH, Generic Mapping, AES-CMAC-256", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x01, 0x04}},
190 {"ECDH, Generic Mapping, 3DES-CBC-CBC", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x02, 0x01}},
191 {"ECDH, Generic Mapping, AES-CMAC-128", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x02, 0x02}},
192 {"ECDH, Generic Mapping, AES-CMAC-192", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x02, 0x03}},
193 {"ECDH, Generic Mapping, AES-CMAC-256", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x02, 0x04}},
194 {"DH, Integrated Mapping, 3DES-CBC-CBC", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x03, 0x01}},
195 {"DH, Integrated Mapping, AES-CMAC-128", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x03, 0x02}},
196 {"DH, Integrated Mapping, AES-CMAC-192", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x03, 0x03}},
197 {"DH, Integrated Mapping, AES-CMAC-256", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x03, 0x04}},
198 {"ECDH, Integrated Mapping, 3DES-CBC-CBC", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x04, 0x01}},
199 {"ECDH, Integrated Mapping, AES-CMAC-128", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x04, 0x02}},
200 {"ECDH, Integrated Mapping, AES-CMAC-192", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x04, 0x03}},
201 {"ECDH, Integrated Mapping, AES-CMAC-256", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x04, 0x04}},
202 {NULL, NULL, {0}}
205 static emrtd_pacesdp_t pacesdp_table[] = {
206 // id name size
207 {0, "1024-bit MODP Group with 160-bit Prime Order Subgroup", 1024},
208 {1, "2048-bit MODP Group with 224-bit Prime Order Subgroup", 2048},
209 {2, "2048-bit MODP Group with 256-bit Prime Order Subgroup", 2048},
210 {8, "NIST P-192 (secp192r1)", 192},
211 {10, "NIST P-224 (secp224r1)", 224},
212 {12, "NIST P-256 (secp256r1)", 256},
213 {15, "NIST P-384 (secp384r1)", 384},
214 {18, "NIST P-521 (secp521r1)", 521},
215 {9, "BrainpoolP192r1", 192},
216 {11, "BrainpoolP224r1", 224},
217 {13, "BrainpoolP256r1", 256},
218 {14, "BrainpoolP320r1", 320},
219 {16, "BrainpoolP384r1", 384},
220 {17, "BrainpoolP521r1", 521},
221 {32, NULL, 0}
224 static emrtd_dg_t *emrtd_tag_to_dg(uint8_t tag) {
225 for (int dgi = 0; dg_table[dgi].filename != NULL; dgi++) {
226 if (dg_table[dgi].tag == tag) {
227 return &dg_table[dgi];
230 return NULL;
232 static emrtd_dg_t *emrtd_fileid_to_dg(uint16_t file_id) {
233 for (int dgi = 0; dg_table[dgi].filename != NULL; dgi++) {
234 if (dg_table[dgi].fileid == file_id) {
235 return &dg_table[dgi];
238 return NULL;
241 static int CmdHelp(const char *Cmd);
243 static bool emrtd_exchange_commands(sAPDU_t apdu, bool include_le, uint16_t le, uint8_t *dataout, size_t maxdataoutlen, size_t *dataoutlen, bool activate_field, bool keep_field_on) {
244 uint16_t sw;
245 int res = Iso7816ExchangeEx(CC_CONTACTLESS, activate_field, keep_field_on, apdu, include_le, le, dataout, maxdataoutlen, dataoutlen, &sw);
247 if (res != PM3_SUCCESS) {
248 return false;
251 if (sw != ISO7816_OK) {
252 PrintAndLogEx(DEBUG, "Command failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
253 return false;
255 return true;
258 static int emrtd_exchange_commands_noout(sAPDU_t apdu, bool activate_field, bool keep_field_on) {
259 uint8_t response[PM3_CMD_DATA_SIZE] = {0};
260 size_t resplen = 0;
261 return emrtd_exchange_commands(apdu, false, 0, response, 0, &resplen, activate_field, keep_field_on);
264 static char emrtd_calculate_check_digit(char *data) {
265 const int mrz_weight[] = {7, 3, 1};
266 int value, cd = 0;
268 for (int i = 0; i < strlen(data); i++) {
269 char d = data[i];
270 if ('A' <= d && d <= 'Z') {
271 value = d - 55;
272 } else if ('a' <= d && d <= 'z') {
273 value = d - 87;
274 } else if (d == '<') {
275 value = 0;
276 } else { // Numbers
277 value = d - 48;
279 cd += value * mrz_weight[i % 3];
281 return cd % 10;
284 static int emrtd_get_asn1_data_length(uint8_t *datain, int datainlen, int offset) {
285 PrintAndLogEx(DEBUG, "asn1 datalength, datain: %s", sprint_hex_inrow(datain, datainlen));
286 int lenfield = (int) * (datain + offset);
287 PrintAndLogEx(DEBUG, "asn1 datalength, lenfield: %02X", lenfield);
288 if (lenfield <= 0x7f) {
289 return lenfield;
290 } else if (lenfield == 0x80) {
291 // TODO: 0x80 means indeterminate, and this impl is a workaround.
292 // Giving rest of the file is a workaround, nothing more, nothing less.
293 // https://wf.lavatech.top/ave-but-random/emrtd-data-quirks#EF_SOD
294 return datainlen;
295 } else if (lenfield == 0x81) {
296 int tmp = (*(datain + offset + 1));
297 return tmp;
298 //return ((int) * (datain + offset + 1));
299 } else if (lenfield == 0x82) {
300 int tmp = (*(datain + offset + 1) << 8);
301 tmp |= *(datain + offset + 2);
302 return tmp;
303 //return ((int) * (datain + offset + 1) << 8) | ((int) * (datain + offset + 2));
304 } else if (lenfield == 0x83) {
305 int tmp = (*(datain + offset + 1) << 16);
306 tmp |= (*(datain + offset + 2) << 8);
307 tmp |= *(datain + offset + 3);
308 return tmp;
309 //return (((int) * (datain + offset + 1) << 16) | ((int) * (datain + offset + 2)) << 8) | ((int) * (datain + offset + 3));
311 return 0;
314 static int emrtd_get_asn1_field_length(uint8_t *datain, int datainlen, int offset) {
315 PrintAndLogEx(DEBUG, "asn1 fieldlength, datain: %s", sprint_hex_inrow(datain, datainlen));
316 int lenfield = (int) * (datain + offset);
317 PrintAndLogEx(DEBUG, "asn1 fieldlength, lenfield: %02X", lenfield);
318 if (lenfield <= 0x80) {
319 return 1;
320 } else if (lenfield == 0x81) {
321 return 2;
322 } else if (lenfield == 0x82) {
323 return 3;
324 } else if (lenfield == 0x83) {
325 return 4;
327 return 0;
330 static void des3_encrypt_cbc(uint8_t *iv, uint8_t *key, uint8_t *input, int inputlen, uint8_t *output) {
331 mbedtls_des3_context ctx;
332 mbedtls_des3_set2key_enc(&ctx, key);
334 mbedtls_des3_crypt_cbc(&ctx // des3_context
335 , MBEDTLS_DES_ENCRYPT // int mode
336 , inputlen // length
337 , iv // iv[8]
338 , input // input
339 , output // output
341 mbedtls_des3_free(&ctx);
344 static void des3_decrypt_cbc(uint8_t *iv, uint8_t *key, uint8_t *input, int inputlen, uint8_t *output) {
345 mbedtls_des3_context ctx;
346 mbedtls_des3_set2key_dec(&ctx, key);
348 mbedtls_des3_crypt_cbc(&ctx // des3_context
349 , MBEDTLS_DES_DECRYPT // int mode
350 , inputlen // length
351 , iv // iv[8]
352 , input // input
353 , output // output
355 mbedtls_des3_free(&ctx);
358 static int pad_block(uint8_t *input, int inputlen, uint8_t *output) {
359 const uint8_t padding[8] = {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
361 memcpy(output, input, inputlen);
363 int to_pad = (8 - (inputlen % 8));
365 for (int i = 0; i < to_pad; i++) {
366 output[inputlen + i] = padding[i];
369 return inputlen + to_pad;
372 static void retail_mac(uint8_t *key, uint8_t *input, int inputlen, uint8_t *output) {
373 // This code assumes blocklength (n) = 8, and input len of up to 240 or so chars
374 // This code takes inspirations from https://github.com/devinvenable/iso9797algorithm3
375 uint8_t k0[8];
376 uint8_t k1[8];
377 uint8_t intermediate[8] = {0x00};
378 uint8_t intermediate_des[256];
379 uint8_t block[8];
380 uint8_t message[256];
382 // Populate keys
383 memcpy(k0, key, 8);
384 memcpy(k1, key + 8, 8);
386 // Prepare message
387 int blocksize = pad_block(input, inputlen, message);
389 // Do chaining and encryption
390 for (int i = 0; i < (blocksize / 8); i++) {
391 memcpy(block, message + (i * 8), 8);
393 // XOR
394 for (int x = 0; x < 8; x++) {
395 intermediate[x] = intermediate[x] ^ block[x];
398 des_encrypt(intermediate_des, intermediate, k0);
399 memcpy(intermediate, intermediate_des, 8);
403 des_decrypt(intermediate_des, intermediate, k1);
404 memcpy(intermediate, intermediate_des, 8);
406 des_encrypt(intermediate_des, intermediate, k0);
407 memcpy(output, intermediate_des, 8);
410 static void emrtd_deskey(uint8_t *seed, const uint8_t *type, int length, uint8_t *dataout) {
411 PrintAndLogEx(DEBUG, "seed.............. %s", sprint_hex_inrow(seed, 16));
413 // combine seed and type
414 uint8_t data[50];
415 memcpy(data, seed, length);
416 memcpy(data + length, type, 4);
417 PrintAndLogEx(DEBUG, "data.............. %s", sprint_hex_inrow(data, length + 4));
419 // SHA1 the key
420 unsigned char key[64];
421 sha1hash(data, length + 4, key);
422 PrintAndLogEx(DEBUG, "key............... %s", sprint_hex_inrow(key, length + 4));
424 // Set parity bits
425 for (int i = 0; i < ((length + 4) / 8); i++) {
426 mbedtls_des_key_set_parity(key + (i * 8));
428 PrintAndLogEx(DEBUG, "post-parity key... %s", sprint_hex_inrow(key, 20));
430 memcpy(dataout, &key, length);
433 static void _emrtd_convert_fileid(uint16_t file, uint8_t *dataout) {
434 dataout[0] = file >> 8;
435 dataout[1] = file & 0xFF;
438 static int emrtd_select_file_by_name(uint8_t namelen, uint8_t *name) {
439 return emrtd_exchange_commands_noout((sAPDU_t) {0, ISO7816_SELECT_FILE, EMRTD_P1_SELECT_BY_NAME, 0x0C, namelen, name}, false, true);
442 static int emrtd_select_file_by_ef(uint16_t file_id) {
443 uint8_t data[2];
444 _emrtd_convert_fileid(file_id, data);
445 return emrtd_exchange_commands_noout((sAPDU_t) {0, ISO7816_SELECT_FILE, EMRTD_P1_SELECT_BY_EF, 0x0C, sizeof(data), data}, false, true);
448 static int emrtd_get_challenge(int length, uint8_t *dataout, size_t maxdataoutlen, size_t *dataoutlen) {
449 return emrtd_exchange_commands((sAPDU_t) {0, ISO7816_GET_CHALLENGE, 0, 0, 0, NULL}, true, length, dataout, maxdataoutlen, dataoutlen, false, true);
452 static int emrtd_external_authenticate(uint8_t *data, int length, uint8_t *dataout, size_t maxdataoutlen, size_t *dataoutlen) {
453 return emrtd_exchange_commands((sAPDU_t) {0, ISO7816_EXTERNAL_AUTHENTICATION, 0, 0, length, data}, true, length, dataout, maxdataoutlen, dataoutlen, false, true);
456 static int _emrtd_read_binary(int offset, int bytes_to_read, uint8_t *dataout, size_t maxdataoutlen, size_t *dataoutlen) {
457 return emrtd_exchange_commands((sAPDU_t) {0, ISO7816_READ_BINARY, offset >> 8, offset & 0xFF, 0, NULL}, true, bytes_to_read, dataout, maxdataoutlen, dataoutlen, false, true);
460 static void emrtd_bump_ssc(uint8_t *ssc) {
461 PrintAndLogEx(DEBUG, "ssc-b: %s", sprint_hex_inrow(ssc, 8));
462 for (int i = 7; i > 0; i--) {
463 if ((*(ssc + i)) == 0xFF) {
464 // Set anything already FF to 0, we'll do + 1 on num to left anyways
465 (*(ssc + i)) = 0;
466 } else {
467 (*(ssc + i)) += 1;
468 PrintAndLogEx(DEBUG, "ssc-a: %s", sprint_hex_inrow(ssc, 8));
469 return;
474 static bool emrtd_check_cc(uint8_t *ssc, uint8_t *key, uint8_t *rapdu, int rapdulength) {
475 // https://elixi.re/i/clarkson.png
476 uint8_t k[500] = { 0x00 };
477 uint8_t cc[500] = { 0x00 };
479 emrtd_bump_ssc(ssc);
481 memcpy(k, ssc, 8);
482 int length = 0;
483 int length2 = 0;
485 if (*(rapdu) == 0x87) {
486 length += 2 + (*(rapdu + 1));
487 memcpy(k + 8, rapdu, length);
488 PrintAndLogEx(DEBUG, "len1: %i", length);
491 if ((*(rapdu + length)) == 0x99) {
492 length2 += 2 + (*(rapdu + (length + 1)));
493 memcpy(k + length + 8, rapdu + length, length2);
494 PrintAndLogEx(DEBUG, "len2: %i", length2);
497 int klength = length + length2 + 8;
499 retail_mac(key, k, klength, cc);
500 PrintAndLogEx(DEBUG, "cc: %s", sprint_hex_inrow(cc, 8));
501 PrintAndLogEx(DEBUG, "rapdu: %s", sprint_hex_inrow(rapdu, rapdulength));
502 PrintAndLogEx(DEBUG, "rapdu cut: %s", sprint_hex_inrow(rapdu + (rapdulength - 8), 8));
503 PrintAndLogEx(DEBUG, "k: %s", sprint_hex_inrow(k, klength));
505 return memcmp(cc, rapdu + (rapdulength - 8), 8) == 0;
508 static bool emrtd_secure_select_file_by_ef(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, uint16_t file) {
509 uint8_t response[PM3_CMD_DATA_SIZE] = { 0x00 };
510 size_t resplen = 0;
512 // convert fileid to bytes
513 uint8_t file_id[2] = { 0x00 };
514 _emrtd_convert_fileid(file, file_id);
516 uint8_t iv[8] = { 0x00 };
517 uint8_t cmd[8] = { 0x00 };
518 uint8_t data[21] = { 0x00 };
519 uint8_t temp[8] = {0x0c, 0xa4, EMRTD_P1_SELECT_BY_EF, 0x0c};
521 int cmdlen = pad_block(temp, 4, cmd);
522 int datalen = pad_block(file_id, 2, data);
523 PrintAndLogEx(DEBUG, "cmd: %s", sprint_hex_inrow(cmd, cmdlen));
524 PrintAndLogEx(DEBUG, "data: %s", sprint_hex_inrow(data, datalen));
526 des3_encrypt_cbc(iv, kenc, data, datalen, temp);
527 PrintAndLogEx(DEBUG, "temp: %s", sprint_hex_inrow(temp, datalen));
528 uint8_t do87[11] = {0x87, 0x09, 0x01};
529 memcpy(do87 + 3, temp, datalen);
530 PrintAndLogEx(DEBUG, "do87: %s", sprint_hex_inrow(do87, datalen + 3));
532 uint8_t m[19];
533 memcpy(m, cmd, cmdlen);
534 memcpy(m + cmdlen, do87, (datalen + 3));
535 PrintAndLogEx(DEBUG, "m: %s", sprint_hex_inrow(m, datalen + cmdlen + 3));
537 emrtd_bump_ssc(ssc);
539 uint8_t n[27];
540 memcpy(n, ssc, 8);
541 memcpy(n + 8, m, (cmdlen + datalen + 3));
542 PrintAndLogEx(DEBUG, "n: %s", sprint_hex_inrow(n, (cmdlen + datalen + 11)));
544 uint8_t cc[8];
545 retail_mac(kmac, n, (cmdlen + datalen + 11), cc);
546 PrintAndLogEx(DEBUG, "cc: %s", sprint_hex_inrow(cc, 8));
548 uint8_t do8e[10] = {0x8E, 0x08};
549 memcpy(do8e + 2, cc, 8);
550 PrintAndLogEx(DEBUG, "do8e: %s", sprint_hex_inrow(do8e, 10));
552 int lc = datalen + 3 + 10;
553 PrintAndLogEx(DEBUG, "lc: %i", lc);
555 memcpy(data, do87, datalen + 3);
556 memcpy(data + (datalen + 3), do8e, 10);
557 PrintAndLogEx(DEBUG, "data: %s", sprint_hex_inrow(data, lc));
559 if (emrtd_exchange_commands((sAPDU_t) {0x0C, ISO7816_SELECT_FILE, EMRTD_P1_SELECT_BY_EF, 0x0C, lc, data}, true, 0, response, sizeof(response), &resplen, false, true) == false) {
560 return false;
563 return emrtd_check_cc(ssc, kmac, response, resplen);
566 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) {
567 uint8_t cmd[8] = { 0x00 };
568 uint8_t data[21] = { 0x00 };
569 uint8_t temp[8] = {0x0c, 0xb0};
571 PrintAndLogEx(DEBUG, "kmac: %s", sprint_hex_inrow(kmac, EMRTD_KMAC_LEN));
573 // Set p1 and p2
574 temp[2] = (uint8_t)(offset >> 8);
575 temp[3] = (uint8_t)(offset >> 0);
577 int cmdlen = pad_block(temp, 4, cmd);
578 PrintAndLogEx(DEBUG, "cmd: %s", sprint_hex_inrow(cmd, cmdlen));
580 uint8_t do97[3] = {0x97, 0x01, bytes_to_read};
582 uint8_t m[11] = { 0x00 };
583 memcpy(m, cmd, 8);
584 memcpy(m + 8, do97, 3);
586 emrtd_bump_ssc(ssc);
588 uint8_t n[19] = { 0x00 };
589 memcpy(n, ssc, 8);
590 memcpy(n + 8, m, 11);
591 PrintAndLogEx(DEBUG, "n: %s", sprint_hex_inrow(n, sizeof(n)));
593 uint8_t cc[8] = { 0x00 };
594 retail_mac(kmac, n, 19, cc);
595 PrintAndLogEx(DEBUG, "cc: %s", sprint_hex_inrow(cc, sizeof(cc)));
597 uint8_t do8e[10] = {0x8E, 0x08};
598 memcpy(do8e + 2, cc, 8);
599 PrintAndLogEx(DEBUG, "do8e: %s", sprint_hex_inrow(do8e, sizeof(do8e)));
601 int lc = 13;
602 PrintAndLogEx(DEBUG, "lc: %i", lc);
604 memcpy(data, do97, 3);
605 memcpy(data + 3, do8e, 10);
606 PrintAndLogEx(DEBUG, "data: %s", sprint_hex_inrow(data, lc));
608 if (emrtd_exchange_commands((sAPDU_t) {0x0C, ISO7816_READ_BINARY, offset >> 8, offset & 0xFF, lc, data}, true, 0, dataout, maxdataoutlen, dataoutlen, false, true) == false) {
609 return false;
612 return emrtd_check_cc(ssc, kmac, dataout, *dataoutlen);
615 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) {
616 uint8_t response[500] = { 0x00 };
617 uint8_t temp[500] = { 0x00 };
618 size_t resplen, cutat = 0;
619 uint8_t iv[8] = { 0x00 };
621 if (_emrtd_secure_read_binary(kmac, ssc, offset, bytes_to_read, response, sizeof(response), &resplen) == false) {
622 return false;
625 PrintAndLogEx(DEBUG, "secreadbindec, offset %i on read %i: encrypted: %s", offset, bytes_to_read, sprint_hex_inrow(response, resplen));
627 cutat = ((int) response[1]) - 1;
629 des3_decrypt_cbc(iv, kenc, response + 3, cutat, temp);
630 memcpy(dataout, temp, bytes_to_read);
631 PrintAndLogEx(DEBUG, "secreadbindec, offset %i on read %i: decrypted: %s", offset, bytes_to_read, sprint_hex_inrow(temp, cutat));
632 PrintAndLogEx(DEBUG, "secreadbindec, offset %i on read %i: decrypted and cut: %s", offset, bytes_to_read, sprint_hex_inrow(dataout, bytes_to_read));
633 *dataoutlen = bytes_to_read;
634 return true;
637 static int emrtd_read_file(uint8_t *dataout, size_t *dataoutlen, uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, bool use_secure) {
638 uint8_t response[EMRTD_MAX_FILE_SIZE] = { 0x00 };
639 size_t resplen = 0;
640 uint8_t tempresponse[500] = { 0x00 };
641 size_t tempresplen = 0;
642 int toread = 4;
643 int offset = 0;
645 if (use_secure) {
646 if (_emrtd_secure_read_binary_decrypt(kenc, kmac, ssc, offset, toread, response, &resplen) == false) {
647 return false;
649 } else {
650 if (_emrtd_read_binary(offset, toread, response, sizeof(response), &resplen) == false) {
651 return false;
655 int datalen = emrtd_get_asn1_data_length(response, resplen, 1);
656 int readlen = datalen - (3 - emrtd_get_asn1_field_length(response, resplen, 1));
657 offset = 4;
659 uint8_t lnbreak = 32;
660 PrintAndLogEx(INFO, "." NOLF);
661 while (readlen > 0) {
662 toread = readlen;
663 if (readlen > 118) {
664 toread = 118;
667 if (use_secure) {
668 if (_emrtd_secure_read_binary_decrypt(kenc, kmac, ssc, offset, toread, tempresponse, &tempresplen) == false) {
669 PrintAndLogEx(NORMAL, "");
670 return false;
672 } else {
673 if (_emrtd_read_binary(offset, toread, tempresponse, sizeof(tempresponse), &tempresplen) == false) {
674 PrintAndLogEx(NORMAL, "");
675 return false;
679 memcpy(response + resplen, tempresponse, tempresplen);
680 offset += toread;
681 readlen -= toread;
682 resplen += tempresplen;
684 PrintAndLogEx(NORMAL, "." NOLF);
685 fflush(stdout);
686 lnbreak--;
687 if (lnbreak == 0) {
688 PrintAndLogEx(NORMAL, "");
689 PrintAndLogEx(INFO, "." NOLF);
690 lnbreak = 32;
693 PrintAndLogEx(NORMAL, "");
695 memcpy(dataout, &response, resplen);
696 *dataoutlen = resplen;
697 return true;
700 static int emrtd_lds_determine_tag_length(uint8_t tag) {
701 if ((tag == 0x5F) || (tag == 0x7F)) {
702 return 2;
704 return 1;
707 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) {
708 int offset = 0;
709 int skipcounter = 0;
711 if (entertoptag) {
712 offset += emrtd_lds_determine_tag_length(*datain);
713 offset += emrtd_get_asn1_field_length(datain, datainlen, offset);
716 while (offset < datainlen) {
717 PrintAndLogEx(DEBUG, "emrtd_lds_get_data_by_tag, offset: %i, data: %X", offset, *(datain + offset));
718 // Determine element ID length to set as offset on asn1datalength
719 int e_idlen = emrtd_lds_determine_tag_length(*(datain + offset));
721 // Get the length of the element
722 int e_datalen = emrtd_get_asn1_data_length(datain + offset, datainlen - offset, e_idlen);
724 // Get the length of the element's length
725 int e_fieldlen = emrtd_get_asn1_field_length(datain + offset, datainlen - offset, e_idlen);
727 PrintAndLogEx(DEBUG, "emrtd_lds_get_data_by_tag, e_idlen: %02X, e_datalen: %02X, e_fieldlen: %02X", e_idlen, e_datalen, e_fieldlen);
729 // If the element is what we're looking for, get the data and return true
730 if (*(datain + offset) == tag1 && (!twobytetag || *(datain + offset + 1) == tag2)) {
731 if (skipcounter < skiptagcount) {
732 skipcounter += 1;
733 } else if (datainlen > e_datalen) {
734 *dataoutlen = e_datalen;
735 memcpy(dataout, datain + offset + e_idlen + e_fieldlen, e_datalen);
736 return true;
737 } else {
738 PrintAndLogEx(ERR, "error (emrtd_lds_get_data_by_tag) e_datalen out-of-bounds");
739 return false;
742 offset += e_idlen + e_datalen + e_fieldlen;
744 // Return false if we can't find the relevant element
745 return false;
748 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) {
749 if (use_secure) {
750 if (emrtd_secure_select_file_by_ef(ks_enc, ks_mac, ssc, file) == false) {
751 PrintAndLogEx(ERR, "Failed to secure select %04X", file);
752 return false;
754 } else {
755 if (emrtd_select_file_by_ef(file) == false) {
756 PrintAndLogEx(ERR, "Failed to select %04X", file);
757 return false;
761 if (emrtd_read_file(dataout, dataoutlen, ks_enc, ks_mac, ssc, use_secure) == false) {
762 PrintAndLogEx(ERR, "Failed to read %04X", file);
763 return false;
765 return true;
768 static const uint8_t jpeg_header[4] = { 0xFF, 0xD8, 0xFF, 0xE0 };
769 static const uint8_t jpeg2k_header[6] = { 0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50 };
771 static int emrtd_dump_ef_dg2(uint8_t *file_contents, size_t file_length, const char *path) {
772 size_t offset;
773 int datalen = 0;
775 // This is a hacky impl that just looks for the image header. I'll improve it eventually.
776 // based on mrpkey.py
777 // Note: Doing file_length - 6 to account for the longest data we're checking.
778 // Checks first byte before the rest to reduce overhead
779 for (offset = 0; offset < file_length - 6; offset++) {
780 if ((file_contents[offset] == 0xFF && memcmp(jpeg_header, file_contents + offset, 4) == 0) ||
781 (file_contents[offset] == 0x00 && memcmp(jpeg2k_header, file_contents + offset, 6) == 0)) {
782 datalen = file_length - offset;
783 break;
787 // If we didn't get any data, return false.
788 if (datalen == 0) {
789 return PM3_ESOFT;
792 char *filepath = calloc(strlen(path) + 100, sizeof(char));
793 if (filepath == NULL)
794 return PM3_EMALLOC;
796 strcpy(filepath, path);
797 strncat(filepath, PATHSEP, 2);
798 strcat(filepath, dg_table[EF_DG2].filename);
800 saveFile(filepath, file_contents[offset] == 0xFF ? ".jpg" : ".jp2", file_contents + offset, datalen);
802 free(filepath);
803 return PM3_SUCCESS;
806 static int emrtd_dump_ef_dg5(uint8_t *file_contents, size_t file_length, const char *path) {
807 uint8_t data[EMRTD_MAX_FILE_SIZE] = { 0x00 };
808 size_t datalen = 0;
810 // If we can't find image in EF_DG5, return false.
811 if (emrtd_lds_get_data_by_tag(file_contents, file_length, data, &datalen, 0x5F, 0x40, true, true, 0) == false) {
812 return PM3_ESOFT;
815 if (datalen < EMRTD_MAX_FILE_SIZE) {
816 char *filepath = calloc(strlen(path) + 100, sizeof(char));
817 if (filepath == NULL) {
818 return PM3_EMALLOC;
820 strcpy(filepath, path);
821 strncat(filepath, PATHSEP, 2);
822 strcat(filepath, dg_table[EF_DG5].filename);
824 saveFile(filepath, data[0] == 0xFF ? ".jpg" : ".jp2", data, datalen);
826 free(filepath);
827 } else {
828 PrintAndLogEx(ERR, "error (emrtd_dump_ef_dg5) datalen out-of-bounds");
829 return PM3_ESOFT;
831 return PM3_SUCCESS;
834 static int emrtd_dump_ef_dg7(uint8_t *file_contents, size_t file_length, const char *path) {
835 uint8_t data[EMRTD_MAX_FILE_SIZE] = { 0x00 };
836 size_t datalen = 0;
838 // If we can't find image in EF_DG7, return false.
839 if (emrtd_lds_get_data_by_tag(file_contents, file_length, data, &datalen, 0x5F, 0x42, true, true, 0) == false) {
840 return PM3_ESOFT;
843 if (datalen < EMRTD_MAX_FILE_SIZE) {
844 char *filepath = calloc(strlen(path) + 100, sizeof(char));
845 if (filepath == NULL) {
846 return PM3_EMALLOC;
848 strcpy(filepath, path);
849 strncat(filepath, PATHSEP, 2);
850 strcat(filepath, dg_table[EF_DG7].filename);
852 saveFile(filepath, data[0] == 0xFF ? ".jpg" : ".jp2", data, datalen);
854 free(filepath);
855 } else {
856 PrintAndLogEx(ERR, "error (emrtd_dump_ef_dg7) datalen out-of-bounds");
857 return PM3_ESOFT;
859 return PM3_SUCCESS;
862 static int emrtd_dump_ef_sod(uint8_t *file_contents, size_t file_length, const char *path) {
863 int fieldlen = emrtd_get_asn1_field_length(file_contents, file_length, 1);
864 int datalen = emrtd_get_asn1_data_length(file_contents, file_length, 1);
866 if (fieldlen + 1 > EMRTD_MAX_FILE_SIZE) {
867 PrintAndLogEx(ERR, "error (emrtd_dump_ef_sod) fieldlen out-of-bounds");
868 return PM3_EOUTOFBOUND;
871 char *filepath = calloc(strlen(path) + 100, sizeof(char));
872 if (filepath == NULL) {
873 return PM3_EMALLOC;
876 strcpy(filepath, path);
877 strncat(filepath, PATHSEP, 2);
878 strcat(filepath, dg_table[EF_SOD].filename);
880 saveFile(filepath, ".p7b", file_contents + fieldlen + 1, datalen);
881 free(filepath);
882 return PM3_ESOFT;
885 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) {
886 uint8_t response[EMRTD_MAX_FILE_SIZE] = { 0x00 };
887 size_t resplen = 0;
889 if (emrtd_select_and_read(response, &resplen, file, ks_enc, ks_mac, ssc, use_secure) == false) {
890 return false;
893 char *filepath = calloc(strlen(path) + 100, sizeof(char));
894 if (filepath == NULL) {
895 return false;
898 strcpy(filepath, path);
899 strncat(filepath, PATHSEP, 2);
900 strcat(filepath, name);
902 PrintAndLogEx(INFO, "Read " _YELLOW_("%s") ", len %zu", name, resplen);
903 PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars)");
904 PrintAndLogEx(DEBUG, "------------------------------------------");
905 PrintAndLogEx(DEBUG, "%s", sprint_hex_inrow(response, resplen));
906 PrintAndLogEx(DEBUG, "------------------------------------------");
907 saveFile(filepath, ".bin", response, resplen);
909 emrtd_dg_t *dg = emrtd_fileid_to_dg(file);
910 if ((dg != NULL) && (dg->dumper != NULL)) {
911 dg->dumper(response, resplen, path);
914 free(filepath);
915 return true;
918 static void rng(int length, uint8_t *dataout) {
919 // Do very very secure prng operations
920 //for (int i = 0; i < (length / 4); i++) {
921 // num_to_bytes(prng_successor(msclock() + i, 32), 4, &dataout[i * 4]);
923 memset(dataout, 0x00, length);
926 static bool emrtd_do_bac(char *documentnumber, char *dob, char *expiry, uint8_t *ssc, uint8_t *ks_enc, uint8_t *ks_mac) {
927 uint8_t response[EMRTD_MAX_FILE_SIZE] = { 0x00 };
928 size_t resplen = 0;
930 uint8_t rnd_ic[10] = { 0x00 }; // 8 + SW
931 uint8_t kenc[50] = { 0x00 };
932 uint8_t kmac[50] = { 0x00 };
933 uint8_t k_icc[16] = { 0x00 };
934 uint8_t S[32] = { 0x00 };
936 uint8_t rnd_ifd[8], k_ifd[16];
937 rng(8, rnd_ifd);
938 rng(16, k_ifd);
940 PrintAndLogEx(DEBUG, "doc............... " _GREEN_("%s"), documentnumber);
941 PrintAndLogEx(DEBUG, "dob............... " _GREEN_("%s"), dob);
942 PrintAndLogEx(DEBUG, "exp............... " _GREEN_("%s"), expiry);
944 char documentnumbercd = emrtd_calculate_check_digit(documentnumber);
945 char dobcd = emrtd_calculate_check_digit(dob);
946 char expirycd = emrtd_calculate_check_digit(expiry);
948 char kmrz[25];
949 snprintf(kmrz, sizeof(kmrz), "%s%i%s%i%s%i", documentnumber, documentnumbercd, dob, dobcd, expiry, expirycd);
950 PrintAndLogEx(DEBUG, "kmrz.............. " _GREEN_("%s"), kmrz);
952 uint8_t kseed[20] = { 0x00 };
953 sha1hash((unsigned char *)kmrz, strlen(kmrz), kseed);
954 PrintAndLogEx(DEBUG, "kseed (sha1)...... %s ", sprint_hex_inrow(kseed, 16));
956 emrtd_deskey(kseed, KENC_type, 16, kenc);
957 emrtd_deskey(kseed, KMAC_type, 16, kmac);
958 PrintAndLogEx(DEBUG, "kenc.............. %s", sprint_hex_inrow(kenc, 16));
959 PrintAndLogEx(DEBUG, "kmac.............. %s", sprint_hex_inrow(kmac, 16));
961 // Get Challenge
962 if (emrtd_get_challenge(8, rnd_ic, sizeof(rnd_ic), &resplen) == false) {
963 PrintAndLogEx(ERR, "Couldn't get challenge.");
964 return false;
966 PrintAndLogEx(DEBUG, "rnd_ic............ %s", sprint_hex_inrow(rnd_ic, 8));
968 memcpy(S, rnd_ifd, 8);
969 memcpy(S + 8, rnd_ic, 8);
970 memcpy(S + 16, k_ifd, 16);
972 PrintAndLogEx(DEBUG, "S................. %s", sprint_hex_inrow(S, 32));
974 uint8_t iv[8] = { 0x00 };
975 uint8_t e_ifd[32] = { 0x00 };
977 des3_encrypt_cbc(iv, kenc, S, sizeof(S), e_ifd);
978 PrintAndLogEx(DEBUG, "e_ifd............. %s", sprint_hex_inrow(e_ifd, 32));
980 uint8_t m_ifd[8] = { 0x00 };
982 retail_mac(kmac, e_ifd, 32, m_ifd);
983 PrintAndLogEx(DEBUG, "m_ifd............. %s", sprint_hex_inrow(m_ifd, 8));
985 uint8_t cmd_data[40];
986 memcpy(cmd_data, e_ifd, 32);
987 memcpy(cmd_data + 32, m_ifd, 8);
989 // Do external authentication
990 if (emrtd_external_authenticate(cmd_data, sizeof(cmd_data), response, sizeof(response), &resplen) == false) {
991 PrintAndLogEx(ERR, "Couldn't do external authentication. Did you supply the correct MRZ info?");
992 return false;
994 PrintAndLogEx(INFO, "External authentication with BAC successful");
996 uint8_t dec_output[32] = { 0x00 };
997 des3_decrypt_cbc(iv, kenc, response, 32, dec_output);
998 PrintAndLogEx(DEBUG, "dec_output........ %s", sprint_hex_inrow(dec_output, 32));
1000 if (memcmp(rnd_ifd, dec_output + 8, 8) != 0) {
1001 PrintAndLogEx(ERR, "Challenge failed, rnd_ifd does not match.");
1002 return false;
1005 memcpy(k_icc, dec_output + 16, 16);
1007 // Calculate session keys
1008 for (int x = 0; x < 16; x++) {
1009 kseed[x] = k_ifd[x] ^ k_icc[x];
1012 PrintAndLogEx(DEBUG, "kseed............ %s", sprint_hex_inrow(kseed, 16));
1014 emrtd_deskey(kseed, KENC_type, 16, ks_enc);
1015 emrtd_deskey(kseed, KMAC_type, 16, ks_mac);
1017 PrintAndLogEx(DEBUG, "ks_enc........ %s", sprint_hex_inrow(ks_enc, 16));
1018 PrintAndLogEx(DEBUG, "ks_mac........ %s", sprint_hex_inrow(ks_mac, 16));
1020 memcpy(ssc, rnd_ic + 4, 4);
1021 memcpy(ssc + 4, rnd_ifd + 4, 4);
1023 PrintAndLogEx(DEBUG, "ssc........... %s", sprint_hex_inrow(ssc, 8));
1025 return true;
1028 static bool emrtd_connect(void) {
1029 int res = Iso7816Connect(CC_CONTACTLESS);
1030 return res == PM3_SUCCESS;
1033 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) {
1035 // Select MRTD applet
1036 uint8_t aid[] = EMRTD_AID_MRTD;
1037 if (emrtd_select_file_by_name(sizeof(aid), aid) == false) {
1038 PrintAndLogEx(ERR, "Couldn't select the MRTD application.");
1039 return false;
1042 // Select EF_COM
1043 if (emrtd_select_file_by_ef(dg_table[EF_COM].fileid) == false) {
1044 *BAC = true;
1045 PrintAndLogEx(INFO, "Authentication is enforced");
1046 PrintAndLogEx(INFO, "Switching to external authentication");
1047 } else {
1048 *BAC = false;
1049 // Select EF_DG1
1050 emrtd_select_file_by_ef(dg_table[EF_DG1].fileid);
1052 size_t resplen = 0;
1053 uint8_t response[EMRTD_MAX_FILE_SIZE] = { 0x00 };
1054 if (emrtd_read_file(response, &resplen, NULL, NULL, NULL, false) == false) {
1055 *BAC = true;
1056 PrintAndLogEx(INFO, "Authentication is enforced");
1057 PrintAndLogEx(INFO, "Switching to external authentication");
1058 } else {
1059 *BAC = false;
1063 // Do Basic Access Control
1064 if (*BAC) {
1065 // If BAC isn't available, exit out and warn user.
1066 if (BAC_available == false) {
1067 PrintAndLogEx(ERR, "This eMRTD enforces authentication, but you didn't supply MRZ data. Cannot proceed.");
1068 PrintAndLogEx(HINT, "Check out `hf emrtd info/dump --h`, supply data with `-n` `-d` and `-e`");
1069 return false;
1072 if (emrtd_do_bac(documentnumber, dob, expiry, ssc, ks_enc, ks_mac) == false) {
1073 return false;
1076 return true;
1079 int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available, const char *path) {
1080 uint8_t response[EMRTD_MAX_FILE_SIZE] = { 0x00 };
1081 size_t resplen = 0;
1082 uint8_t ssc[8] = { 0x00 };
1083 uint8_t ks_enc[EMRTD_KMAC_LEN] = { 0x00 };
1084 uint8_t ks_mac[EMRTD_KMAC_LEN] = { 0x00 };
1085 bool BAC = false;
1087 // Select the eMRTD
1088 if (emrtd_connect() == false) {
1089 DropField();
1090 return PM3_ESOFT;
1093 // Dump EF_CardAccess (if available)
1094 if (emrtd_dump_file(ks_enc, ks_mac, ssc, dg_table[EF_CardAccess].fileid, dg_table[EF_CardAccess].filename, BAC, path) == false) {
1095 PrintAndLogEx(INFO, "Couldn't dump EF_CardAccess, card does not support PACE");
1096 PrintAndLogEx(HINT, "This is expected behavior for cards without PACE, and isn't something to be worried about");
1099 // Authenticate with the eMRTD
1100 if (emrtd_do_auth(documentnumber, dob, expiry, BAC_available, &BAC, ssc, ks_enc, ks_mac) == false) {
1101 DropField();
1102 return PM3_ESOFT;
1105 // Select EF_COM
1106 if (emrtd_select_and_read(response, &resplen, dg_table[EF_COM].fileid, ks_enc, ks_mac, ssc, BAC) == false) {
1107 PrintAndLogEx(ERR, "Failed to read EF_COM");
1108 DropField();
1109 return PM3_ESOFT;
1113 char *filepath = calloc(strlen(path) + 100, sizeof(char));
1114 if (filepath == NULL) {
1115 return PM3_EMALLOC;
1118 strcpy(filepath, path);
1119 strncat(filepath, PATHSEP, 2);
1120 strcat(filepath, dg_table[EF_COM].filename);
1122 PrintAndLogEx(INFO, "Read EF_COM, len %zu", resplen);
1123 PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars)... %s", sprint_hex_inrow(response, resplen));
1124 saveFile(filepath, ".bin", response, resplen);
1126 free(filepath);
1128 uint8_t filelist[50];
1129 size_t filelistlen = 0;
1131 if (emrtd_lds_get_data_by_tag(response, resplen, filelist, &filelistlen, 0x5c, 0x00, false, true, 0) == false) {
1132 PrintAndLogEx(ERR, "Failed to read file list from EF_COM");
1133 DropField();
1134 return PM3_ESOFT;
1137 PrintAndLogEx(DEBUG, "File List... %s", sprint_hex_inrow(filelist, filelistlen));
1138 // Add EF_SOD to the list
1139 filelist[filelistlen++] = 0x77;
1140 // Dump all files in the file list
1141 for (int i = 0; i < filelistlen; i++) {
1142 emrtd_dg_t *dg = emrtd_tag_to_dg(filelist[i]);
1143 if (dg == NULL) {
1144 PrintAndLogEx(INFO, "File tag not found, skipping... %02X", filelist[i]);
1145 continue;
1147 PrintAndLogEx(DEBUG, "Current file... %s", dg->filename);
1148 if (!dg->pace && !dg->eac) {
1149 emrtd_dump_file(ks_enc, ks_mac, ssc, dg->fileid, dg->filename, BAC, path);
1152 DropField();
1153 return PM3_SUCCESS;
1156 static bool emrtd_compare_check_digit(char *datain, int datalen, char expected_check_digit) {
1157 char tempdata[90] = { 0x00 };
1158 memcpy(tempdata, datain, datalen);
1160 uint8_t check_digit = emrtd_calculate_check_digit(tempdata) + 0x30;
1161 bool res = check_digit == expected_check_digit;
1162 PrintAndLogEx(DEBUG, "emrtd_compare_check_digit, expected %c == %c calculated ( %s )"
1163 , expected_check_digit
1164 , check_digit
1165 , (res) ? _GREEN_("ok") : _RED_("fail"));
1166 return res;
1169 static bool emrtd_mrz_verify_check_digit(char *mrz, int offset, int datalen) {
1170 char tempdata[90] = { 0x00 };
1171 memcpy(tempdata, mrz + offset, datalen);
1172 return emrtd_compare_check_digit(tempdata, datalen, mrz[offset + datalen]);
1175 static void emrtd_print_legal_sex(char *legal_sex) {
1176 char sex[12] = { 0x00 };
1177 switch (*legal_sex) {
1178 case 'M':
1179 strncpy(sex, "Male", 5);
1180 break;
1181 case 'F':
1182 strncpy(sex, "Female", 7);
1183 break;
1184 case '<':
1185 strncpy(sex, "Unspecified", 12);
1186 break;
1188 PrintAndLogEx(SUCCESS, "Legal Sex Marker......... " _YELLOW_("%s"), sex);
1191 static int emrtd_mrz_determine_length(const char *mrz, int offset, int max_length) {
1192 int i;
1193 for (i = max_length; i >= 1; i--) {
1194 if (mrz[offset + i - 1] != '<') {
1195 return i;
1199 return 0;
1202 static int emrtd_mrz_determine_separator(const char *mrz, int offset, int max_length) {
1203 // Note: this function does not account for len=0
1204 int i;
1205 for (i = max_length - 1; i > 0; i--) {
1206 if (mrz[offset + i] == '<' && mrz[offset + i + 1] == '<') {
1207 break;
1210 return i;
1213 static void emrtd_mrz_replace_pad(char *data, int datalen, char newchar) {
1214 for (int i = 0; i < datalen; i++) {
1215 if (data[i] == '<') {
1216 data[i] = newchar;
1221 static void emrtd_print_optional_elements(char *mrz, int offset, int length, bool verify_check_digit) {
1222 int i = emrtd_mrz_determine_length(mrz, offset, length);
1223 if (i == 0) {
1224 return;
1227 PrintAndLogEx(SUCCESS, "Optional elements........ " _YELLOW_("%.*s"), i, mrz + offset);
1229 if (verify_check_digit && !emrtd_mrz_verify_check_digit(mrz, offset, length)) {
1230 PrintAndLogEx(SUCCESS, _RED_("Optional element check digit is invalid."));
1234 static void emrtd_print_document_number(char *mrz, int offset) {
1235 int i = emrtd_mrz_determine_length(mrz, offset, 9);
1236 if (i == 0) {
1237 return;
1240 PrintAndLogEx(SUCCESS, "Document Number.......... " _YELLOW_("%.*s"), i, mrz + offset);
1242 if (!emrtd_mrz_verify_check_digit(mrz, offset, 9)) {
1243 PrintAndLogEx(SUCCESS, _RED_("Document number check digit is invalid."));
1247 static void emrtd_print_name(char *mrz, int offset, int max_length, bool localized) {
1248 char final_name[100] = { 0x00 };
1249 int namelen = emrtd_mrz_determine_length(mrz, offset, max_length);
1250 if (namelen == 0) {
1251 return;
1253 int sep = emrtd_mrz_determine_separator(mrz, offset, namelen);
1255 // Account for mononyms
1256 if (sep != 0) {
1257 int firstnamelen = (namelen - (sep + 2));
1259 memcpy(final_name, mrz + offset + sep + 2, firstnamelen);
1260 final_name[firstnamelen] = ' ';
1261 memcpy(final_name + firstnamelen + 1, mrz + offset, sep);
1262 } else {
1263 memcpy(final_name, mrz + offset, namelen);
1266 // Replace < characters with spaces
1267 emrtd_mrz_replace_pad(final_name, namelen, ' ');
1269 if (localized) {
1270 PrintAndLogEx(SUCCESS, "Legal Name (Localized)... " _YELLOW_("%s"), final_name);
1271 } else {
1272 PrintAndLogEx(SUCCESS, "Legal Name............... " _YELLOW_("%s"), final_name);
1276 static void emrtd_mrz_convert_date(char *mrz, int offset, char *final_date, bool is_expiry, bool is_full, bool is_ascii) {
1277 char work_date[9] = { 0x00 };
1278 int len = is_full ? 8 : 6;
1280 // Copy the data to a working array in the right format
1281 if (!is_ascii) {
1282 memcpy(work_date, sprint_hex_inrow((uint8_t *)mrz + offset, len / 2), len);
1283 } else {
1284 memcpy(work_date, mrz + offset, len);
1287 // Set offset to 0 as we've now copied data.
1288 offset = 0;
1290 if (is_full) {
1291 // If we get the full date, use the first two characters from that for year
1292 memcpy(final_date, work_date, 2);
1293 // and do + 2 on offset so that rest of code uses the right data
1294 offset += 2;
1295 } else {
1296 char temp_year[3] = { 0x00 };
1297 memcpy(temp_year, work_date, 2);
1298 // If it's > 20, assume 19xx.
1299 if (strtol(temp_year, NULL, 10) < 20 || is_expiry) {
1300 final_date[0] = '2';
1301 final_date[1] = '0';
1302 } else {
1303 final_date[0] = '1';
1304 final_date[1] = '9';
1308 memcpy(final_date + 2, work_date + offset, 2);
1309 final_date[4] = '-';
1310 memcpy(final_date + 5, work_date + offset + 2, 2);
1311 final_date[7] = '-';
1312 memcpy(final_date + 8, work_date + offset + 4, 2);
1315 static void emrtd_print_dob(char *mrz, int offset, bool full, bool ascii) {
1316 char final_date[12] = { 0x00 };
1317 emrtd_mrz_convert_date(mrz, offset, final_date, false, full, ascii);
1319 PrintAndLogEx(SUCCESS, "Date of birth............ " _YELLOW_("%s"), final_date);
1321 if (!full && !emrtd_mrz_verify_check_digit(mrz, offset, 6)) {
1322 PrintAndLogEx(SUCCESS, _RED_("Date of Birth check digit is invalid."));
1326 static void emrtd_print_expiry(char *mrz, int offset) {
1327 char final_date[12] = { 0x00 };
1328 emrtd_mrz_convert_date(mrz, offset, final_date, true, false, true);
1330 PrintAndLogEx(SUCCESS, "Date of expiry........... " _YELLOW_("%s"), final_date);
1332 if (!emrtd_mrz_verify_check_digit(mrz, offset, 6)) {
1333 PrintAndLogEx(SUCCESS, _RED_("Date of expiry check digit is invalid."));
1337 static void emrtd_print_issuance(char *data, bool ascii) {
1338 char final_date[12] = { 0x00 };
1339 emrtd_mrz_convert_date(data, 0, final_date, true, true, ascii);
1341 PrintAndLogEx(SUCCESS, "Date of issue............ " _YELLOW_("%s"), final_date);
1344 static void emrtd_print_personalization_timestamp(uint8_t *data, size_t datalen) {
1345 if (datalen < 7) {
1346 return;
1349 char str_date[0x0F] = { 0x00 };
1350 strncpy(str_date, sprint_hex_inrow(data, 0x07), sizeof(str_date) - 1);
1352 char final_date[20] = { 0x00 };
1353 snprintf(final_date, sizeof(final_date), "%.4s-%.2s-%.2s %.2s:%.2s:%.2s"
1354 , str_date
1355 , str_date + 4
1356 , str_date + 6
1357 , str_date + 8
1358 , str_date + 10
1359 , str_date + 12
1362 PrintAndLogEx(SUCCESS, "Personalization at....... " _YELLOW_("%s"), final_date);
1365 static void emrtd_print_unknown_timestamp_5f85(uint8_t *data, size_t datalen) {
1366 if (datalen < 14) {
1367 return;
1369 char final_date[20] = { 0x00 };
1370 snprintf(final_date, sizeof(final_date), "%.4s-%.2s-%.2s %.2s:%.2s:%.2s"
1371 , data
1372 , data + 4
1373 , data + 6
1374 , data + 8
1375 , data + 10
1376 , data + 12
1379 PrintAndLogEx(SUCCESS, "Unknown timestamp 5F85... " _YELLOW_("%s"), final_date);
1380 PrintAndLogEx(HINT, "This is very likely the personalization timestamp, but it is using an undocumented tag.");
1383 static int emrtd_print_ef_com_info(uint8_t *data, size_t datalen) {
1384 uint8_t filelist[50];
1385 size_t filelistlen = 0;
1386 bool res = emrtd_lds_get_data_by_tag(data, datalen, filelist, &filelistlen, 0x5c, 0x00, false, true, 0);
1387 if (res == false) {
1388 PrintAndLogEx(ERR, "Failed to read file list from EF_COM.");
1389 return PM3_ESOFT;
1392 // List files in the file list
1393 PrintAndLogEx(NORMAL, "");
1394 PrintAndLogEx(INFO, "------------------------ " _CYAN_("EF_COM") " ------------------------");
1395 for (int i = 0; i < filelistlen; i++) {
1396 emrtd_dg_t *dg = emrtd_tag_to_dg(filelist[i]);
1397 if (dg == NULL) {
1398 PrintAndLogEx(INFO, "File tag not found, skipping: %02X", filelist[i]);
1399 continue;
1401 int n = 25 - strlen(dg->filename);
1402 PrintAndLogEx(SUCCESS, "%s%*.*s " _YELLOW_("%s"), dg->filename, n, n, pad, dg->desc);
1404 return PM3_SUCCESS;
1407 static int emrtd_print_ef_dg1_info(uint8_t *data, size_t datalen) {
1408 int td_variant = 0;
1410 PrintAndLogEx(NORMAL, "");
1411 PrintAndLogEx(INFO, "------------------------ " _CYAN_("EF_DG1") " ------------------------");
1413 // MRZ on TD1 is 90 characters, 30 on each row.
1414 // MRZ on TD3 is 88 characters, 44 on each row.
1415 char mrz[90] = { 0x00 };
1416 size_t mrzlen = 0;
1418 if (emrtd_lds_get_data_by_tag(data, datalen, (uint8_t *) mrz, &mrzlen, 0x5f, 0x1f, true, true, 0) == false) {
1419 PrintAndLogEx(ERR, "Failed to read MRZ from EF_DG1.");
1420 return PM3_ESOFT;
1423 // Determine and print the document type
1424 if (mrz[0] == 'I' && mrz[1] == 'P') {
1425 PrintAndLogEx(SUCCESS, "Document Type............ " _YELLOW_("Passport Card"));
1426 } else if (mrz[0] == 'I') {
1427 PrintAndLogEx(SUCCESS, "Document Type............ " _YELLOW_("ID Card"));
1428 } else if (mrz[0] == 'P') {
1429 PrintAndLogEx(SUCCESS, "Document Type............ " _YELLOW_("Passport"));
1430 } else if (mrz[0] == 'A') {
1431 PrintAndLogEx(SUCCESS, "Document Type............ " _YELLOW_("Residency Permit"));
1432 } else {
1433 PrintAndLogEx(SUCCESS, "Document Type............ " _YELLOW_("Unknown"));
1436 if (mrzlen == 90) {
1437 td_variant = 1;
1438 } else if (mrzlen == 88) {
1439 td_variant = 3;
1440 } else {
1441 PrintAndLogEx(ERR, "MRZ length (%zu) is wrong.", mrzlen);
1442 return PM3_ESOFT;
1445 PrintAndLogEx(SUCCESS, "Document Form Factor..... " _YELLOW_("TD%i"), td_variant);
1447 // Print the MRZ
1448 if (td_variant == 1) {
1449 PrintAndLogEx(DEBUG, "MRZ Row 1... " _YELLOW_("%.30s"), mrz);
1450 PrintAndLogEx(DEBUG, "MRZ Row 2... " _YELLOW_("%.30s"), mrz + 30);
1451 PrintAndLogEx(DEBUG, "MRZ Row 3... " _YELLOW_("%.30s"), mrz + 60);
1452 } else if (td_variant == 3) {
1453 PrintAndLogEx(DEBUG, "MRZ Row 1... " _YELLOW_("%.44s"), mrz);
1454 PrintAndLogEx(DEBUG, "MRZ Row 2... " _YELLOW_("%.44s"), mrz + 44);
1457 PrintAndLogEx(SUCCESS, "Issuing state............ " _YELLOW_("%.3s"), mrz + 2);
1459 if (td_variant == 3) {
1460 // Passport form factor
1461 PrintAndLogEx(SUCCESS, "Nationality.............. " _YELLOW_("%.3s"), mrz + 44 + 10);
1462 emrtd_print_name(mrz, 5, 38, false);
1463 emrtd_print_document_number(mrz, 44);
1464 emrtd_print_dob(mrz, 44 + 13, false, true);
1465 emrtd_print_legal_sex(&mrz[44 + 20]);
1466 emrtd_print_expiry(mrz, 44 + 21);
1467 emrtd_print_optional_elements(mrz, 44 + 28, 14, true);
1469 // Calculate and verify composite check digit
1470 char composite_check_data[50] = { 0x00 };
1471 memcpy(composite_check_data, mrz + 44, 10);
1472 memcpy(composite_check_data + 10, mrz + 44 + 13, 7);
1473 memcpy(composite_check_data + 17, mrz + 44 + 21, 23);
1475 if (emrtd_compare_check_digit(composite_check_data, 39, mrz[87]) == false) {
1476 PrintAndLogEx(SUCCESS, _RED_("Composite check digit is invalid."));
1478 } else if (td_variant == 1) {
1479 // ID form factor
1480 PrintAndLogEx(SUCCESS, "Nationality.............. " _YELLOW_("%.3s"), mrz + 30 + 15);
1481 emrtd_print_name(mrz, 60, 30, false);
1482 emrtd_print_document_number(mrz, 5);
1483 emrtd_print_dob(mrz, 30, false, true);
1484 emrtd_print_legal_sex(&mrz[30 + 7]);
1485 emrtd_print_expiry(mrz, 30 + 8);
1486 emrtd_print_optional_elements(mrz, 15, 15, false);
1487 emrtd_print_optional_elements(mrz, 30 + 18, 11, false);
1489 // Calculate and verify composite check digit
1490 if (emrtd_compare_check_digit(mrz, 59, mrz[59]) == false) {
1491 PrintAndLogEx(SUCCESS, _RED_("Composite check digit is invalid."));
1495 return PM3_SUCCESS;
1498 static int emrtd_print_ef_dg2_info(uint8_t *data, size_t datalen) {
1500 int offset = 0;
1502 // This is a hacky impl that just looks for the image header. I'll improve it eventually.
1503 // based on mrpkey.py
1504 // Note: Doing datalen - 6 to account for the longest data we're checking.
1505 // Checks first byte before the rest to reduce overhead
1506 for (offset = 0; offset < datalen - 6; offset++) {
1507 if ((data[offset] == 0xFF && memcmp(jpeg_header, data + offset, 4) == 0) ||
1508 (data[offset] == 0x00 && memcmp(jpeg2k_header, data + offset, 6) == 0)) {
1509 datalen = datalen - offset;
1510 break;
1514 // If we didn't get any data, return false.
1515 if (datalen == 0) {
1516 return PM3_ESOFT;
1519 ShowPictureWindow(data + offset, datalen);
1520 return PM3_SUCCESS;
1523 static int emrtd_print_ef_dg5_info(uint8_t *data, size_t datalen) {
1525 int offset = 0;
1527 // This is a hacky impl that just looks for the image header. I'll improve it eventually.
1528 // based on mrpkey.py
1529 // Note: Doing datalen - 6 to account for the longest data we're checking.
1530 // Checks first byte before the rest to reduce overhead
1531 for (offset = 0; offset < datalen - 6; offset++) {
1532 if ((data[offset] == 0xFF && memcmp(jpeg_header, data + offset, 4) == 0) ||
1533 (data[offset] == 0x00 && memcmp(jpeg2k_header, data + offset, 6) == 0)) {
1534 datalen = datalen - offset;
1535 break;
1539 // If we didn't get any data, return false.
1540 if (datalen == 0) {
1541 return PM3_ESOFT;
1544 ShowPictureWindow(data + offset, datalen);
1545 return PM3_SUCCESS;
1548 static int emrtd_print_ef_dg7_info(uint8_t *data, size_t datalen) {
1550 int offset = 0;
1552 // This is a hacky impl that just looks for the image header. I'll improve it eventually.
1553 // based on mrpkey.py
1554 // Note: Doing datalen - 6 to account for the longest data we're checking.
1555 // Checks first byte before the rest to reduce overhead
1556 for (offset = 0; offset < datalen - 6; offset++) {
1557 if ((data[offset] == 0xFF && memcmp(jpeg_header, data + offset, 4) == 0) ||
1558 (data[offset] == 0x00 && memcmp(jpeg2k_header, data + offset, 6) == 0)) {
1559 datalen = datalen - offset;
1560 break;
1564 // If we didn't get any data, return false.
1565 if (datalen == 0) {
1566 return PM3_ESOFT;
1569 ShowPictureWindow(data + offset, datalen);
1570 return PM3_SUCCESS;
1573 static int emrtd_print_ef_dg11_info(uint8_t *data, size_t datalen) {
1574 uint8_t taglist[100] = { 0x00 };
1575 size_t taglistlen = 0;
1576 uint8_t tagdata[1000] = { 0x00 };
1577 size_t tagdatalen = 0;
1579 PrintAndLogEx(NORMAL, "");
1580 PrintAndLogEx(INFO, "------------------------ " _CYAN_("EF_DG11") " -----------------------");
1582 if (emrtd_lds_get_data_by_tag(data, datalen, taglist, &taglistlen, 0x5c, 0x00, false, true, 0) == false) {
1583 PrintAndLogEx(ERR, "Failed to read file list from EF_DG11.");
1584 return PM3_ESOFT;
1587 for (int i = 0; i < taglistlen; i++) {
1588 bool res = emrtd_lds_get_data_by_tag(data, datalen, tagdata, &tagdatalen, taglist[i], taglist[i + 1], taglist[i] == 0x5f, true, 0);
1589 (void)res;
1590 // Don't bother with empty tags
1591 if (tagdatalen == 0) {
1592 continue;
1594 // Special behavior for two char tags
1595 if (taglist[i] == 0x5f) {
1596 switch (taglist[i + 1]) {
1597 case 0x0e:
1598 emrtd_print_name((char *) tagdata, 0, tagdatalen, true);
1599 break;
1600 case 0x0f:
1601 emrtd_print_name((char *) tagdata, 0, tagdatalen, false);
1602 break;
1603 case 0x10:
1604 PrintAndLogEx(SUCCESS, "Personal Number.......... " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
1605 break;
1606 case 0x11:
1607 // TODO: acc for < separation
1608 PrintAndLogEx(SUCCESS, "Place of Birth........... " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
1609 break;
1610 case 0x42:
1611 // TODO: acc for < separation
1612 PrintAndLogEx(SUCCESS, "Permanent Address........ " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
1613 break;
1614 case 0x12:
1615 PrintAndLogEx(SUCCESS, "Telephone................ " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
1616 break;
1617 case 0x13:
1618 PrintAndLogEx(SUCCESS, "Profession............... " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
1619 break;
1620 case 0x14:
1621 PrintAndLogEx(SUCCESS, "Title.................... " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
1622 break;
1623 case 0x15:
1624 PrintAndLogEx(SUCCESS, "Personal Summary......... " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
1625 break;
1626 case 0x16:
1627 saveFile("ProofOfCitizenship", tagdata[0] == 0xFF ? ".jpg" : ".jp2", tagdata, tagdatalen);
1628 break;
1629 case 0x17:
1630 // TODO: acc for < separation
1631 PrintAndLogEx(SUCCESS, "Other valid TDs nums..... " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
1632 break;
1633 case 0x18:
1634 PrintAndLogEx(SUCCESS, "Custody Information...... " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
1635 break;
1636 case 0x2b:
1637 emrtd_print_dob((char *) tagdata, 0, true, tagdatalen != 4);
1638 break;
1639 default:
1640 PrintAndLogEx(SUCCESS, "Unknown Field %02X%02X....... %s", taglist[i], taglist[i + 1], sprint_hex_inrow(tagdata, tagdatalen));
1641 break;
1644 i += 1;
1645 } else {
1646 // TODO: Account for A0
1647 PrintAndLogEx(SUCCESS, "Unknown Field %02X......... %s", taglist[i], sprint_hex_inrow(tagdata, tagdatalen));
1650 return PM3_SUCCESS;
1653 static int emrtd_print_ef_dg12_info(uint8_t *data, size_t datalen) {
1654 uint8_t taglist[100] = { 0x00 };
1655 size_t taglistlen = 0;
1656 uint8_t tagdata[1000] = { 0x00 };
1657 size_t tagdatalen = 0;
1659 PrintAndLogEx(NORMAL, "");
1660 PrintAndLogEx(INFO, "------------------------ " _CYAN_("EF_DG12") " -----------------------");
1662 if (emrtd_lds_get_data_by_tag(data, datalen, taglist, &taglistlen, 0x5c, 0x00, false, true, 0) == false) {
1663 PrintAndLogEx(ERR, "Failed to read file list from EF_DG12.");
1664 return PM3_ESOFT;
1667 for (int i = 0; i < taglistlen; i++) {
1668 bool res = emrtd_lds_get_data_by_tag(data, datalen, tagdata, &tagdatalen, taglist[i], taglist[i + 1], taglist[i] == 0x5f, true, 0);
1669 (void)res;
1670 // Don't bother with empty tags
1671 if (tagdatalen == 0) {
1672 continue;
1674 // Special behavior for two char tags
1675 if (taglist[i] == 0x5f) {
1676 // Several things here are longer than the rest but I can't think of a way to shorten them
1677 // ...and I doubt many states are using them.
1678 switch (taglist[i + 1]) {
1679 case 0x19:
1680 PrintAndLogEx(SUCCESS, "Issuing Authority........ " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
1681 break;
1682 case 0x26:
1683 emrtd_print_issuance((char *) tagdata, tagdatalen != 4);
1684 break;
1685 case 0x1b:
1686 PrintAndLogEx(SUCCESS, "Endorsements & Observations... " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
1687 break;
1688 case 0x1c:
1689 PrintAndLogEx(SUCCESS, "Tax/Exit Requirements.... " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
1690 break;
1691 case 0x1d:
1692 saveFile("FrontOfDocument", tagdata[0] == 0xFF ? ".jpg" : ".jp2", tagdata, tagdatalen);
1693 break;
1694 case 0x1e:
1695 saveFile("BackOfDocument", tagdata[0] == 0xFF ? ".jpg" : ".jp2", tagdata, tagdatalen);
1696 break;
1697 case 0x55:
1698 emrtd_print_personalization_timestamp(tagdata, tagdatalen);
1699 break;
1700 case 0x56:
1701 PrintAndLogEx(SUCCESS, "Serial of Personalization System... " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
1702 break;
1703 case 0x85:
1704 emrtd_print_unknown_timestamp_5f85(tagdata, tagdatalen);
1705 break;
1706 default:
1707 PrintAndLogEx(SUCCESS, "Unknown Field %02X%02X....... %s", taglist[i], taglist[i + 1], sprint_hex_inrow(tagdata, tagdatalen));
1708 break;
1711 i += 1;
1712 } else {
1713 // TODO: Account for A0
1714 PrintAndLogEx(SUCCESS, "Unknown Field %02X......... %s", taglist[i], sprint_hex_inrow(tagdata, tagdatalen));
1717 return PM3_SUCCESS;
1720 static int emrtd_ef_sod_extract_signatures(uint8_t *data, size_t datalen, uint8_t *dataout, size_t *dataoutlen) {
1721 uint8_t top[EMRTD_MAX_FILE_SIZE] = { 0x00 };
1722 uint8_t signeddata[EMRTD_MAX_FILE_SIZE] = { 0x00 };
1723 uint8_t emrtdsigcontainer[EMRTD_MAX_FILE_SIZE] = { 0x00 };
1724 uint8_t emrtdsig[EMRTD_MAX_FILE_SIZE] = { 0x00 };
1725 uint8_t emrtdsigtext[EMRTD_MAX_FILE_SIZE] = { 0x00 };
1726 size_t toplen, signeddatalen, emrtdsigcontainerlen, emrtdsiglen, emrtdsigtextlen = 0;
1728 if (emrtd_lds_get_data_by_tag(data, datalen, top, &toplen, 0x30, 0x00, false, true, 0) == false) {
1729 PrintAndLogEx(ERR, "Failed to read top from EF_SOD.");
1730 return false;
1733 PrintAndLogEx(DEBUG, "top: %s.", sprint_hex_inrow(top, toplen));
1735 if (emrtd_lds_get_data_by_tag(top, toplen, signeddata, &signeddatalen, 0xA0, 0x00, false, false, 0) == false) {
1736 PrintAndLogEx(ERR, "Failed to read signedData from EF_SOD.");
1737 return false;
1740 PrintAndLogEx(DEBUG, "signeddata: %s.", sprint_hex_inrow(signeddata, signeddatalen));
1742 // Do true on reading into the tag as it's a "sequence"
1743 if (emrtd_lds_get_data_by_tag(signeddata, signeddatalen, emrtdsigcontainer, &emrtdsigcontainerlen, 0x30, 0x00, false, true, 0) == false) {
1744 PrintAndLogEx(ERR, "Failed to read eMRTDSignature container from EF_SOD.");
1745 return false;
1748 PrintAndLogEx(DEBUG, "emrtdsigcontainer: %s.", sprint_hex_inrow(emrtdsigcontainer, emrtdsigcontainerlen));
1750 if (emrtd_lds_get_data_by_tag(emrtdsigcontainer, emrtdsigcontainerlen, emrtdsig, &emrtdsiglen, 0xA0, 0x00, false, false, 0) == false) {
1751 PrintAndLogEx(ERR, "Failed to read eMRTDSignature from EF_SOD.");
1752 return false;
1755 PrintAndLogEx(DEBUG, "emrtdsig: %s.", sprint_hex_inrow(emrtdsig, emrtdsiglen));
1757 // TODO: Not doing memcpy here, it didn't work, fix it somehow
1758 if (emrtd_lds_get_data_by_tag(emrtdsig, emrtdsiglen, emrtdsigtext, &emrtdsigtextlen, 0x04, 0x00, false, false, 0) == false) {
1759 PrintAndLogEx(ERR, "Failed to read eMRTDSignature (text) from EF_SOD.");
1760 return false;
1762 memcpy(dataout, emrtdsigtext, emrtdsigtextlen);
1763 *dataoutlen = emrtdsigtextlen;
1764 return PM3_SUCCESS;
1767 static int emrtd_parse_ef_sod_hash_algo(uint8_t *data, size_t datalen, int *hashalgo) {
1768 uint8_t hashalgoset[64] = { 0x00 };
1769 size_t hashalgosetlen = 0;
1771 // We'll return hash algo -1 if we can't find anything
1772 *hashalgo = -1;
1774 if (emrtd_lds_get_data_by_tag(data, datalen, hashalgoset, &hashalgosetlen, 0x30, 0x00, false, true, 0) == false) {
1775 PrintAndLogEx(ERR, "Failed to read hash algo set from EF_SOD.");
1776 return false;
1779 PrintAndLogEx(DEBUG, "hash algo set: %s", sprint_hex_inrow(hashalgoset, hashalgosetlen));
1781 // If last two bytes are 05 00, ignore them.
1782 // https://wf.lavatech.top/ave-but-random/emrtd-data-quirks#EF_SOD
1783 if (hashalgoset[hashalgosetlen - 2] == 0x05 && hashalgoset[hashalgosetlen - 1] == 0x00) {
1784 hashalgosetlen -= 2;
1787 for (int hashi = 0; hashalg_table[hashi].name != NULL; hashi++) {
1788 PrintAndLogEx(DEBUG, "trying: %s", hashalg_table[hashi].name);
1789 // We're only interested in checking if the length matches to avoid memory shenanigans
1790 if (hashalg_table[hashi].descriptorlen != hashalgosetlen) {
1791 PrintAndLogEx(DEBUG, "len mismatch: %zu", hashalgosetlen);
1792 continue;
1795 if (memcmp(hashalg_table[hashi].descriptor, hashalgoset, hashalgosetlen) == 0) {
1796 *hashalgo = hashi;
1797 return PM3_SUCCESS;
1801 PrintAndLogEx(ERR, "Failed to parse hash list (Unknown algo: %s). Hash verification won't be available.", sprint_hex_inrow(hashalgoset, hashalgosetlen));
1802 return PM3_ESOFT;
1805 static int emrtd_parse_ef_sod_hashes(uint8_t *data, size_t datalen, uint8_t *hashes, int *hashalgo) {
1806 uint8_t emrtdsig[EMRTD_MAX_FILE_SIZE] = { 0x00 };
1807 uint8_t hashlist[EMRTD_MAX_FILE_SIZE] = { 0x00 };
1808 uint8_t hash[64] = { 0x00 };
1809 size_t hashlen = 0;
1811 uint8_t hashidstr[4] = { 0x00 };
1812 size_t hashidstrlen = 0;
1814 size_t emrtdsiglen = 0;
1815 size_t hashlistlen = 0;
1816 size_t offset = 0;
1818 if (emrtd_ef_sod_extract_signatures(data, datalen, emrtdsig, &emrtdsiglen) != PM3_SUCCESS) {
1819 return false;
1822 PrintAndLogEx(DEBUG, "hash data... %s", sprint_hex_inrow(emrtdsig, emrtdsiglen));
1824 emrtd_parse_ef_sod_hash_algo(emrtdsig, emrtdsiglen, hashalgo);
1826 if (emrtd_lds_get_data_by_tag(emrtdsig, emrtdsiglen, hashlist, &hashlistlen, 0x30, 0x00, false, true, 1) == false) {
1827 PrintAndLogEx(ERR, "Failed to read hash list from EF_SOD");
1828 return false;
1831 PrintAndLogEx(DEBUG, "hash list... %s", sprint_hex_inrow(hashlist, hashlistlen));
1833 while (offset < hashlistlen) {
1834 // Get the length of the element
1835 int e_datalen = emrtd_get_asn1_data_length(hashlist + offset, hashlistlen - offset, 1);
1837 // Get the length of the element's length
1838 int e_fieldlen = emrtd_get_asn1_field_length(hashlist + offset, hashlistlen - offset, 1);
1840 switch (hashlist[offset]) {
1841 case 0x30: {
1842 // iceman: if these two calls fails, feels like we should have a better check in place
1843 bool res = emrtd_lds_get_data_by_tag(hashlist + offset + e_fieldlen + 1, e_datalen, hashidstr, &hashidstrlen, 0x02, 0x00, false, false, 0);
1844 (void)res;
1845 res = emrtd_lds_get_data_by_tag(hashlist + offset + e_fieldlen + 1, e_datalen, hash, &hashlen, 0x04, 0x00, false, false, 0);
1846 (void)res;
1847 if (hashlen <= 64) {
1848 memcpy(hashes + (hashidstr[0] * 64), hash, hashlen);
1849 } else {
1850 PrintAndLogEx(ERR, "error (emrtd_parse_ef_sod_hashes) hashlen out-of-bounds");
1852 break;
1855 // + 1 for length of ID
1856 offset += 1 + e_datalen + e_fieldlen;
1859 return PM3_SUCCESS;
1862 static int emrtd_print_ef_sod_info(uint8_t *dg_hashes_calc, uint8_t *dg_hashes_sod, int hash_algo, bool fastdump) {
1863 PrintAndLogEx(NORMAL, "");
1864 PrintAndLogEx(INFO, "------------------------ " _CYAN_("EF_SOD") " ------------------------");
1865 PrintAndLogEx(INFO, "Document Security Object, ");
1866 PrintAndLogEx(INFO, "contains the digital signatures of the passport data");
1867 PrintAndLogEx(INFO, "");
1869 if (hash_algo == -1) {
1870 PrintAndLogEx(SUCCESS, "Hash algorithm... " _YELLOW_("Unknown"));
1871 } else {
1873 PrintAndLogEx(SUCCESS, "Hash algorithm... " _YELLOW_("%s"), hashalg_table[hash_algo].name);
1875 uint8_t all_zeroes[64] = { 0x00 };
1877 for (int i = 1; i <= 16; i++) {
1879 bool calc_all_zero = (memcmp(dg_hashes_calc + (i * 64), all_zeroes, hashalg_table[hash_algo].hashlen) == 0);
1880 bool sod_all_zero = (memcmp(dg_hashes_sod + (i * 64), all_zeroes, hashalg_table[hash_algo].hashlen) == 0);
1881 bool hash_matches = (memcmp(dg_hashes_sod + (i * 64), dg_hashes_calc + (i * 64), hashalg_table[hash_algo].hashlen) == 0);
1883 // Ignore files we don't haven't read and lack hashes to
1884 if (calc_all_zero == true && sod_all_zero == true) {
1885 continue;
1888 // silly padding thingy
1889 int n = 40 - strlen(dg_table[i].desc);
1891 if (calc_all_zero == true) {
1893 if (fastdump && !dg_table[i].fastdump && !dg_table[i].pace && !dg_table[i].eac) {
1894 PrintAndLogEx(SUCCESS, "EF_DG%2i %s %*.*s File was skipped, but is in EF_SOD", i, dg_table[i].desc, n, n, pad);
1895 } else {
1896 PrintAndLogEx(SUCCESS, "EF_DG%2i %s %*.*s File couldn't be read, but is in EF_SOD", i, dg_table[i].desc, n, n, pad);
1899 } else if (sod_all_zero == true) {
1900 PrintAndLogEx(SUCCESS, "EF_DG%2i %s %*.*s " _RED_("File is not in EF_SOD"), i, dg_table[i].desc, n, n, pad);
1901 } else if (hash_matches == false) {
1902 PrintAndLogEx(SUCCESS, "EF_DG%2i %s %*.*s " _RED_("Invalid"), i, dg_table[i].desc, n, n, pad);
1903 } else {
1904 PrintAndLogEx(SUCCESS, "EF_DG%2i %s %*.*s " _GREEN_("Valid"), i, dg_table[i].desc, n, n, pad);
1909 return PM3_SUCCESS;
1912 static int emrtd_print_ef_cardaccess_info(uint8_t *data, size_t datalen) {
1913 uint8_t dataset[100] = { 0x00 };
1914 size_t datasetlen = 0;
1915 uint8_t datafromtag[100] = { 0x00 };
1916 size_t datafromtaglen = 0;
1917 uint8_t parsednum = 0;
1919 PrintAndLogEx(NORMAL, "");
1920 PrintAndLogEx(INFO, "--------------------- " _CYAN_("EF_CardAccess") " --------------------");
1922 if (emrtd_lds_get_data_by_tag(data, datalen, dataset, &datasetlen, 0x30, 0x00, false, true, 0) == false) {
1923 PrintAndLogEx(ERR, "Failed to read set from EF_CardAccess.");
1924 return PM3_ESOFT;
1927 // Get PACE version
1928 if (emrtd_lds_get_data_by_tag(dataset, datasetlen, datafromtag, &datafromtaglen, 0x02, 0x00, false, false, 0) == false) {
1929 PrintAndLogEx(ERR, "Failed to read PACE version from EF_CardAccess.");
1930 return PM3_ESOFT;
1932 // TODO: hack!!!
1933 memcpy(&parsednum, datafromtag, datafromtaglen);
1934 PrintAndLogEx(SUCCESS, "PACE version............. " _YELLOW_("%i"), parsednum);
1936 // Get PACE algorithm
1937 if (emrtd_lds_get_data_by_tag(dataset, datasetlen, datafromtag, &datafromtaglen, 0x06, 0x00, false, false, 0) == false) {
1938 PrintAndLogEx(ERR, "Failed to read PACE algorithm from EF_CardAccess.");
1939 return PM3_ESOFT;
1942 for (int pacei = 0; pacealg_table[pacei].name != NULL; pacei++) {
1943 PrintAndLogEx(DEBUG, "Trying: %s", pacealg_table[pacei].name);
1945 if (memcmp(pacealg_table[pacei].descriptor, datafromtag, datafromtaglen) == 0) {
1946 PrintAndLogEx(SUCCESS, "PACE algorithm........... " _YELLOW_("%s"), pacealg_table[pacei].name);
1950 // Get PACE parameter ID
1951 if (emrtd_lds_get_data_by_tag(dataset, datasetlen, datafromtag, &datafromtaglen, 0x02, 0x00, false, false, 1) == false) {
1952 PrintAndLogEx(ERR, "Failed to read PACE parameter ID from EF_CardAccess.");
1953 return PM3_ESOFT;
1956 // TODO: hack!!!
1957 memcpy(&parsednum, datafromtag, datafromtaglen);
1958 for (int pacepari = 0; pacesdp_table[pacepari].id != 32; pacepari++) {
1959 PrintAndLogEx(DEBUG, "Trying: %s", pacesdp_table[pacepari].name);
1961 if (pacesdp_table[pacepari].id == parsednum) {
1962 PrintAndLogEx(SUCCESS, "PACE parameter........... " _YELLOW_("%s"), pacesdp_table[pacepari].name);
1964 // TODO: account for RFU
1967 return PM3_SUCCESS;
1970 int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available, bool only_fast) {
1971 uint8_t response[EMRTD_MAX_FILE_SIZE] = { 0x00 };
1972 size_t resplen = 0;
1973 uint8_t ssc[8] = { 0x00 };
1974 uint8_t ks_enc[16] = { 0x00 };
1975 uint8_t ks_mac[16] = { 0x00 };
1976 bool BAC = false;
1977 bool PACE_available = true;
1979 // Select the eMRTD
1980 if (emrtd_connect() == false) {
1981 DropField();
1982 return PM3_ESOFT;
1984 bool use14b = (GetISODEPState() == ISODEP_NFCB);
1986 // Read EF_CardAccess
1987 if (emrtd_select_and_read(response, &resplen, dg_table[EF_CardAccess].fileid, ks_enc, ks_mac, ssc, BAC) == false) {
1988 PACE_available = false;
1989 PrintAndLogEx(HINT, "The error above this is normal. It just means that your eMRTD lacks PACE.");
1992 // Select and authenticate with the eMRTD
1993 bool auth_result = emrtd_do_auth(documentnumber, dob, expiry, BAC_available, &BAC, ssc, ks_enc, ks_mac);
1995 PrintAndLogEx(NORMAL, "");
1996 PrintAndLogEx(INFO, "---------------------- " _CYAN_("Basic Info") " ----------------------");
1997 PrintAndLogEx(SUCCESS, "Communication standard... %s", use14b ? _YELLOW_("ISO/IEC 14443(B)") : _YELLOW_("ISO/IEC 14443(A)"));
1998 PrintAndLogEx(SUCCESS, "Authentication........... %s", BAC ? _GREEN_("Enforced") : _RED_("Not enforced"));
1999 PrintAndLogEx(SUCCESS, "PACE..................... %s", PACE_available ? _GREEN_("Available") : _YELLOW_("Not available"));
2000 PrintAndLogEx(SUCCESS, "Authentication result.... %s", auth_result ? _GREEN_("Successful") : _RED_("Failed"));
2002 if (PACE_available) {
2003 emrtd_print_ef_cardaccess_info(response, resplen);
2006 if (auth_result == false) {
2007 DropField();
2008 return PM3_ESOFT;
2011 // Read EF_COM to get file list
2012 if (emrtd_select_and_read(response, &resplen, dg_table[EF_COM].fileid, ks_enc, ks_mac, ssc, BAC) == false) {
2013 PrintAndLogEx(ERR, "Failed to read EF_COM");
2014 DropField();
2015 return PM3_ESOFT;
2018 int res = emrtd_print_ef_com_info(response, resplen);
2019 if (res != PM3_SUCCESS) {
2020 DropField();
2021 return res;
2024 uint8_t filelist[50];
2025 size_t filelistlen = 0;
2027 if (emrtd_lds_get_data_by_tag(response, resplen, filelist, &filelistlen, 0x5c, 0x00, false, true, 0) == false) {
2028 PrintAndLogEx(ERR, "Failed to read file list from EF_COM.");
2029 DropField();
2030 return PM3_ESOFT;
2033 // Grab the hash list from EF_SOD
2034 uint8_t dg_hashes_sod[17][64] = { { 0 } };
2035 uint8_t dg_hashes_calc[17][64] = { { 0 } };
2036 int hash_algo = 0;
2038 if (!emrtd_select_and_read(response, &resplen, dg_table[EF_SOD].fileid, ks_enc, ks_mac, ssc, BAC)) {
2039 PrintAndLogEx(ERR, "Failed to read EF_SOD.");
2040 DropField();
2041 return PM3_ESOFT;
2044 res = emrtd_parse_ef_sod_hashes(response, resplen, *dg_hashes_sod, &hash_algo);
2045 if (res != PM3_SUCCESS) {
2046 PrintAndLogEx(ERR, "Failed to read hash list from EF_SOD. Hash checks will fail");
2049 // Dump all files in the file list
2050 for (int i = 0; i < filelistlen; i++) {
2052 emrtd_dg_t *dg = emrtd_tag_to_dg(filelist[i]);
2053 if (dg == NULL) {
2054 PrintAndLogEx(INFO, "File tag not found, skipping... %02X", filelist[i]);
2055 continue;
2058 if (((dg->fastdump && only_fast) || !only_fast) && !dg->pace && !dg->eac) {
2059 if (emrtd_select_and_read(response, &resplen, dg->fileid, ks_enc, ks_mac, ssc, BAC)) {
2060 if (dg->parser != NULL)
2061 dg->parser(response, resplen);
2063 PrintAndLogEx(DEBUG, "EF_DG%i hash algo... %i", dg->dgnum, hash_algo);
2064 // Check file hash
2065 if (hash_algo != -1) {
2066 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));
2067 hashalg_table[hash_algo].hasher(response, resplen, dg_hashes_calc[dg->dgnum]);
2068 PrintAndLogEx(DEBUG, "EF_DG%i hash calc........ %s", dg->dgnum, sprint_hex_inrow(dg_hashes_calc[dg->dgnum], hashalg_table[hash_algo].hashlen));
2073 DropField();
2075 emrtd_print_ef_sod_info(*dg_hashes_calc, *dg_hashes_sod, hash_algo, true);
2077 return PM3_SUCCESS;
2080 int infoHF_EMRTD_offline(const char *path) {
2081 uint8_t *data;
2082 size_t datalen = 0;
2083 char *filepath = calloc(strlen(path) + 100, sizeof(char));
2084 if (filepath == NULL) {
2085 return PM3_EMALLOC;
2087 strcpy(filepath, path);
2088 strncat(filepath, PATHSEP, 2);
2089 strcat(filepath, dg_table[EF_COM].filename);
2091 if ((loadFile_safeEx(filepath, ".BIN", (void **)&data, (size_t *)&datalen, false) != PM3_SUCCESS) &&
2092 (loadFile_safeEx(filepath, ".bin", (void **)&data, (size_t *)&datalen, false) != PM3_SUCCESS)) {
2093 PrintAndLogEx(ERR, "Failed to read EF_COM");
2094 free(filepath);
2095 return PM3_ESOFT;
2098 int res = emrtd_print_ef_com_info(data, datalen);
2099 if (res != PM3_SUCCESS) {
2100 free(data);
2101 free(filepath);
2102 return res;
2105 uint8_t filelist[50];
2106 size_t filelistlen = 0;
2107 res = emrtd_lds_get_data_by_tag(data, datalen, filelist, &filelistlen, 0x5c, 0x00, false, true, 0);
2108 if (res == false) {
2109 PrintAndLogEx(ERR, "Failed to read file list from EF_COM");
2110 free(data);
2111 free(filepath);
2112 return PM3_ESOFT;
2114 free(data);
2116 // Grab the hash list
2117 uint8_t dg_hashes_sod[17][64] = { { 0 } };
2118 uint8_t dg_hashes_calc[17][64] = { { 0 } };
2119 int hash_algo = 0;
2121 strcpy(filepath, path);
2122 strncat(filepath, PATHSEP, 2);
2123 strcat(filepath, dg_table[EF_CardAccess].filename);
2125 if ((loadFile_safeEx(filepath, ".BIN", (void **)&data, (size_t *)&datalen, false) == PM3_SUCCESS) ||
2126 (loadFile_safeEx(filepath, ".bin", (void **)&data, (size_t *)&datalen, false) == PM3_SUCCESS)) {
2127 emrtd_print_ef_cardaccess_info(data, datalen);
2128 free(data);
2129 } else {
2130 PrintAndLogEx(HINT, "The error above this is normal. It just means that your eMRTD lacks PACE");
2133 strcpy(filepath, path);
2134 strncat(filepath, PATHSEP, 2);
2135 strcat(filepath, dg_table[EF_SOD].filename);
2137 if ((loadFile_safeEx(filepath, ".BIN", (void **)&data, (size_t *)&datalen, false) != PM3_SUCCESS) &&
2138 (loadFile_safeEx(filepath, ".bin", (void **)&data, (size_t *)&datalen, false) != PM3_SUCCESS)) {
2139 PrintAndLogEx(ERR, "Failed to read EF_SOD");
2140 free(filepath);
2141 return PM3_ESOFT;
2144 // coverity scan CID 395630,
2145 if (data == NULL) {
2146 free(filepath);
2147 return PM3_ESOFT;
2150 res = emrtd_parse_ef_sod_hashes(data, datalen, *dg_hashes_sod, &hash_algo);
2151 if (res != PM3_SUCCESS) {
2152 PrintAndLogEx(ERR, "Failed to read hash list from EF_SOD. Hash checks will fail");
2154 free(data);
2156 // Read files in the file list
2157 for (int i = 0; i < filelistlen; i++) {
2158 emrtd_dg_t *dg = emrtd_tag_to_dg(filelist[i]);
2159 if (dg == NULL) {
2160 PrintAndLogEx(INFO, "File tag not found, skipping... %02X", filelist[i]);
2161 continue;
2163 if (!dg->pace && !dg->eac) {
2164 strcpy(filepath, path);
2165 strncat(filepath, PATHSEP, 2);
2166 strcat(filepath, dg->filename);
2167 if ((loadFile_safeEx(filepath, ".BIN", (void **)&data, (size_t *)&datalen, false) == PM3_SUCCESS) ||
2168 (loadFile_safeEx(filepath, ".bin", (void **)&data, (size_t *)&datalen, false) == PM3_SUCCESS)) {
2169 // we won't halt on parsing errors
2170 if (dg->parser != NULL) {
2171 dg->parser(data, datalen);
2174 PrintAndLogEx(DEBUG, "EF_DG%i hash algo... %i", dg->dgnum, hash_algo);
2175 // Check file hash
2176 if (hash_algo != -1) {
2177 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));
2178 hashalg_table[hash_algo].hasher(data, datalen, dg_hashes_calc[dg->dgnum]);
2179 PrintAndLogEx(DEBUG, "EF_DG%i hash calc........ %s", dg->dgnum, sprint_hex_inrow(dg_hashes_calc[dg->dgnum], hashalg_table[hash_algo].hashlen));
2181 free(data);
2185 free(filepath);
2187 emrtd_print_ef_sod_info(*dg_hashes_calc, *dg_hashes_sod, hash_algo, false);
2189 return PM3_SUCCESS;
2192 static bool validate_date(uint8_t *data, int datalen) {
2193 // Date has to be 6 chars
2194 if (datalen != 6) {
2195 return false;
2198 // Check for valid date and month numbers
2199 char temp[4] = { 0x00 };
2200 memcpy(temp, data + 2, 2);
2201 int month = (int) strtol(temp, NULL, 10);
2202 memcpy(temp, data + 4, 2);
2203 int day = (int) strtol(temp, NULL, 10);
2205 return !(day <= 0 || day > 31 || month <= 0 || month > 12);
2208 static int CmdHFeMRTDDump(const char *Cmd) {
2209 CLIParserContext *ctx;
2210 CLIParserInit(&ctx, "hf emrtd dump",
2211 "Dump all files on an eMRTD",
2212 "hf emrtd dump\n"
2213 "hf emrtd dump --dir ../dump\n"
2214 "hf emrtd dump -n 123456789 -d 890101 -e 250401"
2217 void *argtable[] = {
2218 arg_param_begin,
2219 arg_str0("n", "doc", "<alphanum>", "document number, up to 9 chars"),
2220 arg_str0("d", "date", "<YYMMDD>", "date of birth in YYMMDD format"),
2221 arg_str0("e", "expiry", "<YYMMDD>", "expiry in YYMMDD format"),
2222 arg_str0("m", "mrz", "<[0-9A-Z<]>", "2nd line of MRZ, 44 chars"),
2223 arg_str0(NULL, "dir", "<str>", "save dump to the given dirpath"),
2224 arg_param_end
2226 CLIExecWithReturn(ctx, Cmd, argtable, true);
2228 uint8_t mrz[45] = { 0x00 };
2229 uint8_t docnum[10] = { 0x00 };
2230 uint8_t dob[7] = { 0x00 };
2231 uint8_t expiry[7] = { 0x00 };
2232 bool BAC = true;
2233 bool error = false;
2234 int slen = 0;
2235 // Go through all args, if even one isn't supplied, mark BAC as unavailable
2236 if (CLIParamStrToBuf(arg_get_str(ctx, 1), docnum, 9, &slen) != 0 || slen == 0) {
2237 BAC = false;
2238 } else {
2239 strn_upper((char *)docnum, slen);
2240 if (slen != 9) {
2241 // Pad to 9 with <
2242 memset(docnum + slen, '<', 9 - slen);
2246 if (CLIParamStrToBuf(arg_get_str(ctx, 2), dob, 6, &slen) != 0 || slen == 0) {
2247 BAC = false;
2248 } else {
2249 if (!validate_date(dob, slen)) {
2250 PrintAndLogEx(ERR, "Date of birth date format is incorrect, cannot continue.");
2251 PrintAndLogEx(HINT, "Use the format YYMMDD.");
2252 error = true;
2256 if (CLIParamStrToBuf(arg_get_str(ctx, 3), expiry, 6, &slen) != 0 || slen == 0) {
2257 BAC = false;
2258 } else {
2259 if (!validate_date(expiry, slen)) {
2260 PrintAndLogEx(ERR, "Expiry date format is incorrect, cannot continue.");
2261 PrintAndLogEx(HINT, "Use the format YYMMDD.");
2262 error = true;
2266 if (CLIParamStrToBuf(arg_get_str(ctx, 4), mrz, 44, &slen) == 0 && slen != 0) {
2267 if (slen != 44) {
2268 PrintAndLogEx(ERR, "MRZ length is incorrect, it should be 44, not %i", slen);
2269 error = true;
2270 } else {
2271 BAC = true;
2272 strn_upper((char *)mrz, slen);
2273 memcpy(docnum, &mrz[0], 9);
2274 memcpy(dob, &mrz[13], 6);
2275 memcpy(expiry, &mrz[21], 6);
2276 // TODO check MRZ checksums?
2277 if (!validate_date(dob, 6)) {
2278 PrintAndLogEx(ERR, "Date of birth date format is incorrect, cannot continue.");
2279 PrintAndLogEx(HINT, "Use the format YYMMDD.");
2280 error = true;
2282 if (!validate_date(expiry, 6)) {
2283 PrintAndLogEx(ERR, "Expiry date format is incorrect, cannot continue.");
2284 PrintAndLogEx(HINT, "Use the format YYMMDD.");
2285 error = true;
2290 uint8_t path[FILENAME_MAX] = { 0x00 };
2291 if (CLIParamStrToBuf(arg_get_str(ctx, 5), path, sizeof(path), &slen) != 0 || slen == 0) {
2292 path[0] = '.';
2295 CLIParserFree(ctx);
2296 if (error) {
2297 return PM3_ESOFT;
2299 bool restore_apdu_logging = GetAPDULogging();
2300 if (g_debugMode >= 2) {
2301 SetAPDULogging(true);
2304 uint64_t t1 = msclock();
2306 int res = dumpHF_EMRTD((char *)docnum, (char *)dob, (char *)expiry, BAC, (const char *)path);
2308 PrintAndLogEx(SUCCESS, "time: %" PRIu64 " seconds\n", (msclock() - t1) / 1000);
2310 SetAPDULogging(restore_apdu_logging);
2311 return res;
2314 static int CmdHFeMRTDInfo(const char *Cmd) {
2315 CLIParserContext *ctx;
2316 CLIParserInit(&ctx, "hf emrtd info",
2317 "Display info about an eMRTD",
2318 "hf emrtd info\n"
2319 "hf emrtd info --dir ../dumps\n"
2320 "hf emrtd info -n 123456789 -d 890101 -e 250401\n"
2321 "hf emrtd info -n 123456789 -d 890101 -e 250401 -i"
2324 void *argtable[] = {
2325 arg_param_begin,
2326 arg_str0("n", "doc", "<alphanum>", "document number, up to 9 chars"),
2327 arg_str0("d", "date", "<YYMMDD>", "date of birth in YYMMDD format"),
2328 arg_str0("e", "expiry", "<YYMMDD>", "expiry in YYMMDD format"),
2329 arg_str0("m", "mrz", "<[0-9A-Z<]>", "2nd line of MRZ, 44 chars (passports only)"),
2330 arg_str0(NULL, "dir", "<str>", "display info from offline dump stored in dirpath"),
2331 arg_lit0("i", "images", "show images"),
2332 arg_param_end
2334 CLIExecWithReturn(ctx, Cmd, argtable, true);
2336 uint8_t mrz[45] = { 0x00 };
2337 uint8_t docnum[10] = { 0x00 };
2338 uint8_t dob[7] = { 0x00 };
2339 uint8_t expiry[7] = { 0x00 };
2340 bool BAC = true;
2341 bool error = false;
2342 int slen = 0;
2343 // Go through all args, if even one isn't supplied, mark BAC as unavailable
2344 if (CLIParamStrToBuf(arg_get_str(ctx, 1), docnum, 9, &slen) != 0 || slen == 0) {
2345 BAC = false;
2346 } else {
2347 strn_upper((char *)docnum, slen);
2348 if (slen != 9) {
2349 memset(docnum + slen, '<', 9 - slen);
2353 if (CLIParamStrToBuf(arg_get_str(ctx, 2), dob, 6, &slen) != 0 || slen == 0) {
2354 BAC = false;
2355 } else {
2356 if (!validate_date(dob, slen)) {
2357 PrintAndLogEx(ERR, "Date of birth date format is incorrect, cannot continue.");
2358 PrintAndLogEx(HINT, "Use the format YYMMDD.");
2359 error = true;
2363 if (CLIParamStrToBuf(arg_get_str(ctx, 3), expiry, 6, &slen) != 0 || slen == 0) {
2364 BAC = false;
2365 } else {
2366 if (!validate_date(expiry, slen)) {
2367 PrintAndLogEx(ERR, "Expiry date format is incorrect, cannot continue.");
2368 PrintAndLogEx(HINT, "Use the format YYMMDD.");
2369 error = true;
2373 if (CLIParamStrToBuf(arg_get_str(ctx, 4), mrz, 44, &slen) == 0 && slen != 0) {
2374 if (slen != 44) {
2375 PrintAndLogEx(ERR, "MRZ length is incorrect, it should be 44, not %i", slen);
2376 error = true;
2377 } else {
2378 BAC = true;
2379 strn_upper((char *)mrz, slen);
2380 memcpy(docnum, &mrz[0], 9);
2381 memcpy(dob, &mrz[13], 6);
2382 memcpy(expiry, &mrz[21], 6);
2383 // TODO check MRZ checksums?
2384 if (!validate_date(dob, 6)) {
2385 PrintAndLogEx(ERR, "Date of birth date format is incorrect, cannot continue.");
2386 PrintAndLogEx(HINT, "Use the format YYMMDD.");
2387 error = true;
2389 if (!validate_date(expiry, 6)) {
2390 PrintAndLogEx(ERR, "Expiry date format is incorrect, cannot continue.");
2391 PrintAndLogEx(HINT, "Use the format YYMMDD.");
2392 error = true;
2396 uint8_t path[FILENAME_MAX] = { 0x00 };
2397 bool is_offline = CLIParamStrToBuf(arg_get_str(ctx, 5), path, sizeof(path), &slen) == 0 && slen > 0;
2398 bool show_images = arg_get_lit(ctx, 6);
2399 CLIParserFree(ctx);
2401 if ((IfPm3Iso14443() == false) && (is_offline == false)) {
2402 PrintAndLogEx(WARNING, "Only offline mode is available");
2403 error = true;
2406 if (error) {
2407 return PM3_ESOFT;
2410 if (is_offline) {
2411 return infoHF_EMRTD_offline((const char *)path);
2412 } else {
2413 bool restore_apdu_logging = GetAPDULogging();
2414 if (g_debugMode >= 2) {
2415 SetAPDULogging(true);
2417 int res = infoHF_EMRTD((char *)docnum, (char *)dob, (char *)expiry, BAC, !show_images);
2418 SetAPDULogging(restore_apdu_logging);
2419 return res;
2423 static int CmdHFeMRTDList(const char *Cmd) {
2424 return CmdTraceListAlias(Cmd, "hf emrtd", "7816");
2427 static command_t CommandTable[] = {
2428 {"help", CmdHelp, AlwaysAvailable, "This help"},
2429 {"dump", CmdHFeMRTDDump, IfPm3Iso14443, "Dump eMRTD files to binary files"},
2430 {"info", CmdHFeMRTDInfo, AlwaysAvailable, "Display info about an eMRTD"},
2431 {"list", CmdHFeMRTDList, AlwaysAvailable, "List ISO 14443A/7816 history"},
2432 {NULL, NULL, NULL, NULL}
2435 static int CmdHelp(const char *Cmd) {
2436 (void)Cmd; // Cmd is not used so far
2437 CmdsHelp(CommandTable);
2438 return PM3_SUCCESS;
2441 int CmdHFeMRTD(const char *Cmd) {
2442 clearCommandBuffer();
2443 return CmdsParse(CommandTable, Cmd);