]>
Commit | Line | Data |
---|---|---|
3c5fce2b | 1 | //----------------------------------------------------------------------------- |
11a78e04 | 2 | // Copyright (C) 2017, 2018 Merlok |
3c5fce2b OM |
3 | // |
4 | // This code is licensed to you under the terms of the GNU GPL, version 2 or, | |
5 | // at your option, any later version. See the LICENSE.txt file for the text of | |
6 | // the license. | |
7 | //----------------------------------------------------------------------------- | |
8 | // EMV commands | |
9 | //----------------------------------------------------------------------------- | |
10 | ||
3ded0f97 | 11 | #include <ctype.h> |
3c5fce2b | 12 | #include "cmdemv.h" |
d03fb293 | 13 | #include "test/cryptotest.h" |
6d31653c | 14 | #include "cliparser/cliparser.h" |
556826b5 | 15 | #include <jansson.h> |
3c5fce2b | 16 | |
3c5fce2b OM |
17 | int CmdHFEMVSelect(const char *cmd) { |
18 | uint8_t data[APDU_AID_LEN] = {0}; | |
19 | int datalen = 0; | |
3c5fce2b | 20 | |
3c5fce2b | 21 | |
02ab5afa | 22 | CLIParserInit("hf emv select", |
3668df05 | 23 | "Executes select applet command", |
a4662ca9 | 24 | "Usage:\n\thf emv select -s a00000000101 -> select card, select applet\n\thf emv select -st a00000000101 -> select card, select applet, show result in TLV\n"); |
3c5fce2b | 25 | |
3668df05 | 26 | void* argtable[] = { |
27 | arg_param_begin, | |
28 | arg_lit0("sS", "select", "activate field and select card"), | |
29 | arg_lit0("kK", "keep", "keep field for next command"), | |
30 | arg_lit0("aA", "apdu", "show APDU reqests and responses"), | |
31 | arg_lit0("tT", "tlv", "TLV decode results"), | |
32 | arg_str0(NULL, NULL, "<HEX applet AID>", NULL), | |
33 | arg_param_end | |
34 | }; | |
35 | CLIExecWithReturn(cmd, argtable, true); | |
36 | ||
37 | bool activateField = arg_get_lit(1); | |
38 | bool leaveSignalON = arg_get_lit(2); | |
39 | bool APDULogging = arg_get_lit(3); | |
40 | bool decodeTLV = arg_get_lit(4); | |
cd2f1acd | 41 | CLIGetStrWithReturn(5, data, &datalen); |
3668df05 | 42 | CLIParserFree(); |
43 | ||
44 | SetAPDULogging(APDULogging); | |
3c5fce2b OM |
45 | |
46 | // exec | |
47 | uint8_t buf[APDU_RES_LEN] = {0}; | |
48 | size_t len = 0; | |
49 | uint16_t sw = 0; | |
50 | int res = EMVSelect(activateField, leaveSignalON, data, datalen, buf, sizeof(buf), &len, &sw, NULL); | |
51 | ||
52 | if (sw) | |
53 | PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); | |
54 | ||
55 | if (res) | |
56 | return res; | |
57 | ||
58 | if (decodeTLV) | |
59 | TLVPrintFromBuffer(buf, len); | |
60 | ||
61 | return 0; | |
62 | } | |
63 | ||
3c5fce2b OM |
64 | int CmdHFEMVSearch(const char *cmd) { |
65 | ||
02ab5afa | 66 | CLIParserInit("hf emv search", |
6d31653c | 67 | "Tries to select all applets from applet list:\n", |
a4662ca9 | 68 | "Usage:\n\thf emv search -s -> select card and search\n\thf emv search -st -> select card, search and show result in TLV\n"); |
6d31653c | 69 | |
70 | void* argtable[] = { | |
71 | arg_param_begin, | |
72 | arg_lit0("sS", "select", "activate field and select card"), | |
73 | arg_lit0("kK", "keep", "keep field ON for next command"), | |
74 | arg_lit0("aA", "apdu", "show APDU reqests and responses"), | |
75 | arg_lit0("tT", "tlv", "TLV decode results of selected applets"), | |
76 | arg_param_end | |
77 | }; | |
78 | CLIExecWithReturn(cmd, argtable, true); | |
3c5fce2b | 79 | |
6d31653c | 80 | bool activateField = arg_get_lit(1); |
81 | bool leaveSignalON = arg_get_lit(2); | |
82 | bool APDULogging = arg_get_lit(3); | |
83 | bool decodeTLV = arg_get_lit(4); | |
3668df05 | 84 | CLIParserFree(); |
6d31653c | 85 | |
86 | SetAPDULogging(APDULogging); | |
3c5fce2b | 87 | |
3c5fce2b OM |
88 | struct tlvdb *t = NULL; |
89 | const char *al = "Applets list"; | |
90 | t = tlvdb_fixed(1, strlen(al), (const unsigned char *)al); | |
91 | ||
92 | if (EMVSearch(activateField, leaveSignalON, decodeTLV, t)) { | |
93 | tlvdb_free(t); | |
94 | return 2; | |
95 | } | |
96 | ||
97 | PrintAndLog("Search completed."); | |
98 | ||
99 | // print list here | |
100 | if (!decodeTLV) { | |
101 | TLVPrintAIDlistFromSelectTLV(t); | |
102 | } | |
103 | ||
104 | tlvdb_free(t); | |
105 | ||
106 | return 0; | |
107 | } | |
108 | ||
3c5fce2b OM |
109 | int CmdHFEMVPPSE(const char *cmd) { |
110 | ||
02ab5afa | 111 | CLIParserInit("hf emv pse", |
a4662ca9 | 112 | "Executes PSE/PPSE select command. It returns list of applet on the card:\n", |
113 | "Usage:\n\thf emv pse -s1 -> select, get pse\n\thf emv pse -st2 -> select, get ppse, show result in TLV\n"); | |
3c5fce2b | 114 | |
a4662ca9 | 115 | void* argtable[] = { |
116 | arg_param_begin, | |
117 | arg_lit0("sS", "select", "activate field and select card"), | |
118 | arg_lit0("kK", "keep", "keep field ON for next command"), | |
119 | arg_lit0("1", "pse", "pse (1PAY.SYS.DDF01) mode"), | |
120 | arg_lit0("2", "ppse", "ppse (2PAY.SYS.DDF01) mode (default mode)"), | |
121 | arg_lit0("aA", "apdu", "show APDU reqests and responses"), | |
122 | arg_lit0("tT", "tlv", "TLV decode results of selected applets"), | |
123 | arg_param_end | |
124 | }; | |
125 | CLIExecWithReturn(cmd, argtable, true); | |
126 | ||
127 | bool activateField = arg_get_lit(1); | |
128 | bool leaveSignalON = arg_get_lit(2); | |
129 | uint8_t PSENum = 2; | |
130 | if (arg_get_lit(3)) | |
131 | PSENum = 1; | |
132 | if (arg_get_lit(4)) | |
133 | PSENum = 2; | |
134 | bool APDULogging = arg_get_lit(5); | |
135 | bool decodeTLV = arg_get_lit(6); | |
136 | CLIParserFree(); | |
137 | ||
138 | SetAPDULogging(APDULogging); | |
3c5fce2b OM |
139 | |
140 | // exec | |
141 | uint8_t buf[APDU_RES_LEN] = {0}; | |
142 | size_t len = 0; | |
143 | uint16_t sw = 0; | |
144 | int res = EMVSelectPSE(activateField, leaveSignalON, PSENum, buf, sizeof(buf), &len, &sw); | |
145 | ||
146 | if (sw) | |
147 | PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); | |
148 | ||
149 | if (res) | |
150 | return res; | |
151 | ||
152 | ||
153 | if (decodeTLV) | |
154 | TLVPrintFromBuffer(buf, len); | |
155 | ||
156 | return 0; | |
157 | } | |
158 | ||
654b6ae3 | 159 | #define TLV_ADD(tag, value)( tlvdb_change_or_add_node(tlvRoot, tag, sizeof(value) - 1, (const unsigned char *)value) ) |
ab2d91f8 | 160 | |
a4662ca9 | 161 | int CmdHFEMVGPO(const char *cmd) { |
cd2f1acd | 162 | uint8_t data[APDU_RES_LEN] = {0}; |
163 | int datalen = 0; | |
164 | ||
02ab5afa | 165 | CLIParserInit("hf emv gpo", |
ab2d91f8 | 166 | "Executes Get Processing Options command. It returns data in TLV format (0x77 - format2) or plain format (0x80 - format1).\nNeeds a EMV applet to be selected.", |
0c4750c3 | 167 | "Usage:\n\thf emv gpo -k -> execute GPO\n" |
168 | "\thf emv gpo -st 01020304 -> execute GPO with 4-byte PDOL data, show result in TLV\n"); | |
169 | // here need to add load params from file and gen pdol | |
cd2f1acd | 170 | |
171 | void* argtable[] = { | |
172 | arg_param_begin, | |
173 | arg_lit0("kK", "keep", "keep field ON for next command"), | |
174 | arg_lit0("pP", "params", "load parameters for PDOL making from `emv/defparams.json` file (by default uses default parameters) (NOT WORK!!!)"), | |
175 | arg_lit0("mM", "make", "make PDOLdata from PDOL (tag 9F38) and parameters (NOT WORK!!!)"), | |
176 | arg_lit0("aA", "apdu", "show APDU reqests and responses"), | |
177 | arg_lit0("tT", "tlv", "TLV decode results of selected applets"), | |
178 | arg_str0(NULL, NULL, "<HEX PDOLdata/PDOL>", NULL), | |
179 | arg_param_end | |
180 | }; | |
181 | CLIExecWithReturn(cmd, argtable, true); | |
182 | ||
183 | bool leaveSignalON = arg_get_lit(1); | |
184 | bool paramsLoadFromFile = arg_get_lit(2); | |
185 | bool dataMakeFromPDOL = arg_get_lit(3); | |
186 | bool APDULogging = arg_get_lit(4); | |
187 | bool decodeTLV = arg_get_lit(5); | |
188 | CLIGetStrWithReturn(6, data, &datalen); | |
189 | CLIParserFree(); | |
190 | ||
191 | SetAPDULogging(APDULogging); | |
192 | ||
193 | // Init TLV tree | |
194 | const char *alr = "Root terminal TLV tree"; | |
195 | struct tlvdb *tlvRoot = tlvdb_fixed(1, strlen(alr), (const unsigned char *)alr); | |
196 | ||
197 | // calc PDOL | |
198 | struct tlv *pdol_data_tlv = NULL; | |
199 | struct tlv data_tlv = { | |
11a78e04 | 200 | .tag = 0x01, |
cd2f1acd | 201 | .len = datalen, |
202 | .value = (uint8_t *)data, | |
203 | }; | |
204 | if (dataMakeFromPDOL) { | |
205 | // TODO | |
206 | PrintAndLog("Make PDOL data not implemented!"); | |
ab2d91f8 | 207 | |
208 | //9F02:(Amount, authorized (Numeric)) len:6 | |
209 | TLV_ADD(0x9F02, "\x00\x00\x00\x00\x01\x00"); | |
210 | //9F1A:(Terminal Country Code) len:2 | |
211 | TLV_ADD(0x9F1A, "ru"); | |
212 | //5F2A:(Transaction Currency Code) len:2 | |
213 | // USD 840, EUR 978, RUR 810, RUB 643, RUR 810(old), UAH 980, AZN 031, n/a 999 | |
214 | TLV_ADD(0x5F2A, "\x09\x80"); | |
215 | //9A:(Transaction Date) len:3 | |
216 | TLV_ADD(0x9A, "\x00\x00\x00"); | |
217 | //9C:(Transaction Type) len:1 | 00 => Goods and service #01 => Cash | |
218 | TLV_ADD(0x9C, "\x00"); | |
219 | // 9F37 Unpredictable Number len:4 | |
220 | TLV_ADD(0x9F37, "\x01\x02\x03\x04"); | |
221 | // 9F6A Unpredictable Number (MSD for UDOL) len:4 | |
222 | TLV_ADD(0x9F6A, "\x01\x02\x03\x04"); | |
223 | //9F66:(Terminal Transaction Qualifiers (TTQ)) len:4 | |
224 | TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC | |
225 | ||
cd2f1acd | 226 | if (paramsLoadFromFile) { |
227 | }; | |
228 | /* pdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x9f38, NULL), tlvRoot, 0x83); | |
229 | if (!pdol_data_tlv){ | |
230 | PrintAndLog("ERROR: can't create PDOL TLV."); | |
231 | tlvdb_free(tlvRoot); | |
232 | return 4; | |
233 | }*/ | |
234 | return 0; | |
235 | } else { | |
236 | pdol_data_tlv = &data_tlv; | |
237 | } | |
238 | ||
239 | size_t pdol_data_tlv_data_len = 0; | |
240 | unsigned char *pdol_data_tlv_data = tlv_encode(pdol_data_tlv, &pdol_data_tlv_data_len); | |
241 | if (!pdol_data_tlv_data) { | |
242 | PrintAndLog("ERROR: can't create PDOL data."); | |
243 | tlvdb_free(tlvRoot); | |
244 | return 4; | |
245 | } | |
246 | PrintAndLog("PDOL data[%d]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len)); | |
247 | ||
248 | // exec | |
249 | uint8_t buf[APDU_RES_LEN] = {0}; | |
250 | size_t len = 0; | |
251 | uint16_t sw = 0; | |
252 | int res = EMVGPO(leaveSignalON, pdol_data_tlv_data, pdol_data_tlv_data_len, buf, sizeof(buf), &len, &sw, tlvRoot); | |
253 | ||
254 | free(pdol_data_tlv_data); | |
255 | tlvdb_free(tlvRoot); | |
256 | ||
257 | if (sw) | |
258 | PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); | |
259 | ||
260 | if (res) | |
261 | return res; | |
262 | ||
263 | if (decodeTLV) | |
264 | TLVPrintFromBuffer(buf, len); | |
a4662ca9 | 265 | |
626e650f | 266 | return 0; |
a4662ca9 | 267 | } |
268 | ||
269 | int CmdHFEMVReadRecord(const char *cmd) { | |
cd2f1acd | 270 | uint8_t data[APDU_RES_LEN] = {0}; |
271 | int datalen = 0; | |
272 | ||
02ab5afa | 273 | CLIParserInit("hf emv readrec", |
ab2d91f8 | 274 | "Executes Read Record command. It returns data in TLV format.\nNeeds a bank applet to be selected and sometimes needs GPO to be executed.", |
275 | "Usage:\n\thf emv readrec -k 0101 -> read file SFI=01, SFIrec=01\n\thf emv readrec -kt 0201-> read file 0201 and show result in TLV\n"); | |
cd2f1acd | 276 | |
277 | void* argtable[] = { | |
278 | arg_param_begin, | |
279 | arg_lit0("kK", "keep", "keep field ON for next command"), | |
280 | arg_lit0("aA", "apdu", "show APDU reqests and responses"), | |
281 | arg_lit0("tT", "tlv", "TLV decode results of selected applets"), | |
ab2d91f8 | 282 | arg_str1(NULL, NULL, "<SFI 1byte HEX><SFIrec 1byte HEX>", NULL), |
cd2f1acd | 283 | arg_param_end |
284 | }; | |
285 | CLIExecWithReturn(cmd, argtable, true); | |
286 | ||
287 | bool leaveSignalON = arg_get_lit(1); | |
288 | bool APDULogging = arg_get_lit(2); | |
289 | bool decodeTLV = arg_get_lit(3); | |
290 | CLIGetStrWithReturn(4, data, &datalen); | |
291 | CLIParserFree(); | |
292 | ||
293 | if (datalen != 2) { | |
294 | PrintAndLog("ERROR: Command needs to have 2 bytes of data"); | |
295 | return 1; | |
296 | } | |
297 | ||
298 | SetAPDULogging(APDULogging); | |
299 | ||
300 | // exec | |
301 | uint8_t buf[APDU_RES_LEN] = {0}; | |
302 | size_t len = 0; | |
303 | uint16_t sw = 0; | |
304 | int res = EMVReadRecord(leaveSignalON, data[0], data[1], buf, sizeof(buf), &len, &sw, NULL); | |
305 | ||
306 | if (sw) | |
307 | PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); | |
308 | ||
309 | if (res) | |
310 | return res; | |
311 | ||
312 | ||
313 | if (decodeTLV) | |
314 | TLVPrintFromBuffer(buf, len); | |
a4662ca9 | 315 | |
626e650f | 316 | return 0; |
a4662ca9 | 317 | } |
318 | ||
319 | int CmdHFEMVAC(const char *cmd) { | |
11a78e04 | 320 | uint8_t data[APDU_RES_LEN] = {0}; |
321 | int datalen = 0; | |
a4662ca9 | 322 | |
02ab5afa | 323 | CLIParserInit("hf emv genac", |
11a78e04 | 324 | "Generate Application Cryptogram command. It returns data in TLV format .\nNeeds a EMV applet to be selected and GPO to be executed.", |
02ab5afa | 325 | "Usage:\n\thf emv genac -k 0102 -> generate AC with 2-byte CDOLdata and keep field ON after command\n" |
326 | "\thf emv genac -t 01020304 -> generate AC with 4-byte CDOL data, show result in TLV\n" | |
327 | "\thf emv genac -Daac 01020304 -> generate AC with 4-byte CDOL data and terminal decision 'declined'\n"); | |
11a78e04 | 328 | |
329 | void* argtable[] = { | |
330 | arg_param_begin, | |
02ab5afa | 331 | arg_lit0("kK", "keep", "keep field ON for next command"), |
332 | arg_lit0("cC", "cda", "executes CDA transaction. Needs to get SDAD in results."), | |
333 | arg_str0("dD", "decision", "<aac|tc|arqc>", "Terminal decision. aac - declined, tc - approved, arqc - online authorisation requested"), | |
334 | arg_lit0("aA", "apdu", "show APDU reqests and responses"), | |
335 | arg_lit0("tT", "tlv", "TLV decode results of selected applets"), | |
336 | arg_str1(NULL, NULL, "<HEX CDOLdata>", NULL), | |
11a78e04 | 337 | arg_param_end |
338 | }; | |
339 | CLIExecWithReturn(cmd, argtable, false); | |
340 | ||
341 | bool leaveSignalON = arg_get_lit(1); | |
48c32f04 | 342 | bool trTypeCDA = arg_get_lit(2); |
02ab5afa | 343 | uint8_t termDecision = 0xff; |
344 | if (arg_get_str_len(3)) { | |
345 | if (!strncmp(arg_get_str(3)->sval[0], "aac", 4)) | |
346 | termDecision = EMVAC_AAC; | |
347 | if (!strncmp(arg_get_str(3)->sval[0], "tc", 4)) | |
348 | termDecision = EMVAC_TC; | |
349 | if (!strncmp(arg_get_str(3)->sval[0], "arqc", 4)) | |
350 | termDecision = EMVAC_ARQC; | |
351 | ||
352 | if (termDecision == 0xff) { | |
353 | PrintAndLog("ERROR: can't find terminal decision '%s'", arg_get_str(3)->sval[0]); | |
354 | return 1; | |
355 | } | |
356 | } else { | |
357 | termDecision = EMVAC_TC; | |
358 | } | |
359 | if (trTypeCDA) | |
360 | termDecision = termDecision | EMVAC_CDAREQ; | |
361 | bool APDULogging = arg_get_lit(4); | |
362 | bool decodeTLV = arg_get_lit(5); | |
363 | CLIGetStrWithReturn(6, data, &datalen); | |
11a78e04 | 364 | CLIParserFree(); |
365 | ||
366 | SetAPDULogging(APDULogging); | |
367 | ||
368 | // Init TLV tree | |
369 | const char *alr = "Root terminal TLV tree"; | |
370 | struct tlvdb *tlvRoot = tlvdb_fixed(1, strlen(alr), (const unsigned char *)alr); | |
371 | ||
372 | // calc CDOL | |
373 | struct tlv *cdol_data_tlv = NULL; | |
374 | // struct tlv *cdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x8c, NULL), tlvRoot, 0x01); // 0x01 - dummy tag | |
375 | struct tlv data_tlv = { | |
376 | .tag = 0x01, | |
377 | .len = datalen, | |
378 | .value = (uint8_t *)data, | |
379 | }; | |
380 | cdol_data_tlv = &data_tlv; | |
381 | PrintAndLog("CDOL data[%d]: %s", cdol_data_tlv->len, sprint_hex(cdol_data_tlv->value, cdol_data_tlv->len)); | |
382 | ||
383 | // exec | |
384 | uint8_t buf[APDU_RES_LEN] = {0}; | |
385 | size_t len = 0; | |
386 | uint16_t sw = 0; | |
02ab5afa | 387 | int res = EMVAC(leaveSignalON, termDecision, (uint8_t *)cdol_data_tlv->value, cdol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot); |
11a78e04 | 388 | |
389 | // free(cdol_data_tlv); | |
390 | tlvdb_free(tlvRoot); | |
391 | ||
392 | if (sw) | |
393 | PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); | |
394 | ||
395 | if (res) | |
396 | return res; | |
397 | ||
398 | if (decodeTLV) | |
399 | TLVPrintFromBuffer(buf, len); | |
400 | ||
401 | return 0; | |
a4662ca9 | 402 | } |
403 | ||
404 | int CmdHFEMVGenerateChallenge(const char *cmd) { | |
405 | ||
02ab5afa | 406 | CLIParserInit("hf emv challenge", |
0c4750c3 | 407 | "Executes Generate Challenge command. It returns 4 or 8-byte random number from card.\nNeeds a EMV applet to be selected and GPO to be executed.", |
8ffdccff | 408 | "Usage:\n\thf emv challenge -> get challenge\n\thf emv challenge -k -> get challenge, keep fileld ON\n"); |
409 | ||
410 | void* argtable[] = { | |
411 | arg_param_begin, | |
412 | arg_lit0("kK", "keep", "keep field ON for next command"), | |
413 | arg_lit0("aA", "apdu", "show APDU reqests and responses"), | |
414 | arg_param_end | |
415 | }; | |
416 | CLIExecWithReturn(cmd, argtable, true); | |
417 | ||
418 | bool leaveSignalON = arg_get_lit(1); | |
419 | bool APDULogging = arg_get_lit(2); | |
420 | CLIParserFree(); | |
421 | ||
422 | SetAPDULogging(APDULogging); | |
423 | ||
424 | // exec | |
425 | uint8_t buf[APDU_RES_LEN] = {0}; | |
426 | size_t len = 0; | |
427 | uint16_t sw = 0; | |
428 | int res = EMVGenerateChallenge(leaveSignalON, buf, sizeof(buf), &len, &sw, NULL); | |
429 | ||
430 | if (sw) | |
431 | PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); | |
432 | ||
433 | if (res) | |
434 | return res; | |
435 | ||
436 | PrintAndLog("Challenge: %s", sprint_hex(buf, len)); | |
437 | ||
438 | if (len != 4 && len != 8) | |
439 | PrintAndLog("WARNING: length of challenge must be 4 or 8, but it %d", len); | |
440 | ||
626e650f | 441 | return 0; |
a4662ca9 | 442 | } |
443 | ||
444 | int CmdHFEMVInternalAuthenticate(const char *cmd) { | |
11a78e04 | 445 | uint8_t data[APDU_RES_LEN] = {0}; |
446 | int datalen = 0; | |
a4662ca9 | 447 | |
02ab5afa | 448 | CLIParserInit("hf emv intauth", |
11a78e04 | 449 | "Generate Internal Authenticate command. Usually needs 4-byte random number. It returns data in TLV format .\nNeeds a EMV applet to be selected and GPO to be executed.", |
450 | "Usage:\n\thf emv intauth -k 01020304 -> execute Internal Authenticate with 4-byte DDOLdata and keep field ON after command\n" | |
451 | "\thf emv intauth -t 01020304 -> execute Internal Authenticate with 4-byte DDOL data, show result in TLV\n"); | |
452 | ||
453 | void* argtable[] = { | |
454 | arg_param_begin, | |
455 | arg_lit0("kK", "keep", "keep field ON for next command"), | |
456 | arg_lit0("aA", "apdu", "show APDU reqests and responses"), | |
457 | arg_lit0("tT", "tlv", "TLV decode results of selected applets"), | |
458 | arg_str1(NULL, NULL, "<HEX DDOLdata>", NULL), | |
459 | arg_param_end | |
460 | }; | |
461 | CLIExecWithReturn(cmd, argtable, false); | |
462 | ||
463 | bool leaveSignalON = arg_get_lit(1); | |
464 | bool APDULogging = arg_get_lit(2); | |
465 | bool decodeTLV = arg_get_lit(3); | |
466 | CLIGetStrWithReturn(4, data, &datalen); | |
467 | CLIParserFree(); | |
468 | ||
469 | SetAPDULogging(APDULogging); | |
470 | ||
471 | // DDOL | |
472 | PrintAndLog("DDOL data[%d]: %s", datalen, sprint_hex(data, datalen)); | |
473 | ||
474 | // exec | |
475 | uint8_t buf[APDU_RES_LEN] = {0}; | |
476 | size_t len = 0; | |
477 | uint16_t sw = 0; | |
478 | int res = EMVInternalAuthenticate(leaveSignalON, data, datalen, buf, sizeof(buf), &len, &sw, NULL); | |
479 | ||
480 | if (sw) | |
481 | PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); | |
482 | ||
483 | if (res) | |
484 | return res; | |
485 | ||
486 | if (decodeTLV) | |
487 | TLVPrintFromBuffer(buf, len); | |
488 | ||
489 | return 0; | |
a4662ca9 | 490 | } |
491 | ||
3c5fce2b OM |
492 | int UsageCmdHFEMVExec(void) { |
493 | PrintAndLog("HELP : Executes EMV contactless transaction:\n"); | |
556826b5 | 494 | PrintAndLog("Usage: hf emv exec [-s][-a][-t][-j][-f][-v][-c][-x][-g]\n"); |
3c5fce2b OM |
495 | PrintAndLog(" Options:"); |
496 | PrintAndLog(" -s : select card"); | |
497 | PrintAndLog(" -a : show APDU reqests and responses\n"); | |
498 | PrintAndLog(" -t : TLV decode results\n"); | |
556826b5 | 499 | PrintAndLog(" -j : load transaction parameters from `emv/defparams.json` file\n"); |
3c5fce2b | 500 | PrintAndLog(" -f : force search AID. Search AID instead of execute PPSE.\n"); |
10d4f823 | 501 | PrintAndLog(" -v : transaction type - qVSDC or M/Chip.\n"); |
502 | PrintAndLog(" -c : transaction type - qVSDC or M/Chip plus CDA (SDAD generation).\n"); | |
503 | PrintAndLog(" -x : transaction type - VSDC. For test only. Not a standart behavior.\n"); | |
d03fb293 | 504 | PrintAndLog(" -g : VISA. generate AC from GPO\n"); |
10d4f823 | 505 | PrintAndLog("By default : transaction type - MSD.\n"); |
3c5fce2b | 506 | PrintAndLog("Samples:"); |
d03fb293 OM |
507 | PrintAndLog(" hf emv exec -s -a -t -> execute MSD transaction"); |
508 | PrintAndLog(" hf emv exec -s -a -t -c -> execute CDA transaction"); | |
3c5fce2b OM |
509 | return 0; |
510 | } | |
511 | ||
d03fb293 | 512 | #define dreturn(n) {free(pdol_data_tlv);tlvdb_free(tlvSelect);tlvdb_free(tlvRoot);DropField();return n;} |
3c5fce2b | 513 | |
556826b5 OM |
514 | bool HexToBuffer(const char *errormsg, const char *hexvalue, uint8_t * buffer, size_t maxbufferlen, size_t *bufferlen) { |
515 | int buflen = 0; | |
516 | ||
517 | switch(param_gethex_to_eol(hexvalue, 0, buffer, maxbufferlen, &buflen)) { | |
518 | case 1: | |
519 | PrintAndLog("%s Invalid HEX value.", errormsg); | |
520 | return false; | |
521 | case 2: | |
522 | PrintAndLog("%s Hex value too large.", errormsg); | |
523 | return false; | |
524 | case 3: | |
525 | PrintAndLog("%s Hex value must have even number of digits.", errormsg); | |
526 | return false; | |
527 | } | |
528 | ||
529 | if (buflen > maxbufferlen) { | |
530 | PrintAndLog("%s HEX length (%d) more than %d", errormsg, *bufferlen, maxbufferlen); | |
531 | return false; | |
532 | } | |
533 | ||
534 | *bufferlen = buflen; | |
535 | ||
536 | return true; | |
537 | } | |
538 | ||
539 | bool ParamLoadFromJson(struct tlvdb *tlv) { | |
540 | json_t *root; | |
541 | json_error_t error; | |
542 | ||
543 | if (!tlv) { | |
544 | PrintAndLog("ERROR load params: tlv tree is NULL."); | |
545 | return false; | |
546 | } | |
547 | ||
548 | // current path + file name | |
549 | const char *relfname = "emv/defparams.json"; | |
550 | char fname[strlen(get_my_executable_directory()) + strlen(relfname) + 1]; | |
551 | strcpy(fname, get_my_executable_directory()); | |
552 | strcat(fname, relfname); | |
553 | ||
554 | root = json_load_file(fname, 0, &error); | |
555 | if (!root) { | |
556 | PrintAndLog("Load params: json error on line %d: %s", error.line, error.text); | |
557 | return false; | |
558 | } | |
559 | ||
560 | if (!json_is_array(root)) { | |
561 | PrintAndLog("Load params: Invalid json format. root must be array."); | |
562 | return false; | |
563 | } | |
564 | ||
565 | PrintAndLog("Load params: json OK"); | |
566 | ||
567 | for(int i = 0; i < json_array_size(root); i++) { | |
568 | json_t *data, *jtype, *jlength, *jvalue; | |
569 | ||
570 | data = json_array_get(root, i); | |
571 | if(!json_is_object(data)) | |
572 | { | |
573 | PrintAndLog("Load params: data [%d] is not an object", i + 1); | |
574 | json_decref(root); | |
575 | return false; | |
576 | } | |
577 | ||
578 | jtype = json_object_get(data, "type"); | |
579 | if(!json_is_string(jtype)) | |
580 | { | |
581 | PrintAndLog("Load params: data [%d] type is not a string", i + 1); | |
582 | json_decref(root); | |
583 | return false; | |
584 | } | |
585 | const char *tlvType = json_string_value(jtype); | |
586 | ||
587 | jvalue = json_object_get(data, "value"); | |
588 | if(!json_is_string(jvalue)) | |
589 | { | |
590 | PrintAndLog("Load params: data [%d] value is not a string", i + 1); | |
591 | json_decref(root); | |
592 | return false; | |
593 | } | |
594 | const char *tlvValue = json_string_value(jvalue); | |
595 | ||
596 | jlength = json_object_get(data, "length"); | |
597 | if(!json_is_number(jlength)) | |
598 | { | |
599 | PrintAndLog("Load params: data [%d] length is not a number", i + 1); | |
600 | json_decref(root); | |
601 | return false; | |
602 | } | |
603 | ||
604 | int tlvLength = json_integer_value(jlength); | |
605 | if (tlvLength > 250) { | |
606 | PrintAndLog("Load params: data [%d] length more than 250", i + 1); | |
607 | json_decref(root); | |
608 | return false; | |
609 | } | |
610 | ||
611 | PrintAndLog("TLV param: %s[%d]=%s", tlvType, tlvLength, tlvValue); | |
612 | uint8_t buf[251] = {0}; | |
613 | size_t buflen = 0; | |
614 | ||
615 | // here max length must be 4, but now tlv_tag_t is 2-byte var. so let it be 2 by now... TODO: needs refactoring tlv_tag_t... | |
616 | if (!HexToBuffer("TLV Error type:", tlvType, buf, 2, &buflen)) { | |
617 | json_decref(root); | |
618 | return false; | |
619 | } | |
620 | tlv_tag_t tag = 0; | |
621 | for (int i = 0; i < buflen; i++) { | |
622 | tag = (tag << 8) + buf[i]; | |
623 | } | |
624 | ||
625 | if (!HexToBuffer("TLV Error value:", tlvValue, buf, sizeof(buf) - 1, &buflen)) { | |
626 | json_decref(root); | |
627 | return false; | |
628 | } | |
629 | ||
630 | if (buflen != tlvLength) { | |
631 | PrintAndLog("Load params: data [%d] length of HEX must(%d) be identical to length in TLV param(%d)", i + 1, buflen, tlvLength); | |
632 | json_decref(root); | |
633 | return false; | |
634 | } | |
635 | ||
636 | tlvdb_change_or_add_node(tlv, tag, tlvLength, (const unsigned char *)buf); | |
637 | } | |
638 | ||
639 | json_decref(root); | |
640 | ||
641 | return true; | |
642 | } | |
643 | ||
644 | void ParamLoadDefaults(struct tlvdb *tlvRoot) { | |
645 | //9F02:(Amount, authorized (Numeric)) len:6 | |
646 | TLV_ADD(0x9F02, "\x00\x00\x00\x00\x01\x00"); | |
647 | //9F1A:(Terminal Country Code) len:2 | |
648 | TLV_ADD(0x9F1A, "ru"); | |
649 | //5F2A:(Transaction Currency Code) len:2 | |
650 | // USD 840, EUR 978, RUR 810, RUB 643, RUR 810(old), UAH 980, AZN 031, n/a 999 | |
651 | TLV_ADD(0x5F2A, "\x09\x80"); | |
652 | //9A:(Transaction Date) len:3 | |
653 | TLV_ADD(0x9A, "\x00\x00\x00"); | |
654 | //9C:(Transaction Type) len:1 | 00 => Goods and service #01 => Cash | |
655 | TLV_ADD(0x9C, "\x00"); | |
656 | // 9F37 Unpredictable Number len:4 | |
657 | TLV_ADD(0x9F37, "\x01\x02\x03\x04"); | |
658 | // 9F6A Unpredictable Number (MSD for UDOL) len:4 | |
659 | TLV_ADD(0x9F6A, "\x01\x02\x03\x04"); | |
660 | //9F66:(Terminal Transaction Qualifiers (TTQ)) len:4 | |
661 | TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC | |
662 | } | |
663 | ||
3c5fce2b OM |
664 | int CmdHFEMVExec(const char *cmd) { |
665 | bool activateField = false; | |
666 | bool showAPDU = false; | |
667 | bool decodeTLV = false; | |
668 | bool forceSearch = false; | |
10d4f823 | 669 | enum TransactionType TrType = TT_MSD; |
d03fb293 | 670 | bool GenACGPO = false; |
556826b5 | 671 | bool paramLoadJSON = false; |
3c5fce2b OM |
672 | |
673 | uint8_t buf[APDU_RES_LEN] = {0}; | |
674 | size_t len = 0; | |
675 | uint16_t sw = 0; | |
676 | uint8_t AID[APDU_AID_LEN] = {0}; | |
677 | size_t AIDlen = 0; | |
d03fb293 OM |
678 | uint8_t ODAiList[4096]; |
679 | size_t ODAiListLen = 0; | |
3c5fce2b OM |
680 | |
681 | int res; | |
682 | ||
d03fb293 OM |
683 | struct tlvdb *tlvSelect = NULL; |
684 | struct tlvdb *tlvRoot = NULL; | |
685 | struct tlv *pdol_data_tlv = NULL; | |
686 | ||
3c5fce2b OM |
687 | if (strlen(cmd) < 1) { |
688 | UsageCmdHFEMVExec(); | |
689 | return 0; | |
690 | } | |
691 | ||
692 | int cmdp = 0; | |
693 | while(param_getchar(cmd, cmdp) != 0x00) { | |
694 | char c = param_getchar(cmd, cmdp); | |
695 | if ((c == '-') && (param_getlength(cmd, cmdp) == 2)) | |
696 | switch (param_getchar_indx(cmd, 1, cmdp)) { | |
697 | case 'h': | |
698 | case 'H': | |
7a7afeba | 699 | UsageCmdHFEMVExec(); |
3c5fce2b OM |
700 | return 0; |
701 | case 's': | |
702 | case 'S': | |
703 | activateField = true; | |
704 | break; | |
705 | case 'a': | |
706 | case 'A': | |
707 | showAPDU = true; | |
708 | break; | |
709 | case 't': | |
710 | case 'T': | |
711 | decodeTLV = true; | |
712 | break; | |
713 | case 'f': | |
714 | case 'F': | |
715 | forceSearch = true; | |
716 | break; | |
10d4f823 | 717 | case 'x': |
718 | case 'X': | |
719 | TrType = TT_VSDC; | |
720 | break; | |
721 | case 'v': | |
722 | case 'V': | |
723 | TrType = TT_QVSDCMCHIP; | |
724 | break; | |
725 | case 'c': | |
726 | case 'C': | |
727 | TrType = TT_CDA; | |
728 | break; | |
d03fb293 OM |
729 | case 'g': |
730 | case 'G': | |
731 | GenACGPO = true; | |
732 | break; | |
556826b5 OM |
733 | case 'j': |
734 | case 'J': | |
735 | paramLoadJSON = true; | |
736 | break; | |
3c5fce2b OM |
737 | default: |
738 | PrintAndLog("Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp)); | |
739 | return 1; | |
740 | } | |
741 | cmdp++; | |
742 | } | |
743 | ||
744 | ||
745 | // init applets list tree | |
3c5fce2b OM |
746 | const char *al = "Applets list"; |
747 | tlvSelect = tlvdb_fixed(1, strlen(al), (const unsigned char *)al); | |
748 | ||
749 | // Application Selection | |
750 | // https://www.openscdp.org/scripts/tutorial/emv/applicationselection.html | |
751 | if (!forceSearch) { | |
752 | // PPSE | |
753 | PrintAndLog("\n* PPSE."); | |
754 | SetAPDULogging(showAPDU); | |
755 | res = EMVSearchPSE(activateField, true, decodeTLV, tlvSelect); | |
756 | ||
757 | // check PPSE and select application id | |
758 | if (!res) { | |
759 | TLVPrintAIDlistFromSelectTLV(tlvSelect); | |
760 | EMVSelectApplication(tlvSelect, AID, &AIDlen); | |
761 | } | |
762 | } | |
763 | ||
764 | // Search | |
765 | if (!AIDlen) { | |
766 | PrintAndLog("\n* Search AID in list."); | |
767 | SetAPDULogging(false); | |
768 | if (EMVSearch(activateField, true, decodeTLV, tlvSelect)) { | |
d03fb293 | 769 | dreturn(2); |
3c5fce2b OM |
770 | } |
771 | ||
772 | // check search and select application id | |
773 | TLVPrintAIDlistFromSelectTLV(tlvSelect); | |
774 | EMVSelectApplication(tlvSelect, AID, &AIDlen); | |
775 | } | |
776 | ||
777 | // Init TLV tree | |
3c5fce2b OM |
778 | const char *alr = "Root terminal TLV tree"; |
779 | tlvRoot = tlvdb_fixed(1, strlen(alr), (const unsigned char *)alr); | |
780 | ||
781 | // check if we found EMV application on card | |
782 | if (!AIDlen) { | |
783 | PrintAndLog("Can't select AID. EMV AID not found"); | |
d03fb293 | 784 | dreturn(2); |
3c5fce2b OM |
785 | } |
786 | ||
787 | // Select | |
788 | PrintAndLog("\n* Selecting AID:%s", sprint_hex_inrow(AID, AIDlen)); | |
789 | SetAPDULogging(showAPDU); | |
790 | res = EMVSelect(false, true, AID, AIDlen, buf, sizeof(buf), &len, &sw, tlvRoot); | |
791 | ||
792 | if (res) { | |
793 | PrintAndLog("Can't select AID (%d). Exit...", res); | |
d03fb293 | 794 | dreturn(3); |
3c5fce2b OM |
795 | } |
796 | ||
797 | if (decodeTLV) | |
798 | TLVPrintFromBuffer(buf, len); | |
799 | PrintAndLog("* Selected."); | |
800 | ||
3c5fce2b OM |
801 | PrintAndLog("\n* Init transaction parameters."); |
802 | ||
556826b5 OM |
803 | ParamLoadDefaults(tlvRoot); |
804 | ||
805 | if (paramLoadJSON) { | |
806 | PrintAndLog("* * Transaction parameters loading from JSON..."); | |
807 | ParamLoadFromJson(tlvRoot); | |
808 | } | |
809 | ||
810 | //9F66:(Terminal Transaction Qualifiers (TTQ)) len:4 | |
d03fb293 OM |
811 | char *qVSDC = "\x26\x00\x00\x00"; |
812 | if (GenACGPO) { | |
813 | qVSDC = "\x26\x80\x00\x00"; | |
814 | } | |
10d4f823 | 815 | switch(TrType) { |
816 | case TT_MSD: | |
817 | TLV_ADD(0x9F66, "\x86\x00\x00\x00"); // MSD | |
818 | break; | |
d03fb293 | 819 | // not standard for contactless. just for test. |
10d4f823 | 820 | case TT_VSDC: |
821 | TLV_ADD(0x9F66, "\x46\x00\x00\x00"); // VSDC | |
822 | break; | |
823 | case TT_QVSDCMCHIP: | |
d03fb293 | 824 | TLV_ADD(0x9F66, qVSDC); // qVSDC |
10d4f823 | 825 | break; |
826 | case TT_CDA: | |
d03fb293 | 827 | TLV_ADD(0x9F66, qVSDC); // qVSDC (VISA CDA not enabled) |
10d4f823 | 828 | break; |
829 | default: | |
10d4f823 | 830 | break; |
831 | } | |
d03fb293 | 832 | |
66efdc1f | 833 | TLVPrintFromTLV(tlvRoot); // TODO delete!!! |
3c5fce2b OM |
834 | |
835 | PrintAndLog("\n* Calc PDOL."); | |
d03fb293 | 836 | pdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x9f38, NULL), tlvRoot, 0x83); |
3c5fce2b OM |
837 | if (!pdol_data_tlv){ |
838 | PrintAndLog("ERROR: can't create PDOL TLV."); | |
d03fb293 | 839 | dreturn(4); |
3c5fce2b OM |
840 | } |
841 | ||
842 | size_t pdol_data_tlv_data_len; | |
843 | unsigned char *pdol_data_tlv_data = tlv_encode(pdol_data_tlv, &pdol_data_tlv_data_len); | |
844 | if (!pdol_data_tlv_data) { | |
845 | PrintAndLog("ERROR: can't create PDOL data."); | |
d03fb293 | 846 | dreturn(4); |
3c5fce2b OM |
847 | } |
848 | PrintAndLog("PDOL data[%d]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len)); | |
849 | ||
3c5fce2b OM |
850 | PrintAndLog("\n* GPO."); |
851 | res = EMVGPO(true, pdol_data_tlv_data, pdol_data_tlv_data_len, buf, sizeof(buf), &len, &sw, tlvRoot); | |
852 | ||
10d4f823 | 853 | free(pdol_data_tlv_data); |
d03fb293 | 854 | //free(pdol_data_tlv); --- free on exit. |
3c5fce2b OM |
855 | |
856 | if (res) { | |
857 | PrintAndLog("GPO error(%d): %4x. Exit...", res, sw); | |
d03fb293 | 858 | dreturn(5); |
3c5fce2b OM |
859 | } |
860 | ||
861 | // process response template format 1 [id:80 2b AIP + x4b AFL] and format 2 [id:77 TLV] | |
862 | if (buf[0] == 0x80) { | |
3c5fce2b OM |
863 | if (decodeTLV){ |
864 | PrintAndLog("GPO response format1:"); | |
865 | TLVPrintFromBuffer(buf, len); | |
866 | } | |
3c5fce2b | 867 | |
66efdc1f | 868 | if (len < 4 || (len - 4) % 4) { |
869 | PrintAndLog("ERROR: GPO response format1 parsing error. length=%d", len); | |
870 | } else { | |
871 | // AIP | |
872 | struct tlvdb * f1AIP = tlvdb_fixed(0x82, 2, buf + 2); | |
873 | tlvdb_add(tlvRoot, f1AIP); | |
874 | if (decodeTLV){ | |
875 | PrintAndLog("\n* * Decode response format 1 (0x80) AIP and AFL:"); | |
876 | TLVPrintFromTLV(f1AIP); | |
877 | } | |
878 | ||
879 | // AFL | |
880 | struct tlvdb * f1AFL = tlvdb_fixed(0x94, len - 4, buf + 2 + 2); | |
881 | tlvdb_add(tlvRoot, f1AFL); | |
882 | if (decodeTLV) | |
883 | TLVPrintFromTLV(f1AFL); | |
884 | } | |
885 | } else { | |
3c5fce2b OM |
886 | if (decodeTLV) |
887 | TLVPrintFromBuffer(buf, len); | |
888 | } | |
889 | ||
66efdc1f | 890 | // extract PAN from track2 |
891 | { | |
892 | const struct tlv *track2 = tlvdb_get(tlvRoot, 0x57, NULL); | |
893 | if (!tlvdb_get(tlvRoot, 0x5a, NULL) && track2 && track2->len >= 8) { | |
894 | struct tlvdb *pan = GetPANFromTrack2(track2); | |
895 | if (pan) { | |
896 | tlvdb_add(tlvRoot, pan); | |
897 | ||
898 | const struct tlv *pantlv = tlvdb_get(tlvRoot, 0x5a, NULL); | |
899 | PrintAndLog("\n* * Extracted PAN from track2: %s", sprint_hex(pantlv->value, pantlv->len)); | |
900 | } else { | |
901 | PrintAndLog("\n* * WARNING: Can't extract PAN from track2."); | |
902 | } | |
903 | } | |
904 | } | |
905 | ||
3c5fce2b OM |
906 | PrintAndLog("\n* Read records from AFL."); |
907 | const struct tlv *AFL = tlvdb_get(tlvRoot, 0x94, NULL); | |
908 | if (!AFL || !AFL->len) { | |
909 | PrintAndLog("WARNING: AFL not found."); | |
910 | } | |
911 | ||
912 | while(AFL && AFL->len) { | |
913 | if (AFL->len % 4) { | |
914 | PrintAndLog("ERROR: Wrong AFL length: %d", AFL->len); | |
915 | break; | |
916 | } | |
917 | ||
918 | for (int i = 0; i < AFL->len / 4; i++) { | |
919 | uint8_t SFI = AFL->value[i * 4 + 0] >> 3; | |
920 | uint8_t SFIstart = AFL->value[i * 4 + 1]; | |
921 | uint8_t SFIend = AFL->value[i * 4 + 2]; | |
922 | uint8_t SFIoffline = AFL->value[i * 4 + 3]; | |
923 | ||
924 | PrintAndLog("* * SFI[%02x] start:%02x end:%02x offline:%02x", SFI, SFIstart, SFIend, SFIoffline); | |
925 | if (SFI == 0 || SFI == 31 || SFIstart == 0 || SFIstart > SFIend) { | |
926 | PrintAndLog("SFI ERROR! Skipped..."); | |
927 | continue; | |
928 | } | |
929 | ||
930 | for(int n = SFIstart; n <= SFIend; n++) { | |
931 | PrintAndLog("* * * SFI[%02x] %d", SFI, n); | |
932 | ||
933 | res = EMVReadRecord(true, SFI, n, buf, sizeof(buf), &len, &sw, tlvRoot); | |
934 | if (res) { | |
935 | PrintAndLog("ERROR SFI[%02x]. APDU error %4x", SFI, sw); | |
936 | continue; | |
937 | } | |
938 | ||
939 | if (decodeTLV) { | |
940 | TLVPrintFromBuffer(buf, len); | |
941 | PrintAndLog(""); | |
942 | } | |
943 | ||
d03fb293 OM |
944 | // Build Input list for Offline Data Authentication |
945 | // EMV 4.3 book3 10.3, page 96 | |
3c5fce2b | 946 | if (SFIoffline) { |
d03fb293 OM |
947 | if (SFI < 11) { |
948 | const unsigned char *abuf = buf; | |
949 | size_t elmlen = len; | |
950 | struct tlv e; | |
951 | if (tlv_parse_tl(&abuf, &elmlen, &e)) { | |
952 | memcpy(&ODAiList[ODAiListLen], &buf[len - elmlen], elmlen); | |
953 | ODAiListLen += elmlen; | |
954 | } else { | |
955 | PrintAndLog("ERROR SFI[%02x]. Creating input list for Offline Data Authentication error.", SFI); | |
956 | } | |
957 | } else { | |
958 | memcpy(&ODAiList[ODAiListLen], buf, len); | |
959 | ODAiListLen += len; | |
960 | } | |
3c5fce2b OM |
961 | } |
962 | } | |
963 | } | |
964 | ||
965 | break; | |
966 | } | |
967 | ||
d03fb293 OM |
968 | // copy Input list for Offline Data Authentication |
969 | if (ODAiListLen) { | |
970 | struct tlvdb *oda = tlvdb_fixed(0x21, ODAiListLen, ODAiList); // not a standard tag | |
971 | tlvdb_add(tlvRoot, oda); | |
972 | PrintAndLog("* Input list for Offline Data Authentication added to TLV. len=%d \n", ODAiListLen); | |
973 | } | |
974 | ||
10d4f823 | 975 | // get AIP |
66efdc1f | 976 | const struct tlv *AIPtlv = tlvdb_get(tlvRoot, 0x82, NULL); |
977 | uint16_t AIP = AIPtlv->value[0] + AIPtlv->value[1] * 0x100; | |
10d4f823 | 978 | PrintAndLog("* * AIP=%04x", AIP); |
66efdc1f | 979 | |
10d4f823 | 980 | // SDA |
981 | if (AIP & 0x0040) { | |
982 | PrintAndLog("\n* SDA"); | |
d03fb293 | 983 | trSDA(tlvRoot); |
10d4f823 | 984 | } |
985 | ||
986 | // DDA | |
987 | if (AIP & 0x0020) { | |
988 | PrintAndLog("\n* DDA"); | |
d03fb293 | 989 | trDDA(decodeTLV, tlvRoot); |
10d4f823 | 990 | } |
991 | ||
992 | // transaction check | |
993 | ||
66efdc1f | 994 | // qVSDC |
10d4f823 | 995 | if (TrType == TT_QVSDCMCHIP|| TrType == TT_CDA){ |
66efdc1f | 996 | // 9F26: Application Cryptogram |
997 | const struct tlv *AC = tlvdb_get(tlvRoot, 0x9F26, NULL); | |
998 | if (AC) { | |
999 | PrintAndLog("\n--> qVSDC transaction."); | |
1000 | PrintAndLog("* AC path"); | |
1001 | ||
1002 | // 9F36: Application Transaction Counter (ATC) | |
1003 | const struct tlv *ATC = tlvdb_get(tlvRoot, 0x9F36, NULL); | |
1004 | if (ATC) { | |
1005 | ||
1006 | // 9F10: Issuer Application Data - optional | |
1007 | const struct tlv *IAD = tlvdb_get(tlvRoot, 0x9F10, NULL); | |
1008 | ||
1009 | // print AC data | |
1010 | PrintAndLog("ATC: %s", sprint_hex(ATC->value, ATC->len)); | |
1011 | PrintAndLog("AC: %s", sprint_hex(AC->value, AC->len)); | |
1012 | if (IAD){ | |
1013 | PrintAndLog("IAD: %s", sprint_hex(IAD->value, IAD->len)); | |
1014 | ||
1015 | if (IAD->len >= IAD->value[0] + 1) { | |
1016 | PrintAndLog("\tKey index: 0x%02x", IAD->value[1]); | |
1017 | PrintAndLog("\tCrypto ver: 0x%02x(%03d)", IAD->value[2], IAD->value[2]); | |
1018 | PrintAndLog("\tCVR:", sprint_hex(&IAD->value[3], IAD->value[0] - 2)); | |
1019 | struct tlvdb * cvr = tlvdb_fixed(0x20, IAD->value[0] - 2, &IAD->value[3]); | |
1020 | TLVPrintFromTLVLev(cvr, 1); | |
1021 | } | |
1022 | } else { | |
1023 | PrintAndLog("WARNING: IAD not found."); | |
1024 | } | |
1025 | ||
1026 | } else { | |
1027 | PrintAndLog("ERROR AC: Application Transaction Counter (ATC) not found."); | |
1028 | } | |
1029 | } | |
1030 | } | |
1031 | ||
10d4f823 | 1032 | // Mastercard M/CHIP |
1033 | if (GetCardPSVendor(AID, AIDlen) == CV_MASTERCARD && (TrType == TT_QVSDCMCHIP || TrType == TT_CDA)){ | |
66efdc1f | 1034 | const struct tlv *CDOL1 = tlvdb_get(tlvRoot, 0x8c, NULL); |
1035 | if (CDOL1 && GetCardPSVendor(AID, AIDlen) == CV_MASTERCARD) { // and m/chip transaction flag | |
10d4f823 | 1036 | PrintAndLog("\n--> Mastercard M/Chip transaction."); |
1037 | ||
1038 | PrintAndLog("* * Generate challenge"); | |
1039 | res = EMVGenerateChallenge(true, buf, sizeof(buf), &len, &sw, tlvRoot); | |
1040 | if (res) { | |
1041 | PrintAndLog("ERROR GetChallenge. APDU error %4x", sw); | |
d03fb293 | 1042 | dreturn(6); |
10d4f823 | 1043 | } |
1044 | if (len < 4) { | |
1045 | PrintAndLog("ERROR GetChallenge. Wrong challenge length %d", len); | |
d03fb293 | 1046 | dreturn(6); |
10d4f823 | 1047 | } |
1048 | ||
1049 | // ICC Dynamic Number | |
1050 | struct tlvdb * ICCDynN = tlvdb_fixed(0x9f4c, len, buf); | |
1051 | tlvdb_add(tlvRoot, ICCDynN); | |
1052 | if (decodeTLV){ | |
1053 | PrintAndLog("\n* * ICC Dynamic Number:"); | |
1054 | TLVPrintFromTLV(ICCDynN); | |
1055 | } | |
1056 | ||
1057 | PrintAndLog("* * Calc CDOL1"); | |
1058 | struct tlv *cdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x8c, NULL), tlvRoot, 0x01); // 0x01 - dummy tag | |
1059 | if (!cdol_data_tlv){ | |
1060 | PrintAndLog("ERROR: can't create CDOL1 TLV."); | |
d03fb293 | 1061 | dreturn(6); |
10d4f823 | 1062 | } |
1063 | PrintAndLog("CDOL1 data[%d]: %s", cdol_data_tlv->len, sprint_hex(cdol_data_tlv->value, cdol_data_tlv->len)); | |
1064 | ||
1065 | PrintAndLog("* * AC1"); | |
1066 | // EMVAC_TC + EMVAC_CDAREQ --- to get SDAD | |
1067 | res = EMVAC(true, (TrType == TT_CDA) ? EMVAC_TC + EMVAC_CDAREQ : EMVAC_TC, (uint8_t *)cdol_data_tlv->value, cdol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot); | |
1068 | ||
10d4f823 | 1069 | if (res) { |
1070 | PrintAndLog("AC1 error(%d): %4x. Exit...", res, sw); | |
d03fb293 | 1071 | dreturn(7); |
10d4f823 | 1072 | } |
1073 | ||
1074 | if (decodeTLV) | |
1075 | TLVPrintFromBuffer(buf, len); | |
1076 | ||
d03fb293 OM |
1077 | // CDA |
1078 | PrintAndLog("\n* CDA:"); | |
1079 | struct tlvdb *ac_tlv = tlvdb_parse_multi(buf, len); | |
1080 | res = trCDA(tlvRoot, ac_tlv, pdol_data_tlv, cdol_data_tlv); | |
1081 | if (res) { | |
1082 | PrintAndLog("CDA error (%d)", res); | |
1083 | } | |
1084 | free(ac_tlv); | |
1085 | free(cdol_data_tlv); | |
1086 | ||
1087 | PrintAndLog("\n* M/Chip transaction result:"); | |
10d4f823 | 1088 | // 9F27: Cryptogram Information Data (CID) |
1089 | const struct tlv *CID = tlvdb_get(tlvRoot, 0x9F27, NULL); | |
1090 | if (CID) { | |
1091 | emv_tag_dump(CID, stdout, 0); | |
1092 | PrintAndLog("------------------------------"); | |
1093 | if (CID->len > 0) { | |
1094 | switch(CID->value[0] & EMVAC_AC_MASK){ | |
1095 | case EMVAC_AAC: | |
1096 | PrintAndLog("Transaction DECLINED."); | |
1097 | break; | |
1098 | case EMVAC_TC: | |
1099 | PrintAndLog("Transaction approved OFFLINE."); | |
1100 | break; | |
1101 | case EMVAC_ARQC: | |
1102 | PrintAndLog("Transaction approved ONLINE."); | |
1103 | break; | |
1104 | default: | |
1105 | PrintAndLog("ERROR: CID transaction code error %2x", CID->value[0] & EMVAC_AC_MASK); | |
1106 | break; | |
1107 | } | |
1108 | } else { | |
1109 | PrintAndLog("ERROR: Wrong CID length %d", CID->len); | |
1110 | } | |
1111 | } else { | |
1112 | PrintAndLog("ERROR: CID(9F27) not found."); | |
1113 | } | |
66efdc1f | 1114 | |
1115 | } | |
1116 | } | |
1117 | ||
1118 | // MSD | |
10d4f823 | 1119 | if (AIP & 0x8000 && TrType == TT_MSD) { |
66efdc1f | 1120 | PrintAndLog("\n--> MSD transaction."); |
1121 | ||
66efdc1f | 1122 | PrintAndLog("* MSD dCVV path. Check dCVV"); |
1123 | ||
1124 | const struct tlv *track2 = tlvdb_get(tlvRoot, 0x57, NULL); | |
1125 | if (track2) { | |
1126 | PrintAndLog("Track2: %s", sprint_hex(track2->value, track2->len)); | |
1127 | ||
1128 | struct tlvdb *dCVV = GetdCVVRawFromTrack2(track2); | |
1129 | PrintAndLog("dCVV raw data:"); | |
1130 | TLVPrintFromTLV(dCVV); | |
1131 | ||
1132 | if (GetCardPSVendor(AID, AIDlen) == CV_MASTERCARD) { | |
1133 | PrintAndLog("\n* Mastercard calculate UDOL"); | |
1134 | ||
1135 | // UDOL (9F69) | |
1136 | const struct tlv *UDOL = tlvdb_get(tlvRoot, 0x9F69, NULL); | |
1137 | // UDOL(9F69) default: 9F6A (Unpredictable number) 4 bytes | |
1138 | const struct tlv defUDOL = { | |
1139 | .tag = 0x01, | |
1140 | .len = 3, | |
1141 | .value = (uint8_t *)"\x9f\x6a\x04", | |
1142 | }; | |
1143 | if (!UDOL) | |
1144 | PrintAndLog("Use default UDOL."); | |
1145 | ||
10d4f823 | 1146 | struct tlv *udol_data_tlv = dol_process(UDOL ? UDOL : &defUDOL, tlvRoot, 0x01); // 0x01 - dummy tag |
66efdc1f | 1147 | if (!udol_data_tlv){ |
1148 | PrintAndLog("ERROR: can't create UDOL TLV."); | |
d03fb293 | 1149 | dreturn(8); |
66efdc1f | 1150 | } |
66efdc1f | 1151 | |
78528074 | 1152 | PrintAndLog("UDOL data[%d]: %s", udol_data_tlv->len, sprint_hex(udol_data_tlv->value, udol_data_tlv->len)); |
66efdc1f | 1153 | |
1154 | PrintAndLog("\n* Mastercard compute cryptographic checksum(UDOL)"); | |
1155 | ||
78528074 | 1156 | res = MSCComputeCryptoChecksum(true, (uint8_t *)udol_data_tlv->value, udol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot); |
66efdc1f | 1157 | if (res) { |
1158 | PrintAndLog("ERROR Compute Crypto Checksum. APDU error %4x", sw); | |
d03fb293 OM |
1159 | free(udol_data_tlv); |
1160 | dreturn(9); | |
66efdc1f | 1161 | } |
1162 | ||
1163 | if (decodeTLV) { | |
1164 | TLVPrintFromBuffer(buf, len); | |
1165 | PrintAndLog(""); | |
1166 | } | |
d03fb293 | 1167 | free(udol_data_tlv); |
66efdc1f | 1168 | |
1169 | } | |
1170 | } else { | |
1171 | PrintAndLog("ERROR MSD: Track2 data not found."); | |
1172 | } | |
1173 | } | |
d03fb293 | 1174 | |
3c5fce2b OM |
1175 | // DropField |
1176 | DropField(); | |
1177 | ||
1178 | // Destroy TLV's | |
d03fb293 | 1179 | free(pdol_data_tlv); |
3c5fce2b OM |
1180 | tlvdb_free(tlvSelect); |
1181 | tlvdb_free(tlvRoot); | |
1182 | ||
1183 | PrintAndLog("\n* Transaction completed."); | |
1184 | ||
1185 | return 0; | |
1186 | } | |
1187 | ||
556826b5 OM |
1188 | int UsageCmdHFEMVScan(void) { |
1189 | PrintAndLog("HELP : Scan EMV card and save it contents to a file. \n"); | |
1190 | PrintAndLog(" It executes EMV contactless transaction and saves result to a file which can be used for emulation.\n"); | |
1191 | PrintAndLog("Usage: hf emv scan [-a][-t][-v][-c][-x][-g] <file_name>\n"); | |
1192 | PrintAndLog(" Options:"); | |
1193 | PrintAndLog(" -a : show APDU reqests and responses\n"); | |
1194 | PrintAndLog(" -t : TLV decode results\n"); | |
1195 | PrintAndLog(" -v : transaction type - qVSDC or M/Chip.\n"); | |
1196 | PrintAndLog(" -c : transaction type - qVSDC or M/Chip plus CDA (SDAD generation).\n"); | |
1197 | PrintAndLog(" -x : transaction type - VSDC. For test only. Not a standart behavior.\n"); | |
1198 | PrintAndLog(" -g : VISA. generate AC from GPO\n"); | |
1199 | PrintAndLog("By default : transaction type - MSD.\n"); | |
1200 | PrintAndLog("Samples:"); | |
1201 | PrintAndLog(" hf emv scan -a -t -> scan MSD transaction mode"); | |
1202 | PrintAndLog(" hf emv scan -a -t -c -> scan CDA transaction mode"); | |
1203 | return 0; | |
1204 | } | |
1205 | ||
1206 | int CmdHFEMVScan(const char *cmd) { | |
1207 | UsageCmdHFEMVScan(); | |
1208 | ||
1209 | return 0; | |
1210 | } | |
1211 | ||
d03fb293 OM |
1212 | int CmdHFEMVTest(const char *cmd) { |
1213 | return ExecuteCryptoTests(true); | |
1214 | } | |
1215 | ||
3c5fce2b OM |
1216 | int CmdHelp(const char *Cmd); |
1217 | static command_t CommandTable[] = { | |
626e650f | 1218 | {"help", CmdHelp, 1, "This help"}, |
1219 | {"exec", CmdHFEMVExec, 0, "Executes EMV contactless transaction."}, | |
1220 | {"pse", CmdHFEMVPPSE, 0, "Execute PPSE. It selects 2PAY.SYS.DDF01 or 1PAY.SYS.DDF01 directory."}, | |
1221 | {"search", CmdHFEMVSearch, 0, "Try to select all applets from applets list and print installed applets."}, | |
1222 | {"select", CmdHFEMVSelect, 0, "Select applet."}, | |
1223 | {"gpo", CmdHFEMVGPO, 0, "Execute GetProcessingOptions."}, | |
1224 | {"readrec", CmdHFEMVReadRecord, 0, "Read files from card."}, | |
1225 | {"genac", CmdHFEMVAC, 0, "Generate ApplicationCryptogram."}, | |
1226 | {"challenge", CmdHFEMVGenerateChallenge, 0, "Generate challenge."}, | |
1227 | {"intauth", CmdHFEMVInternalAuthenticate, 0, "Internal authentication."}, | |
556826b5 | 1228 | // {"scan", CmdHFEMVScan, 0, "Scan EMV card and save it contents to json file for emulator."}, |
11a78e04 | 1229 | {"test", CmdHFEMVTest, 0, "Crypto logic test."}, |
3c5fce2b OM |
1230 | {NULL, NULL, 0, NULL} |
1231 | }; | |
1232 | ||
1233 | int CmdHFEMV(const char *Cmd) { | |
1234 | CmdsParse(CommandTable, Cmd); | |
1235 | return 0; | |
1236 | } | |
1237 | ||
1238 | int CmdHelp(const char *Cmd) { | |
1239 | CmdsHelp(CommandTable); | |
1240 | return 0; | |
1241 | } |