Merge pull request #491 from pwpiwi/mod_select
[proxmark3-svn] / client / emv / cmdemv.c
CommitLineData
3c5fce2b
OM
1//-----------------------------------------------------------------------------
2// Copyright (C) 2017 Merlok
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
11#include "cmdemv.h"
12
13int UsageCmdHFEMVSelect(void) {
14 PrintAndLog("HELP : Executes select applet command:\n");
15 PrintAndLog("Usage: hf emv select [-s][-k][-a][-t] <HEX applet AID>\n");
16 PrintAndLog(" Options:");
17 PrintAndLog(" -s : select card");
18 PrintAndLog(" -k : keep field for next command");
19 PrintAndLog(" -a : show APDU reqests and responses\n");
20 PrintAndLog(" -t : TLV decode results\n");
21 PrintAndLog("Samples:");
22 PrintAndLog(" hf emv select -s a00000000101 -> select card, select applet");
23 PrintAndLog(" hf emv select -s -t a00000000101 -> select card, select applet, show result in TLV");
24 return 0;
25}
26
27int CmdHFEMVSelect(const char *cmd) {
28 uint8_t data[APDU_AID_LEN] = {0};
29 int datalen = 0;
30 bool activateField = false;
31 bool leaveSignalON = false;
32 bool decodeTLV = false;
33
34 if (strlen(cmd) < 1) {
35 UsageCmdHFEMVSelect();
36 return 0;
37 }
38
39 SetAPDULogging(false);
40
41 int cmdp = 0;
42 while(param_getchar(cmd, cmdp) != 0x00) {
43 char c = param_getchar(cmd, cmdp);
44 if ((c == '-') && (param_getlength(cmd, cmdp) == 2))
45 switch (param_getchar_indx(cmd, 1, cmdp)) {
46 case 'h':
47 case 'H':
48 UsageCmdHFEMVSelect();
49 return 0;
50 case 's':
51 case 'S':
52 activateField = true;
53 break;
54 case 'k':
55 case 'K':
56 leaveSignalON = true;
57 break;
58 case 'a':
59 case 'A':
60 SetAPDULogging(true);
61 break;
62 case 't':
63 case 'T':
64 decodeTLV = true;
65 break;
66 default:
67 PrintAndLog("Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp));
68 return 1;
69 }
70
71 if (isxdigit(c)) {
72 switch(param_gethex_to_eol(cmd, cmdp, data, sizeof(data), &datalen)) {
73 case 1:
74 PrintAndLog("Invalid HEX value.");
75 return 1;
76 case 2:
77 PrintAndLog("AID too large.");
78 return 1;
79 case 3:
80 PrintAndLog("Hex must have even number of digits.");
81 return 1;
82 }
83
84 // we get all the hex to end of line with spaces
85 break;
86 }
87
88
89 cmdp++;
90 }
91
92 // exec
93 uint8_t buf[APDU_RES_LEN] = {0};
94 size_t len = 0;
95 uint16_t sw = 0;
96 int res = EMVSelect(activateField, leaveSignalON, data, datalen, buf, sizeof(buf), &len, &sw, NULL);
97
98 if (sw)
99 PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
100
101 if (res)
102 return res;
103
104 if (decodeTLV)
105 TLVPrintFromBuffer(buf, len);
106
107 return 0;
108}
109
110int UsageCmdHFEMVSearch(void) {
111 PrintAndLog("HELP : Tries to select all applets from applet list:\n");
112 PrintAndLog("Usage: hf emv search [-s][-k][-a][-t]\n");
113 PrintAndLog(" Options:");
114 PrintAndLog(" -s : select card");
115 PrintAndLog(" -k : keep field for next command");
116 PrintAndLog(" -a : show APDU reqests and responses\n");
117 PrintAndLog(" -t : TLV decode results of selected applets\n");
118 PrintAndLog("Samples:");
119 PrintAndLog(" hf emv search -s -> select card and search");
120 PrintAndLog(" hf emv search -s -t -> select card, search and show result in TLV");
121 return 0;
122}
123
124int CmdHFEMVSearch(const char *cmd) {
125
126 bool activateField = false;
127 bool leaveSignalON = false;
128 bool decodeTLV = false;
129
130 if (strlen(cmd) < 1) {
131 UsageCmdHFEMVSearch();
132 return 0;
133 }
134
135 SetAPDULogging(false);
136
137 int cmdp = 0;
138 while(param_getchar(cmd, cmdp) != 0x00) {
139 char c = param_getchar(cmd, cmdp);
140 if ((c == '-') && (param_getlength(cmd, cmdp) == 2))
141 switch (param_getchar_indx(cmd, 1, cmdp)) {
142 case 'h':
143 case 'H':
144 UsageCmdHFEMVSearch();
145 return 0;
146 case 's':
147 case 'S':
148 activateField = true;
149 break;
150 case 'k':
151 case 'K':
152 leaveSignalON = true;
153 break;
154 case 'a':
155 case 'A':
156 SetAPDULogging(true);
157 break;
158 case 't':
159 case 'T':
160 decodeTLV = true;
161 break;
162 default:
163 PrintAndLog("Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp));
164 return 1;
165 }
166 cmdp++;
167 }
168
169 struct tlvdb *t = NULL;
170 const char *al = "Applets list";
171 t = tlvdb_fixed(1, strlen(al), (const unsigned char *)al);
172
173 if (EMVSearch(activateField, leaveSignalON, decodeTLV, t)) {
174 tlvdb_free(t);
175 return 2;
176 }
177
178 PrintAndLog("Search completed.");
179
180 // print list here
181 if (!decodeTLV) {
182 TLVPrintAIDlistFromSelectTLV(t);
183 }
184
185 tlvdb_free(t);
186
187 return 0;
188}
189
190int UsageCmdHFEMVPPSE(void) {
191 PrintAndLog("HELP : Executes PSE/PPSE select command. It returns list of applet on the card:\n");
192 PrintAndLog("Usage: hf emv pse [-s][-k][-1][-2][-a][-t]\n");
193 PrintAndLog(" Options:");
194 PrintAndLog(" -s : select card");
195 PrintAndLog(" -k : keep field for next command");
196 PrintAndLog(" -1 : ppse (1PAY.SYS.DDF01)");
197 PrintAndLog(" -2 : pse (2PAY.SYS.DDF01)");
198 PrintAndLog(" -a : show APDU reqests and responses\n");
199 PrintAndLog(" -t : TLV decode results\n");
200 PrintAndLog("Samples:");
201 PrintAndLog(" hf emv pse -s -1 -> select, get pse");
202 PrintAndLog(" hf emv pse -s -k -2 -> select, get ppse, keep field");
203 PrintAndLog(" hf emv pse -s -t -2 -> select, get ppse, show result in TLV");
204 return 0;
205}
206
207int CmdHFEMVPPSE(const char *cmd) {
208
209 uint8_t PSENum = 2;
210 bool activateField = false;
211 bool leaveSignalON = false;
212 bool decodeTLV = false;
213
214 if (strlen(cmd) < 1) {
215 UsageCmdHFEMVPPSE();
216 return 0;
217 }
218
219 SetAPDULogging(false);
220
221 int cmdp = 0;
222 while(param_getchar(cmd, cmdp) != 0x00) {
223 char c = param_getchar(cmd, cmdp);
224 if ((c == '-') && (param_getlength(cmd, cmdp) == 2))
225 switch (param_getchar_indx(cmd, 1, cmdp)) {
226 case 'h':
227 case 'H':
228 UsageCmdHFEMVPPSE();
229 return 0;
230 case 's':
231 case 'S':
232 activateField = true;
233 break;
234 case 'k':
235 case 'K':
236 leaveSignalON = true;
237 break;
238 case 'a':
239 case 'A':
240 SetAPDULogging(true);
241 break;
242 case 't':
243 case 'T':
244 decodeTLV = true;
245 break;
246 case '1':
247 PSENum = 1;
248 break;
249 case '2':
250 PSENum = 2;
251 break;
252 default:
253 PrintAndLog("Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp));
254 return 1;
255 }
256 cmdp++;
257 }
258
259 // exec
260 uint8_t buf[APDU_RES_LEN] = {0};
261 size_t len = 0;
262 uint16_t sw = 0;
263 int res = EMVSelectPSE(activateField, leaveSignalON, PSENum, buf, sizeof(buf), &len, &sw);
264
265 if (sw)
266 PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
267
268 if (res)
269 return res;
270
271
272 if (decodeTLV)
273 TLVPrintFromBuffer(buf, len);
274
275 return 0;
276}
277
278int UsageCmdHFEMVExec(void) {
279 PrintAndLog("HELP : Executes EMV contactless transaction:\n");
280 PrintAndLog("Usage: hf emv exec [-s][-a][-t]\n");
281 PrintAndLog(" Options:");
282 PrintAndLog(" -s : select card");
283 PrintAndLog(" -a : show APDU reqests and responses\n");
284 PrintAndLog(" -t : TLV decode results\n");
285 PrintAndLog(" -f : force search AID. Search AID instead of execute PPSE.\n");
286 PrintAndLog("Samples:");
287 PrintAndLog(" hf emv pse -s -> select card");
288 PrintAndLog(" hf emv pse -s -t -a -> select card, show responses in TLV, show APDU");
289 return 0;
290}
291
292#define TLV_ADD(tag, value)( tlvdb_add(tlvRoot, tlvdb_fixed(tag, sizeof(value) - 1, (const unsigned char *)value)) )
293
294int CmdHFEMVExec(const char *cmd) {
295 bool activateField = false;
296 bool showAPDU = false;
297 bool decodeTLV = false;
298 bool forceSearch = false;
299
300 uint8_t buf[APDU_RES_LEN] = {0};
301 size_t len = 0;
302 uint16_t sw = 0;
303 uint8_t AID[APDU_AID_LEN] = {0};
304 size_t AIDlen = 0;
305
306 int res;
307
308 if (strlen(cmd) < 1) {
309 UsageCmdHFEMVExec();
310 return 0;
311 }
312
313 int cmdp = 0;
314 while(param_getchar(cmd, cmdp) != 0x00) {
315 char c = param_getchar(cmd, cmdp);
316 if ((c == '-') && (param_getlength(cmd, cmdp) == 2))
317 switch (param_getchar_indx(cmd, 1, cmdp)) {
318 case 'h':
319 case 'H':
320 UsageCmdHFEMVPPSE();
321 return 0;
322 case 's':
323 case 'S':
324 activateField = true;
325 break;
326 case 'a':
327 case 'A':
328 showAPDU = true;
329 break;
330 case 't':
331 case 'T':
332 decodeTLV = true;
333 break;
334 case 'f':
335 case 'F':
336 forceSearch = true;
337 break;
338 default:
339 PrintAndLog("Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp));
340 return 1;
341 }
342 cmdp++;
343 }
344
345
346 // init applets list tree
347 struct tlvdb *tlvSelect = NULL;
348 const char *al = "Applets list";
349 tlvSelect = tlvdb_fixed(1, strlen(al), (const unsigned char *)al);
350
351 // Application Selection
352 // https://www.openscdp.org/scripts/tutorial/emv/applicationselection.html
353 if (!forceSearch) {
354 // PPSE
355 PrintAndLog("\n* PPSE.");
356 SetAPDULogging(showAPDU);
357 res = EMVSearchPSE(activateField, true, decodeTLV, tlvSelect);
358
359 // check PPSE and select application id
360 if (!res) {
361 TLVPrintAIDlistFromSelectTLV(tlvSelect);
362 EMVSelectApplication(tlvSelect, AID, &AIDlen);
363 }
364 }
365
366 // Search
367 if (!AIDlen) {
368 PrintAndLog("\n* Search AID in list.");
369 SetAPDULogging(false);
370 if (EMVSearch(activateField, true, decodeTLV, tlvSelect)) {
371 tlvdb_free(tlvSelect);
372 return 2;
373 }
374
375 // check search and select application id
376 TLVPrintAIDlistFromSelectTLV(tlvSelect);
377 EMVSelectApplication(tlvSelect, AID, &AIDlen);
378 }
379
380 // Init TLV tree
381 struct tlvdb *tlvRoot = NULL;
382 const char *alr = "Root terminal TLV tree";
383 tlvRoot = tlvdb_fixed(1, strlen(alr), (const unsigned char *)alr);
384
385 // check if we found EMV application on card
386 if (!AIDlen) {
387 PrintAndLog("Can't select AID. EMV AID not found");
388 return 2;
389 }
390
391 // Select
392 PrintAndLog("\n* Selecting AID:%s", sprint_hex_inrow(AID, AIDlen));
393 SetAPDULogging(showAPDU);
394 res = EMVSelect(false, true, AID, AIDlen, buf, sizeof(buf), &len, &sw, tlvRoot);
395
396 if (res) {
397 PrintAndLog("Can't select AID (%d). Exit...", res);
398 return 3;
399 }
400
401 if (decodeTLV)
402 TLVPrintFromBuffer(buf, len);
403 PrintAndLog("* Selected.");
404
405PrintAndLog("-----BREAK.");
406return 0;
407 PrintAndLog("\n* Init transaction parameters.");
408
409 //9F66:(Terminal Transaction Qualifiers (TTQ)) len:4
410 TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // E6
411 //9F02:(Amount, Authorised (Numeric)) len:6
412 TLV_ADD(0x9F02, "\x00\x00\x00\x00\x01\x00");
413 //9F1A:(Terminal Country Code) len:2
414 TLV_ADD(0x9F1A, "ru");
415 //5F2A:(Transaction Currency Code) len:2
416 // USD 840, EUR 978, RUR 810, RUB 643, RUR 810(old), UAH 980, AZN 031, n/a 999
417 TLV_ADD(0x5F2A, "\x09\x80");
418 //9A:(Transaction Date) len:3
419 TLV_ADD(0x9A, "\x00\x00\x00");
420 //9C:(Transaction Type) len:1 | 00 => Goods and service #01 => Cash
421 TLV_ADD(0x9C, "\x00");
422 // 9F37 Unpredictable Number len:4
423 TLV_ADD(0x9F37, "\x01\x02\x03\x04");
424
425 TLVPrintFromTLV(tlvRoot);
426
427 PrintAndLog("\n* Calc PDOL.");
428 struct tlv *pdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x9f38, NULL), tlvRoot, 0x83);
429 if (!pdol_data_tlv){
430 PrintAndLog("ERROR: can't create PDOL TLV.");
431 return 4;
432 }
433
434 size_t pdol_data_tlv_data_len;
435 unsigned char *pdol_data_tlv_data = tlv_encode(pdol_data_tlv, &pdol_data_tlv_data_len);
436 if (!pdol_data_tlv_data) {
437 PrintAndLog("ERROR: can't create PDOL data.");
438 return 4;
439 }
440 PrintAndLog("PDOL data[%d]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len));
441
442//PrintAndLog("-----BREAK.");
443//return 0;
444 PrintAndLog("\n* GPO.");
445 res = EMVGPO(true, pdol_data_tlv_data, pdol_data_tlv_data_len, buf, sizeof(buf), &len, &sw, tlvRoot);
446
447 free(pdol_data_tlv);
448
449 if (res) {
450 PrintAndLog("GPO error(%d): %4x. Exit...", res, sw);
451 return 5;
452 }
453
454 // process response template format 1 [id:80 2b AIP + x4b AFL] and format 2 [id:77 TLV]
455 if (buf[0] == 0x80) {
456
457
458
459 if (decodeTLV){
460 PrintAndLog("GPO response format1:");
461 TLVPrintFromBuffer(buf, len);
462 }
463 } else {
464
465
466
467 if (decodeTLV)
468 TLVPrintFromBuffer(buf, len);
469 }
470
471 PrintAndLog("\n* Read records from AFL.");
472 const struct tlv *AFL = tlvdb_get(tlvRoot, 0x94, NULL);
473 if (!AFL || !AFL->len) {
474 PrintAndLog("WARNING: AFL not found.");
475 }
476
477 while(AFL && AFL->len) {
478 if (AFL->len % 4) {
479 PrintAndLog("ERROR: Wrong AFL length: %d", AFL->len);
480 break;
481 }
482
483 for (int i = 0; i < AFL->len / 4; i++) {
484 uint8_t SFI = AFL->value[i * 4 + 0] >> 3;
485 uint8_t SFIstart = AFL->value[i * 4 + 1];
486 uint8_t SFIend = AFL->value[i * 4 + 2];
487 uint8_t SFIoffline = AFL->value[i * 4 + 3];
488
489 PrintAndLog("* * SFI[%02x] start:%02x end:%02x offline:%02x", SFI, SFIstart, SFIend, SFIoffline);
490 if (SFI == 0 || SFI == 31 || SFIstart == 0 || SFIstart > SFIend) {
491 PrintAndLog("SFI ERROR! Skipped...");
492 continue;
493 }
494
495 for(int n = SFIstart; n <= SFIend; n++) {
496 PrintAndLog("* * * SFI[%02x] %d", SFI, n);
497
498 res = EMVReadRecord(true, SFI, n, buf, sizeof(buf), &len, &sw, tlvRoot);
499 if (res) {
500 PrintAndLog("ERROR SFI[%02x]. APDU error %4x", SFI, sw);
501 continue;
502 }
503
504 if (decodeTLV) {
505 TLVPrintFromBuffer(buf, len);
506 PrintAndLog("");
507 }
508
509 if (SFIoffline) {
510 // here will be offline records storing...
511 // dont foget: if (sfi < 11)
512 }
513 }
514 }
515
516 break;
517 }
518
519 // additional contacless EMV commands (fDDA, CDA, external authenticate)
520
521
522 // DropField
523 DropField();
524
525 // Destroy TLV's
526 tlvdb_free(tlvSelect);
527 tlvdb_free(tlvRoot);
528
529 PrintAndLog("\n* Transaction completed.");
530
531 return 0;
532}
533
534int CmdHelp(const char *Cmd);
535static command_t CommandTable[] = {
536 {"help", CmdHelp, 1, "This help"},
537 {"exec", CmdHFEMVExec, 0, "Executes EMV contactless transaction."},
538 {"pse", CmdHFEMVPPSE, 0, "Execute PPSE. It selects 2PAY.SYS.DDF01 or 1PAY.SYS.DDF01 directory."},
539 {"search", CmdHFEMVSearch, 0, "Try to select all applets from applets list and print installed applets."},
540 {"select", CmdHFEMVSelect, 0, "Select applet."},
541 {NULL, NULL, 0, NULL}
542};
543
544int CmdHFEMV(const char *Cmd) {
545 CmdsParse(CommandTable, Cmd);
546 return 0;
547}
548
549int CmdHelp(const char *Cmd) {
550 CmdsHelp(CommandTable);
551 return 0;
552}
Impressum, Datenschutz