]>
Commit | Line | Data |
---|---|---|
a2bb2735 | 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 core functions | |
9 | //----------------------------------------------------------------------------- | |
10 | ||
11 | #include "emvcore.h" | |
12 | ||
3c5fce2b OM |
13 | // Got from here. Thanks) |
14 | // https://eftlab.co.uk/index.php/site-map/knowledge-base/211-emv-aid-rid-pix | |
15 | const char *PSElist [] = { | |
16 | "325041592E5359532E4444463031", // 2PAY.SYS.DDF01 - Visa Proximity Payment System Environment - PPSE | |
17 | "315041592E5359532E4444463031" // 1PAY.SYS.DDF01 - Visa Payment System Environment - PSE | |
18 | }; | |
19 | const size_t PSElistLen = sizeof(PSElist)/sizeof(char*); | |
20 | ||
21 | const char *AIDlist [] = { | |
22 | // Visa International | |
23 | "A00000000305076010", // VISA ELO Credit | |
24 | "A0000000031010", // VISA Debit/Credit (Classic) | |
25 | "A0000000031010", // ddddddddddddddddddddddddddddddddddddddddddddddddddddddddd | |
26 | "A000000003101001", // VISA Credit | |
27 | "A000000003101002", // VISA Debit | |
28 | "A0000000032010", // VISA Electron | |
29 | "A0000000032020", // VISA | |
30 | "A0000000033010", // VISA Interlink | |
31 | "A0000000034010", // VISA Specific | |
32 | "A0000000035010", // VISA Specific | |
33 | "A0000000036010", // Domestic Visa Cash Stored Value | |
34 | "A0000000036020", // International Visa Cash Stored Value | |
35 | "A0000000038002", // VISA Auth, VisaRemAuthen EMV-CAP (DPA) | |
36 | "A0000000038010", // VISA Plus | |
37 | "A0000000039010", // VISA Loyalty | |
38 | "A000000003999910", // VISA Proprietary ATM | |
39 | // Visa USA | |
40 | "A000000098", // Debit Card | |
41 | "A0000000980848", // Debit Card | |
42 | // Mastercard International | |
43 | "A00000000401", // MasterCard PayPass | |
44 | "A0000000041010", // MasterCard Credit | |
45 | "A00000000410101213", // MasterCard Credit | |
46 | "A00000000410101215", // MasterCard Credit | |
47 | "A0000000042010", // MasterCard Specific | |
48 | "A0000000043010", // MasterCard Specific | |
49 | "A0000000043060", // Maestro (Debit) | |
50 | "A000000004306001", // Maestro (Debit) | |
51 | "A0000000044010", // MasterCard Specific | |
52 | "A0000000045010", // MasterCard Specific | |
53 | "A0000000046000", // Cirrus | |
54 | "A0000000048002", // SecureCode Auth EMV-CAP | |
55 | "A0000000049999", // MasterCard PayPass | |
56 | // American Express | |
57 | "A000000025", | |
58 | "A0000000250000", | |
59 | "A00000002501", | |
60 | "A000000025010402", | |
61 | "A000000025010701", | |
62 | "A000000025010801", | |
63 | // Groupement des Cartes Bancaires "CB" | |
64 | "A0000000421010", // Cartes Bancaire EMV Card | |
65 | "A0000000422010", | |
66 | "A0000000423010", | |
67 | "A0000000424010", | |
68 | "A0000000425010", | |
69 | // JCB CO., LTD. | |
70 | "A00000006510", // JCB | |
71 | "A0000000651010", // JCB J Smart Credit | |
72 | "A0000001544442", // Banricompras Debito - Banrisul - Banco do Estado do Rio Grande do SUL - S.A. | |
73 | "F0000000030001", // BRADESCO | |
74 | "A0000005241010", // RuPay - RuPay | |
75 | "D5780000021010" // Bankaxept - Bankaxept | |
76 | }; | |
77 | const size_t AIDlistLen = sizeof(AIDlist)/sizeof(char*); | |
78 | ||
79 | static bool APDULogging = false; | |
80 | void SetAPDULogging(bool logging) { | |
81 | APDULogging = logging; | |
82 | } | |
83 | ||
33a9982c | 84 | static bool print_cb(void *data, const struct tlv *tlv, int level, bool is_leaf) { |
43912d63 | 85 | emv_tag_dump(tlv, stdout, level); |
33a9982c | 86 | if (is_leaf) { |
87 | dump_buffer(tlv->value, tlv->len, stdout, level); | |
88 | } | |
a2bb2735 | 89 | |
90 | return true; | |
91 | } | |
92 | ||
93 | void TLVPrintFromBuffer(uint8_t *data, int datalen) { | |
94 | struct tlvdb *t = NULL; | |
23207d74 | 95 | t = tlvdb_parse_multi(data, datalen); |
a2bb2735 | 96 | if (t) { |
3c5fce2b | 97 | PrintAndLog("-------------------- TLV decoded --------------------"); |
a2bb2735 | 98 | |
43912d63 | 99 | tlvdb_visit(t, print_cb, NULL, 0); |
a2bb2735 | 100 | tlvdb_free(t); |
101 | } else { | |
102 | PrintAndLog("TLV ERROR: Can't parse response as TLV tree."); | |
103 | } | |
104 | } | |
3c5fce2b OM |
105 | |
106 | void TLVPrintFromTLV(struct tlvdb *tlv) { | |
107 | if (!tlv) | |
108 | return; | |
109 | ||
110 | tlvdb_visit(tlv, print_cb, NULL, 0); | |
111 | } | |
112 | ||
113 | void TLVPrintAIDlistFromSelectTLV(struct tlvdb *tlv) { | |
114 | PrintAndLog("|------------------|--------|-------------------------|"); | |
115 | PrintAndLog("| AID |Priority| Name |"); | |
116 | PrintAndLog("|------------------|--------|-------------------------|"); | |
117 | ||
118 | struct tlvdb *ttmp = tlvdb_find(tlv, 0x6f); | |
119 | if (!ttmp) | |
120 | PrintAndLog("| none |"); | |
121 | ||
122 | while (ttmp) { | |
123 | const struct tlv *tgAID = tlvdb_get_inchild(ttmp, 0x84, NULL); | |
124 | const struct tlv *tgName = tlvdb_get_inchild(ttmp, 0x50, NULL); | |
125 | const struct tlv *tgPrio = tlvdb_get_inchild(ttmp, 0x87, NULL); | |
126 | if (!tgAID) | |
127 | break; | |
128 | PrintAndLog("|%s| %s |%s|", | |
129 | sprint_hex_inrow_ex(tgAID->value, tgAID->len, 18), | |
130 | (tgPrio) ? sprint_hex(tgPrio->value, 1) : " ", | |
131 | (tgName) ? sprint_ascii_ex(tgName->value, tgName->len, 25) : " "); | |
132 | ||
133 | ttmp = tlvdb_find_next(ttmp, 0x6f); | |
134 | } | |
135 | ||
136 | PrintAndLog("|------------------|--------|-------------------------|"); | |
137 | } | |
138 | ||
139 | ||
140 | int EMVSelect(bool ActivateField, bool LeaveFieldON, uint8_t *AID, size_t AIDLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { | |
141 | uint8_t data[APDU_RES_LEN] = {0}; | |
142 | *ResultLen = 0; | |
143 | if (sw) *sw = 0; | |
144 | uint16_t isw = 0; | |
145 | ||
146 | // select APDU | |
147 | data[0] = 0x00; | |
148 | data[1] = 0xA4; | |
149 | data[2] = 0x04; | |
150 | data[3] = 0x00; | |
151 | data[4] = AIDLen; | |
152 | memcpy(&data[5], AID, AIDLen); | |
153 | ||
154 | if (ActivateField) | |
155 | DropField(); | |
156 | ||
157 | if (APDULogging) | |
158 | PrintAndLog(">>>> %s", sprint_hex(data, AIDLen + 6)); | |
159 | ||
160 | int res = ExchangeAPDU14a(data, AIDLen + 6, ActivateField, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen); | |
161 | ||
162 | if (APDULogging) | |
163 | PrintAndLog("<<<< %s", sprint_hex(Result, *ResultLen)); | |
164 | ||
165 | if (res) { | |
166 | return res; | |
167 | } | |
168 | ||
169 | if (*ResultLen < 2) { | |
170 | PrintAndLog("SELECT ERROR: returned %d bytes", *ResultLen); | |
171 | return 5; | |
172 | } | |
173 | ||
174 | *ResultLen -= 2; | |
175 | isw = Result[*ResultLen] * 0x0100 + Result[*ResultLen + 1]; | |
176 | if (sw) | |
177 | *sw = isw; | |
178 | ||
179 | if (isw != 0x9000) { | |
180 | if (APDULogging) | |
181 | PrintAndLog("SELECT ERROR: [%4X] %s", isw, GetAPDUCodeDescription(*sw >> 8, *sw & 0xff)); | |
182 | return 5; | |
183 | } | |
184 | ||
185 | // add to tlv tree | |
186 | if (tlv) { | |
187 | struct tlvdb *t = tlvdb_parse_multi(Result, *ResultLen); | |
188 | tlvdb_add(tlv, t); | |
189 | } | |
190 | ||
191 | return 0; | |
192 | } | |
193 | ||
194 | int EMVSelectPSE(bool ActivateField, bool LeaveFieldON, uint8_t PSENum, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) { | |
195 | uint8_t buf[APDU_AID_LEN] = {0}; | |
196 | *ResultLen = 0; | |
197 | int len = 0; | |
198 | int res = 0; | |
199 | switch (PSENum) { | |
200 | case 1: | |
201 | param_gethex_to_eol(PSElist[1], 0, buf, sizeof(buf), &len); | |
202 | break; | |
203 | case 2: | |
204 | param_gethex_to_eol(PSElist[0], 0, buf, sizeof(buf), &len); | |
205 | break; | |
206 | default: | |
207 | return -1; | |
208 | } | |
209 | ||
210 | // select | |
211 | res = EMVSelect(ActivateField, LeaveFieldON, buf, len, Result, MaxResultLen, ResultLen, sw, NULL); | |
212 | ||
213 | return res; | |
214 | } | |
215 | ||
216 | int EMVSearchPSE(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv) { | |
217 | uint8_t data[APDU_RES_LEN] = {0}; | |
218 | size_t datalen = 0; | |
219 | uint16_t sw = 0; | |
220 | int res; | |
221 | ||
222 | // select PPSE | |
223 | res = EMVSelectPSE(ActivateField, true, 2, data, sizeof(data), &datalen, &sw); | |
224 | ||
225 | if (!res){ | |
226 | struct tlvdb *t = NULL; | |
227 | t = tlvdb_parse_multi(data, datalen); | |
228 | if (t) { | |
229 | int retrycnt = 0; | |
230 | struct tlvdb *ttmp = tlvdb_find_path(t, (tlv_tag_t[]){0x6f, 0xa5, 0xbf0c, 0x61, 0x00}); | |
231 | if (!ttmp) | |
232 | PrintAndLog("PPSE don't have records."); | |
233 | ||
234 | while (ttmp) { | |
235 | const struct tlv *tgAID = tlvdb_get_inchild(ttmp, 0x4f, NULL); | |
236 | if (tgAID) { | |
237 | res = EMVSelect(false, true, (uint8_t *)tgAID->value, tgAID->len, data, sizeof(data), &datalen, &sw, tlv); | |
238 | ||
239 | // retry if error and not returned sw error | |
240 | if (res && res != 5) { | |
241 | if (++retrycnt < 3){ | |
242 | continue; | |
243 | } else { | |
244 | // card select error, proxmark error | |
245 | if (res == 1) { | |
246 | PrintAndLog("Exit..."); | |
247 | return 1; | |
248 | } | |
249 | ||
250 | retrycnt = 0; | |
251 | PrintAndLog("Retry failed [%s]. Skiped...", sprint_hex_inrow(tgAID->value, tgAID->len)); | |
252 | } | |
253 | ||
254 | // next element | |
255 | ttmp = tlvdb_find_next(ttmp, 0x61); | |
256 | continue; | |
257 | } | |
258 | retrycnt = 0; | |
259 | ||
260 | // all is ok | |
261 | if (decodeTLV){ | |
262 | PrintAndLog("%s:", sprint_hex_inrow(tgAID->value, tgAID->len)); | |
263 | TLVPrintFromBuffer(data, datalen); | |
264 | } | |
265 | } | |
266 | ||
267 | ttmp = tlvdb_find_next(ttmp, 0x61); | |
268 | } | |
269 | ||
270 | tlvdb_free(t); | |
271 | } else { | |
272 | PrintAndLog("PPSE ERROR: Can't get TLV from response."); | |
273 | } | |
274 | } else { | |
275 | PrintAndLog("PPSE ERROR: Can't select PPSE AID. Error: %d", res); | |
276 | } | |
277 | ||
278 | if(!LeaveFieldON) | |
279 | DropField(); | |
280 | ||
281 | return res; | |
282 | } | |
283 | ||
284 | int EMVSearch(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv) { | |
285 | uint8_t aidbuf[APDU_AID_LEN] = {0}; | |
286 | int aidlen = 0; | |
287 | uint8_t data[APDU_RES_LEN] = {0}; | |
288 | size_t datalen = 0; | |
289 | uint16_t sw = 0; | |
290 | ||
291 | int res = 0; | |
292 | int retrycnt = 0; | |
293 | for(int i = 0; i < AIDlistLen; i ++) { | |
294 | param_gethex_to_eol(AIDlist[i], 0, aidbuf, sizeof(aidbuf), &aidlen); | |
295 | res = EMVSelect((i == 0) ? ActivateField : false, (i == AIDlistLen - 1) ? LeaveFieldON : true, aidbuf, aidlen, data, sizeof(data), &datalen, &sw, tlv); | |
296 | // retry if error and not returned sw error | |
297 | if (res && res != 5) { | |
298 | if (++retrycnt < 3){ | |
299 | i--; | |
300 | } else { | |
301 | // card select error, proxmark error | |
302 | if (res == 1) { | |
303 | PrintAndLog("Exit..."); | |
304 | return 1; | |
305 | } | |
306 | ||
307 | retrycnt = 0; | |
308 | PrintAndLog("Retry failed [%s]. Skiped...", AIDlist[i]); | |
309 | } | |
310 | continue; | |
311 | } | |
312 | retrycnt = 0; | |
313 | ||
314 | if (res) | |
315 | continue; | |
316 | ||
317 | if (decodeTLV){ | |
318 | PrintAndLog("%s:", AIDlist[i]); | |
319 | TLVPrintFromBuffer(data, datalen); | |
320 | } | |
321 | } | |
322 | ||
323 | return 0; | |
324 | } | |
325 | ||
326 | int EMVSelectApplication(struct tlvdb *tlv, uint8_t *AID, size_t *AIDlen) { | |
327 | // needs to check priority. 0x00 - highest | |
328 | int prio = 0xffff; | |
329 | ||
330 | *AIDlen = 0; | |
331 | ||
332 | struct tlvdb *ttmp = tlvdb_find(tlv, 0x6f); | |
333 | if (!ttmp) | |
334 | return 1; | |
335 | ||
336 | while (ttmp) { | |
337 | const struct tlv *tgAID = tlvdb_get_inchild(ttmp, 0x84, NULL); | |
338 | const struct tlv *tgPrio = tlvdb_get_inchild(ttmp, 0x87, NULL); | |
339 | ||
340 | if (!tgAID) | |
341 | break; | |
342 | ||
343 | if (tgPrio) { | |
344 | int pt = bytes_to_num((uint8_t*)tgPrio->value, (tgPrio->len < 2) ? tgPrio->len : 2); | |
345 | if (pt < prio) { | |
346 | prio = pt; | |
347 | ||
348 | memcpy(AID, tgAID->value, tgAID->len); | |
349 | *AIDlen = tgAID->len; | |
350 | } | |
351 | } else { | |
352 | // takes the first application from list wo priority | |
353 | if (!*AIDlen) { | |
354 | memcpy(AID, tgAID->value, tgAID->len); | |
355 | *AIDlen = tgAID->len; | |
356 | } | |
357 | } | |
358 | ||
359 | ttmp = tlvdb_find_next(ttmp, 0x6f); | |
360 | } | |
361 | ||
362 | return 0; | |
363 | } | |
364 | ||
365 | int EMVGPO(bool LeaveFieldON, uint8_t *PDOL, size_t PDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { | |
366 | uint8_t data[APDU_RES_LEN] = {0}; | |
367 | *ResultLen = 0; | |
368 | if (sw) *sw = 0; | |
369 | uint16_t isw = 0; | |
370 | ||
371 | // GPO APDU | |
372 | data[0] = 0x80; | |
373 | data[1] = 0xA8; | |
374 | data[2] = 0x00; | |
375 | data[3] = 0x00; | |
376 | data[4] = PDOLLen; | |
377 | if (PDOL) | |
378 | memcpy(&data[5], PDOL, PDOLLen); | |
379 | ||
380 | ||
381 | if (APDULogging) | |
382 | PrintAndLog(">>>> %s", sprint_hex(data, PDOLLen + 5)); | |
383 | ||
384 | int res = ExchangeAPDU14a(data, PDOLLen + 5, false, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen); | |
385 | ||
386 | if (APDULogging) | |
387 | PrintAndLog("<<<< %s", sprint_hex(Result, *ResultLen)); | |
388 | ||
389 | if (res) { | |
390 | return res; | |
391 | } | |
392 | ||
393 | if (*ResultLen < 2) { | |
394 | PrintAndLog("GPO ERROR: returned %d bytes", *ResultLen); | |
395 | return 5; | |
396 | } | |
397 | ||
398 | *ResultLen -= 2; | |
399 | isw = Result[*ResultLen] * 0x0100 + Result[*ResultLen + 1]; | |
400 | if (sw) | |
401 | *sw = isw; | |
402 | ||
403 | if (isw != 0x9000) { | |
404 | if (APDULogging) | |
405 | PrintAndLog("GPO ERROR: [%4X] %s", isw, GetAPDUCodeDescription(*sw >> 8, *sw & 0xff)); | |
406 | return 5; | |
407 | } | |
408 | ||
409 | // add to tlv tree | |
410 | if (tlv) { | |
411 | struct tlvdb *t = tlvdb_parse_multi(Result, *ResultLen); | |
412 | tlvdb_add(tlv, t); | |
413 | } | |
414 | ||
415 | return 0; | |
416 | } | |
417 | ||
418 | int EMVReadRecord(bool LeaveFieldON, uint8_t SFI, uint8_t SFIrec, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { | |
419 | uint8_t data[10] = {0}; | |
420 | *ResultLen = 0; | |
421 | if (sw) *sw = 0; | |
422 | uint16_t isw = 0; | |
423 | ||
424 | // read record APDU | |
425 | data[0] = 0x00; | |
426 | data[1] = 0xb2; | |
427 | data[2] = SFIrec; | |
428 | data[3] = (SFI << 3) | 0x04; | |
429 | data[4] = 0; | |
430 | ||
431 | if (APDULogging) | |
432 | PrintAndLog(">>>> %s", sprint_hex(data, 5)); | |
433 | ||
434 | int res = ExchangeAPDU14a(data, 5, false, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen); | |
435 | ||
436 | if (APDULogging) | |
437 | PrintAndLog("<<<< %s", sprint_hex(Result, *ResultLen)); | |
438 | ||
439 | if (res) { | |
440 | return res; | |
441 | } | |
442 | ||
443 | *ResultLen -= 2; | |
444 | isw = Result[*ResultLen] * 0x0100 + Result[*ResultLen + 1]; | |
445 | if (sw) | |
446 | *sw = isw; | |
447 | ||
448 | if (isw != 0x9000) { | |
449 | if (APDULogging) | |
450 | PrintAndLog("Read record ERROR: [%4X] %s", isw, GetAPDUCodeDescription(*sw >> 8, *sw & 0xff)); | |
451 | return 5; | |
452 | } | |
453 | ||
454 | // add to tlv tree | |
455 | if (tlv) { | |
456 | struct tlvdb *t = tlvdb_parse_multi(Result, *ResultLen); | |
457 | tlvdb_add(tlv, t); | |
458 | } | |
459 | ||
460 | return 0; | |
461 | } | |
462 | ||
463 |