1 //-----------------------------------------------------------------------------
2 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
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.
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"
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
45 #define EMRTD_P1_SELECT_BY_EF 0x02
46 #define EMRTD_P1_SELECT_BY_NAME 0x04
47 #define EMRTD_P2_PROPRIETARY 0x0C
50 #define EMRTD_AID_MRTD {0xA0, 0x00, 0x00, 0x02, 0x47, 0x10, 0x01}
52 #define EMRTD_KMAC_LEN 16
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
70 -- EF.CardSecurity (1D)
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)
78 -- Visa Records Application DF (AID: A0 00 00 02 47 20 02)
79 - EF.Certificates (1A)
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)
88 -----------------------
89 File names and what they contain
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?)
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
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}},
205 static emrtd_pacesdp_t pacesdp_table
[] = {
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},
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
];
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
];
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
) {
245 int res
= Iso7816ExchangeEx(CC_CONTACTLESS
, activate_field
, keep_field_on
, apdu
, include_le
, le
, dataout
, maxdataoutlen
, dataoutlen
, &sw
);
247 if (res
!= PM3_SUCCESS
) {
251 if (sw
!= ISO7816_OK
) {
252 PrintAndLogEx(DEBUG
, "Command failed (%04x - %s).", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
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};
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};
268 for (int i
= 0; i
< strlen(data
); i
++) {
270 if ('A' <= d
&& d
<= 'Z') {
272 } else if ('a' <= d
&& d
<= 'z') {
274 } else if (d
== '<') {
279 cd
+= value
* mrz_weight
[i
% 3];
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) {
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
295 } else if (lenfield
== 0x81) {
296 int tmp
= (*(datain
+ offset
+ 1));
298 //return ((int) * (datain + offset + 1));
299 } else if (lenfield
== 0x82) {
300 int tmp
= (*(datain
+ offset
+ 1) << 8);
301 tmp
|= *(datain
+ offset
+ 2);
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);
309 //return (((int) * (datain + offset + 1) << 16) | ((int) * (datain + offset + 2)) << 8) | ((int) * (datain + offset + 3));
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) {
320 } else if (lenfield
== 0x81) {
322 } else if (lenfield
== 0x82) {
324 } else if (lenfield
== 0x83) {
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
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
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
377 uint8_t intermediate
[8] = {0x00};
378 uint8_t intermediate_des
[256];
380 uint8_t message
[256];
384 memcpy(k1
, key
+ 8, 8);
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);
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
415 memcpy(data
, seed
, length
);
416 memcpy(data
+ length
, type
, 4);
417 PrintAndLogEx(DEBUG
, "data.............. %s", sprint_hex_inrow(data
, length
+ 4));
420 unsigned char key
[64];
421 sha1hash(data
, length
+ 4, key
);
422 PrintAndLogEx(DEBUG
, "key............... %s", sprint_hex_inrow(key
, length
+ 4));
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
) {
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
468 PrintAndLogEx(DEBUG
, "ssc-a: %s", sprint_hex_inrow(ssc
, 8));
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 };
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 };
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));
533 memcpy(m
, cmd
, cmdlen
);
534 memcpy(m
+ cmdlen
, do87
, (datalen
+ 3));
535 PrintAndLogEx(DEBUG
, "m: %s", sprint_hex_inrow(m
, datalen
+ cmdlen
+ 3));
541 memcpy(n
+ 8, m
, (cmdlen
+ datalen
+ 3));
542 PrintAndLogEx(DEBUG
, "n: %s", sprint_hex_inrow(n
, (cmdlen
+ datalen
+ 11)));
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) {
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
));
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 };
584 memcpy(m
+ 8, do97
, 3);
588 uint8_t n
[19] = { 0x00 };
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
)));
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) {
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) {
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
;
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 };
640 uint8_t tempresponse
[500] = { 0x00 };
641 size_t tempresplen
= 0;
646 if (_emrtd_secure_read_binary_decrypt(kenc
, kmac
, ssc
, offset
, toread
, response
, &resplen
) == false) {
650 if (_emrtd_read_binary(offset
, toread
, response
, sizeof(response
), &resplen
) == 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));
659 uint8_t lnbreak
= 32;
660 PrintAndLogEx(INFO
, "." NOLF
);
661 while (readlen
> 0) {
668 if (_emrtd_secure_read_binary_decrypt(kenc
, kmac
, ssc
, offset
, toread
, tempresponse
, &tempresplen
) == false) {
669 PrintAndLogEx(NORMAL
, "");
673 if (_emrtd_read_binary(offset
, toread
, tempresponse
, sizeof(tempresponse
), &tempresplen
) == false) {
674 PrintAndLogEx(NORMAL
, "");
679 memcpy(response
+ resplen
, tempresponse
, tempresplen
);
682 resplen
+= tempresplen
;
684 PrintAndLogEx(NORMAL
, "." NOLF
);
688 PrintAndLogEx(NORMAL
, "");
689 PrintAndLogEx(INFO
, "." NOLF
);
693 PrintAndLogEx(NORMAL
, "");
695 memcpy(dataout
, &response
, resplen
);
696 *dataoutlen
= resplen
;
700 static int emrtd_lds_determine_tag_length(uint8_t tag
) {
701 if ((tag
== 0x5F) || (tag
== 0x7F)) {
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
) {
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
) {
733 } else if (datainlen
> e_datalen
) {
734 *dataoutlen
= e_datalen
;
735 memcpy(dataout
, datain
+ offset
+ e_idlen
+ e_fieldlen
, e_datalen
);
738 PrintAndLogEx(ERR
, "error (emrtd_lds_get_data_by_tag) e_datalen out-of-bounds");
742 offset
+= e_idlen
+ e_datalen
+ e_fieldlen
;
744 // Return false if we can't find the relevant element
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
) {
750 if (emrtd_secure_select_file_by_ef(ks_enc
, ks_mac
, ssc
, file
) == false) {
751 PrintAndLogEx(ERR
, "Failed to secure select %04X", file
);
755 if (emrtd_select_file_by_ef(file
) == false) {
756 PrintAndLogEx(ERR
, "Failed to select %04X", file
);
761 if (emrtd_read_file(dataout
, dataoutlen
, ks_enc
, ks_mac
, ssc
, use_secure
) == false) {
762 PrintAndLogEx(ERR
, "Failed to read %04X", file
);
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
) {
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
;
787 // If we didn't get any data, return false.
792 char *filepath
= calloc(strlen(path
) + 100, sizeof(char));
793 if (filepath
== NULL
)
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
);
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 };
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) {
815 if (datalen
< EMRTD_MAX_FILE_SIZE
) {
816 char *filepath
= calloc(strlen(path
) + 100, sizeof(char));
817 if (filepath
== NULL
) {
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
);
828 PrintAndLogEx(ERR
, "error (emrtd_dump_ef_dg5) datalen out-of-bounds");
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 };
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) {
843 if (datalen
< EMRTD_MAX_FILE_SIZE
) {
844 char *filepath
= calloc(strlen(path
) + 100, sizeof(char));
845 if (filepath
== NULL
) {
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
);
856 PrintAndLogEx(ERR
, "error (emrtd_dump_ef_dg7) datalen out-of-bounds");
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
) {
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
);
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 };
889 if (emrtd_select_and_read(response
, &resplen
, file
, ks_enc
, ks_mac
, ssc
, use_secure
) == false) {
893 char *filepath
= calloc(strlen(path
) + 100, sizeof(char));
894 if (filepath
== NULL
) {
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
);
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 };
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];
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
);
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));
962 if (emrtd_get_challenge(8, rnd_ic
, sizeof(rnd_ic
), &resplen
) == false) {
963 PrintAndLogEx(ERR
, "Couldn't get challenge.");
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?");
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.");
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));
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.");
1043 if (emrtd_select_file_by_ef(dg_table
[EF_COM
].fileid
) == false) {
1045 PrintAndLogEx(INFO
, "Authentication is enforced");
1046 PrintAndLogEx(INFO
, "Switching to external authentication");
1050 emrtd_select_file_by_ef(dg_table
[EF_DG1
].fileid
);
1053 uint8_t response
[EMRTD_MAX_FILE_SIZE
] = { 0x00 };
1054 if (emrtd_read_file(response
, &resplen
, NULL
, NULL
, NULL
, false) == false) {
1056 PrintAndLogEx(INFO
, "Authentication is enforced");
1057 PrintAndLogEx(INFO
, "Switching to external authentication");
1063 // Do Basic Access Control
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`");
1072 if (emrtd_do_bac(documentnumber
, dob
, expiry
, ssc
, ks_enc
, ks_mac
) == false) {
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 };
1082 uint8_t ssc
[8] = { 0x00 };
1083 uint8_t ks_enc
[EMRTD_KMAC_LEN
] = { 0x00 };
1084 uint8_t ks_mac
[EMRTD_KMAC_LEN
] = { 0x00 };
1088 if (emrtd_connect() == false) {
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) {
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");
1113 char *filepath
= calloc(strlen(path
) + 100, sizeof(char));
1114 if (filepath
== NULL
) {
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
);
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");
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
]);
1144 PrintAndLogEx(INFO
, "File tag not found, skipping... %02X", filelist
[i
]);
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
);
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
1165 , (res
) ? _GREEN_("ok") : _RED_("fail"));
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
) {
1179 strncpy(sex
, "Male", 5);
1182 strncpy(sex
, "Female", 7);
1185 strncpy(sex
, "Unspecified", 12);
1188 PrintAndLogEx(SUCCESS
, "Legal Sex Marker......... " _YELLOW_("%s"), sex
);
1191 static int emrtd_mrz_determine_length(const char *mrz
, int offset
, int max_length
) {
1193 for (i
= max_length
; i
>= 1; i
--) {
1194 if (mrz
[offset
+ i
- 1] != '<') {
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
1205 for (i
= max_length
- 1; i
> 0; i
--) {
1206 if (mrz
[offset
+ i
] == '<' && mrz
[offset
+ i
+ 1] == '<') {
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
] == '<') {
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
);
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);
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
);
1253 int sep
= emrtd_mrz_determine_separator(mrz
, offset
, namelen
);
1255 // Account for mononyms
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
);
1263 memcpy(final_name
, mrz
+ offset
, namelen
);
1266 // Replace < characters with spaces
1267 emrtd_mrz_replace_pad(final_name
, namelen
, ' ');
1270 PrintAndLogEx(SUCCESS
, "Legal Name (Localized)... " _YELLOW_("%s"), final_name
);
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
1282 memcpy(work_date
, sprint_hex_inrow((uint8_t *)mrz
+ offset
, len
/ 2), len
);
1284 memcpy(work_date
, mrz
+ offset
, len
);
1287 // Set offset to 0 as we've now copied data.
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
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';
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
) {
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"
1362 PrintAndLogEx(SUCCESS
, "Personalization at....... " _YELLOW_("%s"), final_date
);
1365 static void emrtd_print_unknown_timestamp_5f85(uint8_t *data
, size_t datalen
) {
1369 char final_date
[20] = { 0x00 };
1370 snprintf(final_date
, sizeof(final_date
), "%.4s-%.2s-%.2s %.2s:%.2s:%.2s"
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);
1388 PrintAndLogEx(ERR
, "Failed to read file list from EF_COM.");
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
]);
1398 PrintAndLogEx(INFO
, "File tag not found, skipping: %02X", filelist
[i
]);
1401 int n
= 25 - strlen(dg
->filename
);
1402 PrintAndLogEx(SUCCESS
, "%s%*.*s " _YELLOW_("%s"), dg
->filename
, n
, n
, pad
, dg
->desc
);
1407 static int emrtd_print_ef_dg1_info(uint8_t *data
, size_t datalen
) {
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 };
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.");
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"));
1433 PrintAndLogEx(SUCCESS
, "Document Type............ " _YELLOW_("Unknown"));
1438 } else if (mrzlen
== 88) {
1441 PrintAndLogEx(ERR
, "MRZ length (%zu) is wrong.", mrzlen
);
1445 PrintAndLogEx(SUCCESS
, "Document Form Factor..... " _YELLOW_("TD%i"), td_variant
);
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) {
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."));
1498 static int emrtd_print_ef_dg2_info(uint8_t *data
, size_t datalen
) {
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
;
1514 // If we didn't get any data, return false.
1519 ShowPictureWindow(data
+ offset
, datalen
);
1523 static int emrtd_print_ef_dg5_info(uint8_t *data
, size_t datalen
) {
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
;
1539 // If we didn't get any data, return false.
1544 ShowPictureWindow(data
+ offset
, datalen
);
1548 static int emrtd_print_ef_dg7_info(uint8_t *data
, size_t datalen
) {
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
;
1564 // If we didn't get any data, return false.
1569 ShowPictureWindow(data
+ offset
, datalen
);
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.");
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);
1590 // Don't bother with empty tags
1591 if (tagdatalen
== 0) {
1594 // Special behavior for two char tags
1595 if (taglist
[i
] == 0x5f) {
1596 switch (taglist
[i
+ 1]) {
1598 emrtd_print_name((char *) tagdata
, 0, tagdatalen
, true);
1601 emrtd_print_name((char *) tagdata
, 0, tagdatalen
, false);
1604 PrintAndLogEx(SUCCESS
, "Personal Number.......... " _YELLOW_("%.*s"), (int)tagdatalen
, tagdata
);
1607 // TODO: acc for < separation
1608 PrintAndLogEx(SUCCESS
, "Place of Birth........... " _YELLOW_("%.*s"), (int)tagdatalen
, tagdata
);
1611 // TODO: acc for < separation
1612 PrintAndLogEx(SUCCESS
, "Permanent Address........ " _YELLOW_("%.*s"), (int)tagdatalen
, tagdata
);
1615 PrintAndLogEx(SUCCESS
, "Telephone................ " _YELLOW_("%.*s"), (int)tagdatalen
, tagdata
);
1618 PrintAndLogEx(SUCCESS
, "Profession............... " _YELLOW_("%.*s"), (int)tagdatalen
, tagdata
);
1621 PrintAndLogEx(SUCCESS
, "Title.................... " _YELLOW_("%.*s"), (int)tagdatalen
, tagdata
);
1624 PrintAndLogEx(SUCCESS
, "Personal Summary......... " _YELLOW_("%.*s"), (int)tagdatalen
, tagdata
);
1627 saveFile("ProofOfCitizenship", tagdata
[0] == 0xFF ? ".jpg" : ".jp2", tagdata
, tagdatalen
);
1630 // TODO: acc for < separation
1631 PrintAndLogEx(SUCCESS
, "Other valid TDs nums..... " _YELLOW_("%.*s"), (int)tagdatalen
, tagdata
);
1634 PrintAndLogEx(SUCCESS
, "Custody Information...... " _YELLOW_("%.*s"), (int)tagdatalen
, tagdata
);
1637 emrtd_print_dob((char *) tagdata
, 0, true, tagdatalen
!= 4);
1640 PrintAndLogEx(SUCCESS
, "Unknown Field %02X%02X....... %s", taglist
[i
], taglist
[i
+ 1], sprint_hex_inrow(tagdata
, tagdatalen
));
1646 // TODO: Account for A0
1647 PrintAndLogEx(SUCCESS
, "Unknown Field %02X......... %s", taglist
[i
], sprint_hex_inrow(tagdata
, tagdatalen
));
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.");
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);
1670 // Don't bother with empty tags
1671 if (tagdatalen
== 0) {
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]) {
1680 PrintAndLogEx(SUCCESS
, "Issuing Authority........ " _YELLOW_("%.*s"), (int)tagdatalen
, tagdata
);
1683 emrtd_print_issuance((char *) tagdata
, tagdatalen
!= 4);
1686 PrintAndLogEx(SUCCESS
, "Endorsements & Observations... " _YELLOW_("%.*s"), (int)tagdatalen
, tagdata
);
1689 PrintAndLogEx(SUCCESS
, "Tax/Exit Requirements.... " _YELLOW_("%.*s"), (int)tagdatalen
, tagdata
);
1692 saveFile("FrontOfDocument", tagdata
[0] == 0xFF ? ".jpg" : ".jp2", tagdata
, tagdatalen
);
1695 saveFile("BackOfDocument", tagdata
[0] == 0xFF ? ".jpg" : ".jp2", tagdata
, tagdatalen
);
1698 emrtd_print_personalization_timestamp(tagdata
, tagdatalen
);
1701 PrintAndLogEx(SUCCESS
, "Serial of Personalization System... " _YELLOW_("%.*s"), (int)tagdatalen
, tagdata
);
1704 emrtd_print_unknown_timestamp_5f85(tagdata
, tagdatalen
);
1707 PrintAndLogEx(SUCCESS
, "Unknown Field %02X%02X....... %s", taglist
[i
], taglist
[i
+ 1], sprint_hex_inrow(tagdata
, tagdatalen
));
1713 // TODO: Account for A0
1714 PrintAndLogEx(SUCCESS
, "Unknown Field %02X......... %s", taglist
[i
], sprint_hex_inrow(tagdata
, tagdatalen
));
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.");
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.");
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.");
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.");
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.");
1762 memcpy(dataout
, emrtdsigtext
, emrtdsigtextlen
);
1763 *dataoutlen
= emrtdsigtextlen
;
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
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.");
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
);
1795 if (memcmp(hashalg_table
[hashi
].descriptor
, hashalgoset
, hashalgosetlen
) == 0) {
1801 PrintAndLogEx(ERR
, "Failed to parse hash list (Unknown algo: %s). Hash verification won't be available.", sprint_hex_inrow(hashalgoset
, hashalgosetlen
));
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 };
1811 uint8_t hashidstr
[4] = { 0x00 };
1812 size_t hashidstrlen
= 0;
1814 size_t emrtdsiglen
= 0;
1815 size_t hashlistlen
= 0;
1818 if (emrtd_ef_sod_extract_signatures(data
, datalen
, emrtdsig
, &emrtdsiglen
) != PM3_SUCCESS
) {
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");
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
]) {
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);
1845 res
= emrtd_lds_get_data_by_tag(hashlist
+ offset
+ e_fieldlen
+ 1, e_datalen
, hash
, &hashlen
, 0x04, 0x00, false, false, 0);
1847 if (hashlen
<= 64) {
1848 memcpy(hashes
+ (hashidstr
[0] * 64), hash
, hashlen
);
1850 PrintAndLogEx(ERR
, "error (emrtd_parse_ef_sod_hashes) hashlen out-of-bounds");
1855 // + 1 for length of ID
1856 offset
+= 1 + e_datalen
+ e_fieldlen
;
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"));
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) {
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
);
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
);
1904 PrintAndLogEx(SUCCESS
, "EF_DG%2i %s %*.*s " _GREEN_("Valid"), i
, dg_table
[i
].desc
, n
, n
, pad
);
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.");
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.");
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.");
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.");
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
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 };
1973 uint8_t ssc
[8] = { 0x00 };
1974 uint8_t ks_enc
[16] = { 0x00 };
1975 uint8_t ks_mac
[16] = { 0x00 };
1977 bool PACE_available
= true;
1980 if (emrtd_connect() == false) {
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) {
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");
2018 int res
= emrtd_print_ef_com_info(response
, resplen
);
2019 if (res
!= PM3_SUCCESS
) {
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.");
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 } };
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.");
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
]);
2054 PrintAndLogEx(INFO
, "File tag not found, skipping... %02X", filelist
[i
]);
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
);
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
));
2075 emrtd_print_ef_sod_info(*dg_hashes_calc
, *dg_hashes_sod
, hash_algo
, true);
2080 int infoHF_EMRTD_offline(const char *path
) {
2083 char *filepath
= calloc(strlen(path
) + 100, sizeof(char));
2084 if (filepath
== NULL
) {
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");
2098 int res
= emrtd_print_ef_com_info(data
, datalen
);
2099 if (res
!= PM3_SUCCESS
) {
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);
2109 PrintAndLogEx(ERR
, "Failed to read file list from EF_COM");
2116 // Grab the hash list
2117 uint8_t dg_hashes_sod
[17][64] = { { 0 } };
2118 uint8_t dg_hashes_calc
[17][64] = { { 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
);
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");
2144 // coverity scan CID 395630,
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");
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
]);
2160 PrintAndLogEx(INFO
, "File tag not found, skipping... %02X", filelist
[i
]);
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
);
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
));
2187 emrtd_print_ef_sod_info(*dg_hashes_calc
, *dg_hashes_sod
, hash_algo
, false);
2192 static bool validate_date(uint8_t *data
, int datalen
) {
2193 // Date has to be 6 chars
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",
2213 "hf emrtd dump --dir ../dump\n"
2214 "hf emrtd dump -n 123456789 -d 890101 -e 250401"
2217 void *argtable
[] = {
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"),
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 };
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) {
2239 strn_upper((char *)docnum
, slen
);
2242 memset(docnum
+ slen
, '<', 9 - slen
);
2246 if (CLIParamStrToBuf(arg_get_str(ctx
, 2), dob
, 6, &slen
) != 0 || slen
== 0) {
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.");
2256 if (CLIParamStrToBuf(arg_get_str(ctx
, 3), expiry
, 6, &slen
) != 0 || slen
== 0) {
2259 if (!validate_date(expiry
, slen
)) {
2260 PrintAndLogEx(ERR
, "Expiry date format is incorrect, cannot continue.");
2261 PrintAndLogEx(HINT
, "Use the format YYMMDD.");
2266 if (CLIParamStrToBuf(arg_get_str(ctx
, 4), mrz
, 44, &slen
) == 0 && slen
!= 0) {
2268 PrintAndLogEx(ERR
, "MRZ length is incorrect, it should be 44, not %i", slen
);
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.");
2282 if (!validate_date(expiry
, 6)) {
2283 PrintAndLogEx(ERR
, "Expiry date format is incorrect, cannot continue.");
2284 PrintAndLogEx(HINT
, "Use the format YYMMDD.");
2290 uint8_t path
[FILENAME_MAX
] = { 0x00 };
2291 if (CLIParamStrToBuf(arg_get_str(ctx
, 5), path
, sizeof(path
), &slen
) != 0 || slen
== 0) {
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
);
2314 static int CmdHFeMRTDInfo(const char *Cmd
) {
2315 CLIParserContext
*ctx
;
2316 CLIParserInit(&ctx
, "hf emrtd info",
2317 "Display info about an eMRTD",
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
[] = {
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"),
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 };
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) {
2347 strn_upper((char *)docnum
, slen
);
2349 memset(docnum
+ slen
, '<', 9 - slen
);
2353 if (CLIParamStrToBuf(arg_get_str(ctx
, 2), dob
, 6, &slen
) != 0 || slen
== 0) {
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.");
2363 if (CLIParamStrToBuf(arg_get_str(ctx
, 3), expiry
, 6, &slen
) != 0 || slen
== 0) {
2366 if (!validate_date(expiry
, slen
)) {
2367 PrintAndLogEx(ERR
, "Expiry date format is incorrect, cannot continue.");
2368 PrintAndLogEx(HINT
, "Use the format YYMMDD.");
2373 if (CLIParamStrToBuf(arg_get_str(ctx
, 4), mrz
, 44, &slen
) == 0 && slen
!= 0) {
2375 PrintAndLogEx(ERR
, "MRZ length is incorrect, it should be 44, not %i", slen
);
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.");
2389 if (!validate_date(expiry
, 6)) {
2390 PrintAndLogEx(ERR
, "Expiry date format is incorrect, cannot continue.");
2391 PrintAndLogEx(HINT
, "Use the format YYMMDD.");
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);
2401 if ((IfPm3Iso14443() == false) && (is_offline
== false)) {
2402 PrintAndLogEx(WARNING
, "Only offline mode is available");
2411 return infoHF_EMRTD_offline((const char *)path
);
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
);
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
);
2441 int CmdHFeMRTD(const char *Cmd
) {
2442 clearCommandBuffer();
2443 return CmdsParse(CommandTable
, Cmd
);