]>
Commit | Line | Data |
---|---|---|
9206d3b0 | 1 | //Peter Fillmore - 2014 |
2 | // | |
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 | //-------------------------------------------------------------------------------- | |
9 | //Routines to support EMV transactions | |
10 | //-------------------------------------------------------------------------------- | |
9206d3b0 | 11 | #include "emvcmd.h" |
9206d3b0 | 12 | |
13 | static emvtags currentcard; //use to hold emv tags for the reader/card during communications | |
9206d3b0 | 14 | |
15 | // The FPGA will report its internal sending delay in | |
d627a2fd | 16 | //uint16_t FpgaSendQueueDelay; |
17 | ||
9206d3b0 | 18 | |
19 | //load individual tag into current card | |
20 | void EMVloadvalue(uint32_t tag, uint8_t *datain){ | |
21 | //Dbprintf("TAG=%i\n", tag); | |
22 | //Dbprintf("DATA=%s\n", datain); | |
23 | emv_settag(tag, datain, ¤tcard); | |
24 | } | |
25 | ||
26 | void EMVReadRecord(uint8_t arg0, uint8_t arg1,emvtags *currentcard) | |
27 | { | |
28 | uint8_t record = arg0; | |
d627a2fd | 29 | uint8_t sfi = arg1 & 0x0F; // convert arg1 to number |
9206d3b0 | 30 | uint8_t receivedAnswer[MAX_FRAME_SIZE]; |
d627a2fd | 31 | |
32 | // variables | |
33 | tlvtag inputtag; // create the tag structure | |
34 | // perform read | |
35 | // write the result to the provided card | |
9206d3b0 | 36 | if(!emv_readrecord(record,sfi,receivedAnswer)) { |
37 | if(EMV_DBGLEVEL >= 1) Dbprintf("readrecord failed"); | |
38 | } | |
39 | if(*(receivedAnswer+1) == 0x70){ | |
40 | decode_ber_tlv_item(receivedAnswer+1, &inputtag); | |
41 | emv_decode_field(inputtag.value, inputtag.valuelength, currentcard); | |
42 | } | |
43 | else | |
44 | { | |
45 | if(EMV_DBGLEVEL >= 1) | |
46 | Dbprintf("Record not found SFI=%i RECORD=%i", sfi, record); | |
47 | } | |
48 | return; | |
49 | } | |
50 | ||
51 | void EMVSelectAID(uint8_t *AID, uint8_t AIDlen, emvtags* inputcard) | |
52 | { | |
53 | uint8_t receivedAnswer[MAX_FRAME_SIZE]; | |
9206d3b0 | 54 | |
d627a2fd | 55 | // variables |
56 | tlvtag inputtag; // create the tag structure | |
57 | // perform select | |
9206d3b0 | 58 | if(!emv_select(AID, AIDlen, receivedAnswer)){ |
59 | if(EMV_DBGLEVEL >= 1) Dbprintf("AID Select failed"); | |
60 | return; | |
61 | } | |
d627a2fd | 62 | // write the result to the provided card |
9206d3b0 | 63 | if(*(receivedAnswer+1) == 0x6F){ |
d627a2fd | 64 | // decode the 6F template |
9206d3b0 | 65 | decode_ber_tlv_item(receivedAnswer+1, &inputtag); |
d627a2fd | 66 | // store 84 and A5 tags |
9206d3b0 | 67 | emv_decode_field(inputtag.value, inputtag.valuelength, ¤tcard); |
d627a2fd | 68 | // decode the A5 tag |
9206d3b0 | 69 | if(currentcard.tag_A5_len > 0) |
70 | emv_decode_field(currentcard.tag_A5, currentcard.tag_A5_len, ¤tcard); | |
71 | ||
d627a2fd | 72 | // copy this result to the DFName |
9206d3b0 | 73 | if(currentcard.tag_84_len == 0) |
74 | memcpy(currentcard.tag_DFName, currentcard.tag_84, currentcard.tag_84_len); | |
75 | ||
d627a2fd | 76 | // decode the BF0C result, assuming 1 directory entry for now |
9206d3b0 | 77 | if(currentcard.tag_BF0C_len !=0){ |
78 | emv_decode_field(currentcard.tag_BF0C, currentcard.tag_BF0C_len, ¤tcard);} | |
d627a2fd | 79 | // retrieve the AID, use the AID to decide what transaction flow to use |
9206d3b0 | 80 | if(currentcard.tag_61_len !=0){ |
81 | emv_decode_field(currentcard.tag_61, currentcard.tag_61_len, ¤tcard);} | |
82 | } | |
83 | if(EMV_DBGLEVEL >= 2) | |
84 | DbpString("SELECT AID COMPLETED"); | |
85 | } | |
86 | ||
87 | int EMVGetProcessingOptions(uint8_t *PDOL, uint8_t PDOLlen, emvtags* inputcard) | |
88 | { | |
89 | uint8_t receivedAnswer[MAX_FRAME_SIZE]; | |
9206d3b0 | 90 | |
d627a2fd | 91 | // variables |
9206d3b0 | 92 | tlvtag inputtag; //create the tag structure |
d627a2fd | 93 | // perform pdol |
9206d3b0 | 94 | if(!emv_getprocessingoptions(PDOL, PDOLlen, receivedAnswer)){ |
95 | if(EMV_DBGLEVEL >= 1) Dbprintf("get processing options failed"); | |
96 | return 0; | |
97 | } | |
d627a2fd | 98 | // write the result to the provided card |
99 | // FORMAT 1 received | |
9206d3b0 | 100 | if(receivedAnswer[1] == 0x80){ |
d627a2fd | 101 | // store AIP |
102 | // decode tag 80 | |
9206d3b0 | 103 | decode_ber_tlv_item(receivedAnswer+1, &inputtag); |
104 | memcpy(currentcard.tag_82, &inputtag.value, sizeof(currentcard.tag_82)); | |
105 | memcpy(currentcard.tag_94, &inputtag.value[2], inputtag.valuelength - sizeof(currentcard.tag_82)); | |
106 | currentcard.tag_94_len = inputtag.valuelength - sizeof(currentcard.tag_82); | |
107 | } | |
108 | else if(receivedAnswer[1] == 0x77){ | |
d627a2fd | 109 | // decode the 77 template |
9206d3b0 | 110 | decode_ber_tlv_item(receivedAnswer+1, &inputtag); |
d627a2fd | 111 | // store 82 and 94 tags (AIP, AFL) |
9206d3b0 | 112 | emv_decode_field(inputtag.value, inputtag.valuelength, ¤tcard); |
113 | } | |
114 | if(EMV_DBGLEVEL >= 2) | |
115 | DbpString("GET PROCESSING OPTIONS COMPLETE"); | |
116 | return 1; | |
117 | } | |
118 | ||
119 | int EMVGetChallenge(emvtags* inputcard) | |
120 | { | |
121 | uint8_t receivedAnswer[MAX_FRAME_SIZE]; | |
d627a2fd | 122 | // variables |
123 | // tlvtag inputtag; //create the tag structure | |
124 | // perform select | |
9206d3b0 | 125 | if(!emv_getchallenge(receivedAnswer)){ |
126 | if(EMV_DBGLEVEL >= 1) Dbprintf("get processing options failed"); | |
127 | return 1; | |
128 | } | |
129 | return 0; | |
130 | } | |
131 | ||
132 | int EMVGenerateAC(uint8_t refcontrol, emvtags* inputcard) | |
133 | { | |
134 | uint8_t receivedAnswer[MAX_FRAME_SIZE]; | |
135 | uint8_t cdolcommand[MAX_FRAME_SIZE]; | |
136 | uint8_t cdolcommandlen = 0; | |
137 | tlvtag temptag; | |
138 | ||
9206d3b0 | 139 | if(currentcard.tag_8C_len > 0) { |
140 | emv_generateDOL(currentcard.tag_8C, currentcard.tag_8C_len, ¤tcard, cdolcommand, &cdolcommandlen); } | |
141 | else{ | |
d627a2fd | 142 | // cdolcommand = NULL; //cdol val is null |
9206d3b0 | 143 | cdolcommandlen = 0; |
144 | } | |
d627a2fd | 145 | // variables |
146 | // tlvtag inputtag; //create the tag structure | |
147 | // perform select | |
9206d3b0 | 148 | if(!emv_generateAC(refcontrol, cdolcommand, cdolcommandlen,receivedAnswer)){ |
149 | if(EMV_DBGLEVEL >= 1) Dbprintf("get processing options failed"); | |
150 | return 1; | |
151 | } | |
152 | if(receivedAnswer[2] == 0x77) //format 2 data field returned | |
153 | { | |
154 | decode_ber_tlv_item(&receivedAnswer[2], &temptag); | |
155 | emv_decode_field(temptag.value, temptag.valuelength, ¤tcard); | |
156 | } | |
157 | ||
158 | return 0; | |
159 | } | |
160 | ||
161 | //function to perform paywave transaction | |
162 | //takes in TTQ, amount authorised, unpredicable number and transaction currency code | |
163 | int EMV_PaywaveTransaction() | |
164 | { | |
165 | uint8_t cardMode = 0; | |
d627a2fd | 166 | // determine mode of transaction from TTQ |
9206d3b0 | 167 | if((currentcard.tag_9F66[0] & 0x40) == 0x40) { |
168 | cardMode = VISA_EMV; | |
169 | } | |
170 | else if((currentcard.tag_9F66[0] & 0x20) == 0x20) { | |
171 | cardMode = VISA_FDDA; | |
172 | } | |
173 | else if((currentcard.tag_9F66[0] & 0x80) == 0x80) { | |
1bfbe92a | 174 | if((currentcard.tag_9F66[1] & 0x80) == 0x80) { //CVN17 |
9206d3b0 | 175 | cardMode = VISA_CVN17; |
3e83ff21 | 176 | } else { |
9206d3b0 | 177 | cardMode = VISA_DCVV; |
3e83ff21 | 178 | } |
9206d3b0 | 179 | } |
180 | ||
d627a2fd | 181 | EMVSelectAID(currentcard.tag_4F,currentcard.tag_4F_len, ¤tcard); // perform second AID command |
9206d3b0 | 182 | |
d627a2fd | 183 | // get PDOL |
9206d3b0 | 184 | uint8_t pdolcommand[20]; //20 byte buffer for pdol data |
185 | uint8_t pdolcommandlen = 0; | |
186 | if(currentcard.tag_9F38_len > 0) { | |
187 | emv_generateDOL(currentcard.tag_9F38, currentcard.tag_9F38_len, ¤tcard, pdolcommand, &pdolcommandlen); | |
188 | } | |
189 | Dbhexdump(pdolcommandlen, pdolcommand,false); | |
190 | ||
191 | if(!EMVGetProcessingOptions(pdolcommand,pdolcommandlen, ¤tcard)) { | |
192 | if(EMV_DBGLEVEL >= 1) Dbprintf("PDOL failed"); | |
193 | return 1; | |
194 | } | |
195 | ||
196 | Dbprintf("AFL="); | |
197 | Dbhexdump(currentcard.tag_94_len, currentcard.tag_94,false); | |
198 | Dbprintf("AIP="); | |
199 | Dbhexdump(2, currentcard.tag_82, false); | |
200 | emv_decodeAIP(currentcard.tag_82); | |
201 | // | |
d627a2fd | 202 | // decode the AFL list and read records |
9206d3b0 | 203 | uint8_t i = 0; |
204 | uint8_t sfi = 0; | |
205 | uint8_t recordstart = 0; | |
206 | uint8_t recordend = 0; | |
207 | if(currentcard.tag_94_len > 0){ | |
208 | while( i < currentcard.tag_94_len){ | |
209 | sfi = (currentcard.tag_94[i++] & 0xF8) >> 3; | |
210 | recordstart = currentcard.tag_94[i++]; | |
211 | recordend = currentcard.tag_94[i++]; | |
212 | for(int j=recordstart; j<(recordend+1); j++){ | |
d627a2fd | 213 | // read records |
9206d3b0 | 214 | EMVReadRecord(j,sfi, ¤tcard); |
d627a2fd | 215 | // while(responsebuffer[0] == 0xF2) { |
9206d3b0 | 216 | // EMVReadRecord(j,sfi, ¤tcard); |
d627a2fd | 217 | // } |
9206d3b0 | 218 | } |
219 | i++; | |
220 | } | |
221 | } | |
222 | else { | |
223 | EMVReadRecord(1,1,¤tcard); | |
224 | EMVReadRecord(1,2,¤tcard); | |
225 | EMVReadRecord(1,3,¤tcard); | |
226 | EMVReadRecord(2,1,¤tcard); | |
227 | EMVReadRecord(2,2,¤tcard); | |
228 | EMVReadRecord(2,3,¤tcard); | |
229 | EMVReadRecord(3,1,¤tcard); | |
230 | EMVReadRecord(3,3,¤tcard); | |
231 | EMVReadRecord(4,2,¤tcard); | |
232 | } | |
d627a2fd | 233 | // EMVGetChallenge(¤tcard); |
234 | // memcpy(currentcard.tag_9F4C,&responsebuffer[1],8); // ICC UN | |
9206d3b0 | 235 | EMVGenerateAC(0x81,¤tcard); |
236 | ||
237 | Dbprintf("CARDMODE=%i",cardMode); | |
238 | return 0; | |
239 | } | |
240 | ||
9206d3b0 | 241 | int EMV_PaypassTransaction() |
242 | { | |
d627a2fd | 243 | // uint8_t *responsebuffer = emv_get_bigbufptr(); |
244 | // tlvtag temptag; //buffer for decoded tags | |
245 | // get the current block counter | |
246 | // select the AID (Mastercard | |
9206d3b0 | 247 | EMVSelectAID(currentcard.tag_4F,currentcard.tag_4F_len, ¤tcard); |
248 | ||
d627a2fd | 249 | // get PDOL |
250 | uint8_t pdolcommand[20]; // 20 byte buffer for pdol data | |
9206d3b0 | 251 | uint8_t pdolcommandlen = 0; |
252 | if(currentcard.tag_9F38_len > 0) { | |
253 | emv_generateDOL(currentcard.tag_9F38, currentcard.tag_9F38_len, ¤tcard, pdolcommand, &pdolcommandlen); | |
254 | } | |
255 | if(EMVGetProcessingOptions(pdolcommand,pdolcommandlen, ¤tcard)) { | |
256 | if(EMV_DBGLEVEL >= 1) Dbprintf("PDOL failed"); | |
257 | return 1; | |
258 | } | |
259 | ||
260 | Dbprintf("AFL="); | |
261 | Dbhexdump(currentcard.tag_94_len, currentcard.tag_94,false); | |
262 | Dbprintf("AIP="); | |
263 | Dbhexdump(2, currentcard.tag_82, false); | |
264 | emv_decodeAIP(currentcard.tag_82); | |
265 | ||
d627a2fd | 266 | // decode the AFL list and read records |
9206d3b0 | 267 | uint8_t i = 0; |
268 | uint8_t sfi = 0; | |
269 | uint8_t recordstart = 0; | |
270 | uint8_t recordend = 0; | |
271 | ||
272 | while( i< currentcard.tag_94_len){ | |
273 | sfi = (currentcard.tag_94[i++] & 0xF8) >> 3; | |
274 | recordstart = currentcard.tag_94[i++]; | |
275 | recordend = currentcard.tag_94[i++]; | |
276 | for(int j=recordstart; j<(recordend+1); j++){ | |
d627a2fd | 277 | // read records |
9206d3b0 | 278 | EMVReadRecord(j,sfi, ¤tcard); |
d627a2fd | 279 | // while(responsebuffer[0] == 0xF2) { |
9206d3b0 | 280 | // EMVReadRecord(j,sfi, ¤tcard); |
d627a2fd | 281 | // } |
9206d3b0 | 282 | } |
283 | i++; | |
284 | } | |
285 | /* get ICC dynamic data */ | |
286 | if((currentcard.tag_82[0] & AIP_CDA_SUPPORTED) == AIP_CDA_SUPPORTED) | |
287 | { | |
d627a2fd | 288 | // DDA supported, so perform GENERATE AC |
289 | // generate the iCC UN | |
9206d3b0 | 290 | EMVGetChallenge(¤tcard); |
291 | //memcpy(currentcard.tag_9F4C,&responsebuffer[1],8); // ICC UN | |
292 | EMVGenerateAC(0x80,¤tcard); | |
293 | ||
294 | ||
d627a2fd | 295 | // generate AC2 |
296 | // if(currentcard.tag_8D_len > 0) { | |
9206d3b0 | 297 | // emv_generateDOL(currentcard.tag_8D, currentcard.tag_8D_len, ¤tcard, cdolcommand, &cdolcommandlen); } |
d627a2fd | 298 | // else{ |
9206d3b0 | 299 | // //cdolcommand = NULL; //cdol val is null |
300 | // cdolcommandlen = 0; | |
d627a2fd | 301 | // } |
302 | // emv_generateAC(0x80, cdolcommand,cdolcommandlen, ¤tcard); | |
9206d3b0 | 303 | |
d627a2fd | 304 | // if(responsebuffer[1] == 0x77) //format 2 data field returned |
305 | // { | |
9206d3b0 | 306 | // decode_ber_tlv_item(&responsebuffer[1], &temptag); |
307 | // emv_decode_field(temptag.value, temptag.valuelength, ¤tcard); | |
d627a2fd | 308 | // } |
9206d3b0 | 309 | } |
d627a2fd | 310 | // generate cryptographic checksum |
311 | // uint8_t udol[4] = {0x00,0x00,0x00,0x00}; | |
312 | // emv_computecryptogram(udol, sizeof(udol)); | |
313 | // if(responsebuffer[1] == 0x77) //format 2 data field returned | |
314 | // { | |
9206d3b0 | 315 | // decode_ber_tlv_item(&responsebuffer[1], &temptag); |
316 | // emv_decode_field(temptag.value, temptag.valuelength, ¤tcard); | |
d627a2fd | 317 | // } |
9206d3b0 | 318 | return 0; |
319 | } | |
320 | ||
321 | void EMVTransaction() | |
322 | { | |
323 | //params | |
324 | uint8_t uid[10] = {0x00}; | |
325 | uint32_t cuid = 0; | |
326 | ||
327 | //setup stuff | |
328 | BigBuf_free(); BigBuf_Clear_ext(false); | |
329 | clear_trace(); | |
330 | set_tracing(TRUE); | |
331 | ||
332 | LED_A_ON(); | |
333 | LED_B_OFF(); | |
334 | LED_C_OFF(); | |
335 | ||
336 | iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); | |
d627a2fd | 337 | |
9206d3b0 | 338 | while(true) { |
16cfceb6 | 339 | if(!iso14443a_select_card(uid, NULL, &cuid, true, 0)) { |
9206d3b0 | 340 | if(EMV_DBGLEVEL >= 1) Dbprintf("Can't select card"); |
341 | break; | |
342 | } | |
343 | //selectPPSE | |
344 | EMVSelectAID((uint8_t *)DF_PSE, 14, ¤tcard); //hard coded len | |
d627a2fd | 345 | |
9206d3b0 | 346 | //get response |
347 | if (!memcmp(currentcard.tag_4F, AID_MASTERCARD, sizeof(AID_MASTERCARD))){ | |
348 | Dbprintf("Mastercard Paypass Card Detected"); | |
349 | EMV_PaypassTransaction(); | |
350 | } | |
351 | else if (!memcmp(currentcard.tag_4F, AID_VISA, sizeof(AID_VISA))){ | |
352 | Dbprintf("VISA Paywave Card Detected"); | |
353 | EMV_PaywaveTransaction(); | |
354 | } | |
355 | //TODO: add other card schemes like AMEX, JCB, China Unionpay etc | |
356 | break; | |
357 | } | |
358 | if (EMV_DBGLEVEL >= 2) DbpString("EMV TRANSACTION FINISHED"); | |
359 | //finish up | |
360 | FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); | |
361 | LEDsoff(); | |
362 | } | |
363 | ||
364 | void EMVdumpcard(void){ | |
365 | dumpCard(¤tcard); | |
366 | } | |
367 | ||
368 | //SIMULATOR CODE | |
369 | //----------------------------------------------------------------------------- | |
370 | // Main loop of simulated tag: receive commands from reader, decide what | |
371 | // response to send, and send it. | |
372 | //----------------------------------------------------------------------------- | |
373 | void SimulateEMVcard() | |
374 | { | |
3e83ff21 | 375 | /* |
376 | ||
9206d3b0 | 377 | //uint8_t sak; //select ACKnowledge |
378 | uint16_t readerPacketLen = 64; //reader packet length - provided by RATS, default to 64 bytes if RATS not supported | |
379 | ||
380 | // The first response contains the ATQA (note: bytes are transmitted in reverse order). | |
381 | //uint8_t atqapacket[2]; | |
382 | ||
383 | // The second response contains the (mandatory) first 24 bits of the UID | |
384 | uint8_t uid0packet[5] = {0x00}; | |
385 | memcpy(uid0packet, currentcard.UID, sizeof(uid0packet)); | |
386 | // Check if the uid uses the (optional) part | |
387 | uint8_t uid1packet[5] = {0x00}; | |
388 | memcpy(uid1packet, currentcard.UID, sizeof(uid1packet)); | |
389 | ||
390 | // Calculate the BitCountCheck (BCC) for the first 4 bytes of the UID. | |
391 | uid0packet[4] = uid0packet[0] ^ uid0packet[1] ^ uid0packet[2] ^ uid0packet[3]; | |
392 | ||
393 | // Prepare the mandatory SAK (for 4 and 7 byte UID) | |
394 | uint8_t sak0packet[3] = {0x00}; | |
395 | memcpy(sak0packet,¤tcard.SAK1,1); | |
396 | ComputeCrc14443(CRC_14443_A, sak0packet, 1, &sak0packet[1], &sak0packet[2]); | |
397 | uint8_t sak1packet[3] = {0x00}; | |
398 | memcpy(sak1packet,¤tcard.SAK2,1); | |
399 | // Prepare the optional second SAK (for 7 byte UID), drop the cascade bit | |
400 | ComputeCrc14443(CRC_14443_A, sak1packet, 1, &sak1packet[1], &sak1packet[2]); | |
401 | ||
402 | uint8_t authanspacket[] = { 0x00, 0x00, 0x00, 0x00 }; // Very random tag nonce | |
403 | //setup response to ATS | |
404 | uint8_t ratspacket[currentcard.ATS_len]; | |
405 | memcpy(ratspacket,currentcard.ATS, currentcard.ATS_len); | |
406 | AppendCrc14443a(ratspacket,sizeof(ratspacket)-2); | |
407 | ||
408 | // Format byte = 0x58: FSCI=0x08 (FSC=256), TA(1) and TC(1) present, | |
409 | // TA(1) = 0x80: different divisors not supported, DR = 1, DS = 1 | |
410 | // TB(1) = not present. Defaults: FWI = 4 (FWT = 256 * 16 * 2^4 * 1/fc = 4833us), SFGI = 0 (SFG = 256 * 16 * 2^0 * 1/fc = 302us) | |
411 | // TC(1) = 0x02: CID supported, NAD not supported | |
412 | //ComputeCrc14443(CRC_14443_A, response6, 4, &response6[4], &response6[5]); | |
413 | ||
414 | //Receive Acknowledge responses differ by PCB byte | |
415 | uint8_t rack0packet[] = {0xa2,0x00,0x00}; | |
416 | AppendCrc14443a(rack0packet,1); | |
417 | uint8_t rack1packet[] = {0xa3,0x00,0x00}; | |
418 | AppendCrc14443a(rack1packet,1); | |
419 | //Negative Acknowledge | |
420 | uint8_t rnak0packet[] = {0xb2,0x00,0x00}; | |
421 | uint8_t rnak1packet[] = {0xb3,0x00,0x00}; | |
422 | AppendCrc14443a(rnak0packet,1); | |
423 | AppendCrc14443a(rnak1packet,1); | |
424 | ||
425 | //Protocol and parameter selection response, just say yes | |
426 | uint8_t ppspacket[] = {0xd0,0x00,0x00}; | |
427 | AppendCrc14443a(ppspacket,1); | |
428 | ||
429 | //hardcoded WTX packet - set to max time (49) | |
430 | uint8_t wtxpacket[] ={0xf2,0x31,0x00,0x00}; | |
431 | AppendCrc14443a(wtxpacket,2); | |
432 | ||
433 | //added additional responses for different readers, namely protocol parameter select and Receive acknowledments. - peter fillmore. | |
434 | //added defininitions for predone responses to aid readability | |
435 | #define ATR 0 | |
436 | #define UID1 1 | |
437 | #define UID2 2 | |
438 | #define SELACK1 3 | |
439 | #define SELACK2 4 | |
440 | #define AUTH_ANS 5 | |
441 | #define ATS 6 | |
442 | #define RACK0 7 | |
443 | #define RACK1 8 | |
444 | #define RNAK0 9 | |
445 | #define RNAK1 10 | |
446 | #define PPSresponse 11 | |
447 | #define WTX 12 | |
448 | ||
449 | #define TAG_RESPONSE_COUNT 13 | |
450 | tag_response_info_t responses[TAG_RESPONSE_COUNT] = { | |
451 | { .response = currentcard.ATQA, .response_n = sizeof(currentcard.ATQA) }, // Answer to request - respond with card type | |
452 | { .response = uid0packet, .response_n = sizeof(uid0packet) }, // Anticollision cascade1 - respond with uid | |
453 | { .response = uid1packet, .response_n = sizeof(uid1packet) }, // Anticollision cascade2 - respond with 2nd half of uid if asked | |
454 | { .response = sak0packet, .response_n = sizeof(sak0packet) }, // Acknowledge select - cascade 1 | |
455 | { .response = sak1packet, .response_n = sizeof(sak1packet) }, // Acknowledge select - cascade 2 | |
456 | { .response = authanspacket, .response_n = sizeof(authanspacket) }, // Authentication answer (random nonce) | |
457 | { .response = ratspacket, .response_n = sizeof(ratspacket) }, // dummy ATS (pseudo-ATR), answer to RATS | |
458 | { .response = rack0packet, .response_n = sizeof(rack0packet) }, //R(ACK)0 | |
459 | { .response = rack1packet, .response_n = sizeof(rack1packet) }, //R(ACK)0 | |
460 | { .response = rnak0packet, .response_n = sizeof(rnak0packet) }, //R(NAK)0 | |
461 | { .response = rnak1packet, .response_n = sizeof(rnak1packet) }, //R(NAK)1 | |
462 | { .response = ppspacket, .response_n = sizeof(ppspacket)}, //PPS packet | |
463 | { .response = wtxpacket, .response_n = sizeof(wtxpacket)}, //WTX packet | |
464 | }; | |
465 | ||
466 | //calculated length of predone responses | |
467 | uint16_t allocatedtaglen = 0; | |
468 | for(int i=0;i<TAG_RESPONSE_COUNT;i++){ | |
469 | allocatedtaglen += responses[i].response_n; | |
470 | } | |
471 | //uint8_t selectOrder = 0; | |
472 | ||
473 | BigBuf_free_keep_EM(); | |
474 | // Allocate 512 bytes for the dynamic modulation, created when the reader queries for it | |
475 | // Such a response is less time critical, so we can prepare them on the fly | |
476 | ||
477 | #define DYNAMIC_RESPONSE_BUFFER_SIZE 256 //max frame size | |
478 | #define DYNAMIC_MODULATION_BUFFER_SIZE 2 + 9*DYNAMIC_RESPONSE_BUFFER_SIZE //(start and stop bit, 8 bit packet with 1 bit parity | |
479 | ||
480 | //uint8_t dynamic_response_buffer[DYNAMIC_RESPONSE_BUFFER_SIZE]; | |
481 | //uint8_t dynamic_modulation_buffer[DYNAMIC_MODULATION_BUFFER_SIZE]; | |
482 | uint8_t *dynamic_response_buffer = BigBuf_malloc(DYNAMIC_RESPONSE_BUFFER_SIZE); | |
483 | uint8_t *dynamic_modulation_buffer = BigBuf_malloc(DYNAMIC_MODULATION_BUFFER_SIZE); | |
484 | ||
485 | tag_response_info_t dynamic_response_info = { | |
486 | .response = dynamic_response_buffer, | |
487 | .response_n = 0, | |
488 | .modulation = dynamic_modulation_buffer, | |
489 | .modulation_n = 0 | |
490 | }; | |
491 | // allocate buffers from BigBuf (so we're not in the stack) | |
492 | uint8_t *receivedCmd = BigBuf_malloc(MAX_FRAME_SIZE); | |
493 | uint8_t *receivedCmdPar = BigBuf_malloc(MAX_PARITY_SIZE); | |
494 | //uint8_t* free_buffer_pointer; | |
495 | //free_buffer_pointer = BigBuf_malloc((allocatedtaglen*8) +(allocatedtaglen) + (TAG_RESPONSE_COUNT * 3)); | |
496 | BigBuf_malloc((allocatedtaglen*8) +(allocatedtaglen) + (TAG_RESPONSE_COUNT * 3)); | |
497 | // clear trace | |
498 | clear_trace(); | |
499 | set_tracing(TRUE); | |
500 | ||
501 | // Prepare the responses of the anticollision phase | |
502 | // there will be not enough time to do this at the moment the reader sends it REQA | |
503 | for (size_t i=0; i<TAG_RESPONSE_COUNT; i++) | |
504 | prepare_allocated_tag_modulation(&responses[i]); | |
505 | ||
506 | int len = 0; | |
507 | ||
508 | // To control where we are in the protocol | |
509 | int order = 0; | |
510 | int lastorder; | |
511 | int currentblock = 1; //init to 1 | |
512 | int previousblock = 0; //used to store previous block counter | |
513 | ||
514 | // Just to allow some checks | |
515 | int happened = 0; | |
516 | int happened2 = 0; | |
517 | int cmdsRecvd = 0; | |
518 | ||
519 | // We need to listen to the high-frequency, peak-detected path. | |
520 | iso14443a_setup(FPGA_HF_ISO14443A_TAGSIM_LISTEN); | |
521 | ||
522 | cmdsRecvd = 0; | |
523 | tag_response_info_t* p_response; | |
524 | ||
525 | LED_A_ON(); | |
526 | for(;;) { | |
527 | // Clean receive command buffer | |
528 | ||
529 | if(!GetIso14443aCommandFromReader(receivedCmd, receivedCmdPar, &len)) { | |
530 | DbpString("Button press"); | |
531 | break; | |
532 | } | |
533 | ||
534 | p_response = NULL; | |
535 | ||
536 | // Okay, look at the command now. | |
537 | previousblock = currentblock; //get previous block | |
538 | lastorder = order; | |
539 | currentblock = receivedCmd[0] & 0x01; | |
540 | ||
541 | if(receivedCmd[0] == 0x26) { // Received a REQUEST | |
3e83ff21 | 542 | p_response = &responses[ATR]; order = ISO14443A_CMD_REQA; |
9206d3b0 | 543 | } else if(receivedCmd[0] == 0x52) { // Received a WAKEUP |
3e83ff21 | 544 | p_response = &responses[ATR]; order = ISO14443A_CMD_WUPA; |
9206d3b0 | 545 | } else if(receivedCmd[1] == 0x20 && receivedCmd[0] == 0x93) { // Received request for UID (cascade 1) |
3e83ff21 | 546 | p_response = &responses[UID1]; order = ISO14443A_CMD_ANTICOLL_OR_SELECT; |
9206d3b0 | 547 | } else if(receivedCmd[1] == 0x20 && receivedCmd[0] == 0x95) { // Received request for UID (cascade 2) |
3e83ff21 | 548 | p_response = &responses[UID2]; order = ISO14443A_CMD_ANTICOLL_OR_SELECT_2; |
9206d3b0 | 549 | } else if(receivedCmd[1] == 0x70 && receivedCmd[0] == 0x93) { // Received a SELECT (cascade 1) |
3e83ff21 | 550 | p_response = &responses[SELACK1]; order = ISO14443A_CMD_ANTICOLL_OR_SELECT; |
9206d3b0 | 551 | } else if(receivedCmd[1] == 0x70 && receivedCmd[0] == 0x95) { // Received a SELECT (cascade 2) |
3e83ff21 | 552 | p_response = &responses[SELACK2]; order = ISO14443A_CMD_ANTICOLL_OR_SELECT_2; |
9206d3b0 | 553 | } else if((receivedCmd[0] & 0xA2) == 0xA2){ //R-Block received |
554 | if(previousblock == currentblock){ //rule 11, retransmit last block | |
555 | p_response = &dynamic_response_info; | |
556 | } else { | |
557 | if((receivedCmd[0] & 0xB2) == 0xB2){ //RNAK, rule 12 | |
558 | if(currentblock == 0) | |
559 | p_response = &responses[RACK0]; | |
560 | else | |
561 | p_response = &responses[RACK1]; | |
562 | } else { | |
563 | //rule 13 | |
564 | //TODO: implement chaining | |
565 | } | |
566 | } | |
567 | } | |
568 | else if(receivedCmd[0] == 0xD0){ //Protocol and parameter selection response | |
569 | p_response = &responses[PPSresponse]; | |
570 | order = PPS; | |
571 | } | |
572 | else if(receivedCmd[0] == 0x30) { // Received a (plain) READ | |
573 | //we're an EMV card - so no read commands | |
574 | p_response = NULL; | |
575 | } else if(receivedCmd[0] == 0x50) { // Received a HALT | |
576 | LogTrace(receivedCmd, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, TRUE); | |
577 | p_response = NULL; | |
578 | order = HLTA; | |
579 | } else if(receivedCmd[0] == 0x60 || receivedCmd[0] == 0x61) { // Received an authentication request | |
580 | p_response = &responses[AUTH_ANS]; | |
581 | order = AUTH; | |
582 | } else if(receivedCmd[0] == 0xE0) { // Received a RATS request | |
583 | readerPacketLen = GetReaderLength(receivedCmd); //get length of supported packet | |
584 | p_response = &responses[ATS]; | |
585 | order = RATS; | |
586 | } else if (order == AUTH && len == 8) { // Received {nr] and {ar} (part of authentication) | |
587 | LogTrace(receivedCmd, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, TRUE); | |
588 | uint32_t nr = bytes_to_num(receivedCmd,4); | |
589 | uint32_t ar = bytes_to_num(receivedCmd+4,4); | |
590 | Dbprintf("Auth attempt {nr}{ar}: %08x %08x",nr,ar); | |
591 | } else { | |
592 | // Check for ISO 14443A-4 compliant commands, look at left nibble | |
593 | switch (receivedCmd[0]) { | |
594 | case 0x0B: | |
595 | case 0x0A: // IBlock (command) | |
596 | case 0x02: | |
597 | case 0x03: { | |
598 | dynamic_response_info.response_n = 0; | |
599 | dynamic_response_info.response[0] = receivedCmd[0]; // copy PCB | |
600 | //dynamic_response_info.response[1] = receivedCmd[1]; // copy PCB | |
601 | dynamic_response_info.response_n++ ; | |
602 | switch(receivedCmd[1]) { | |
603 | case 0x00: | |
604 | switch(receivedCmd[2]){ | |
605 | case 0xA4: //select | |
606 | if(receivedCmd[5] == 0x0E){ | |
607 | } | |
608 | else if(receivedCmd[5] == 0x07){ | |
609 | //selectOrder = 0; | |
610 | } | |
611 | else{ //send not supported msg | |
612 | memcpy(dynamic_response_info.response+1, "\x6a\x82", 2); | |
613 | dynamic_response_info.response_n += 2; | |
614 | } | |
615 | break; | |
616 | case 0xB2: //read record | |
617 | if(receivedCmd[3] == 0x01 && receivedCmd[4] == 0x0C){ | |
618 | dynamic_response_info.response_n += 2; | |
619 | Dbprintf("READ RECORD 1 1"); | |
620 | } | |
621 | break; | |
622 | } | |
623 | break; | |
624 | case 0x80: | |
625 | switch(receivedCmd[2]){ | |
626 | case 0xA8: //get processing options | |
627 | break; | |
628 | } | |
629 | } | |
630 | }break; | |
631 | case 0x1A: | |
632 | case 0x1B: { // Chaining command | |
633 | dynamic_response_info.response[0] = 0xaa | ((receivedCmd[0]) & 1); | |
634 | dynamic_response_info.response_n = 2; | |
635 | } break; | |
636 | ||
637 | case 0xaa: | |
638 | case 0xbb: { | |
639 | dynamic_response_info.response[0] = receivedCmd[0] ^ 0x11; | |
640 | dynamic_response_info.response_n = 2; | |
641 | } break; | |
642 | ||
643 | case 0xBA: { // | |
644 | memcpy(dynamic_response_info.response,"\xAB\x00",2); | |
645 | dynamic_response_info.response_n = 2; | |
646 | } break; | |
647 | ||
648 | case 0xCA: | |
649 | case 0xC2: { // Readers sends deselect command | |
650 | //we send the command back - this is what tags do in android implemenation i believe - peterfillmore | |
651 | memcpy(dynamic_response_info.response,receivedCmd,1); | |
652 | dynamic_response_info.response_n = 1; | |
653 | } break; | |
654 | ||
655 | default: { | |
656 | // Never seen this command before | |
657 | LogTrace(receivedCmd, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, TRUE); | |
658 | Dbprintf("Received unknown command (len=%d):",len); | |
659 | Dbhexdump(len,receivedCmd,false); | |
660 | // Do not respond | |
661 | dynamic_response_info.response_n = 0; | |
662 | } break; | |
663 | } | |
664 | ||
665 | if (dynamic_response_info.response_n > 0) { | |
666 | // Copy the CID from the reader query | |
667 | //dynamic_response_info.response[1] = receivedCmd[1]; | |
668 | ||
669 | // Add CRC bytes, always used in ISO 14443A-4 compliant cards | |
670 | AppendCrc14443a(dynamic_response_info.response,dynamic_response_info.response_n); | |
671 | dynamic_response_info.response_n += 2; | |
672 | if(dynamic_response_info.response_n > readerPacketLen){ //throw error if our reader doesn't support the send packet length | |
673 | Dbprintf("Error: tag response is longer then what the reader supports, TODO:implement command chaining"); | |
674 | LogTrace(receivedCmd, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, TRUE); | |
675 | break; | |
676 | } | |
677 | if (prepare_tag_modulation(&dynamic_response_info,DYNAMIC_MODULATION_BUFFER_SIZE) == false) { | |
678 | Dbprintf("Error preparing tag response"); | |
679 | LogTrace(receivedCmd, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, TRUE); | |
680 | break; | |
681 | } | |
682 | p_response = &dynamic_response_info; | |
683 | } | |
684 | } | |
685 | ||
686 | // Count number of wakeups received after a halt | |
687 | if(order == HLTA && lastorder == PPS) { happened++; } | |
688 | ||
689 | // Count number of other messages after a halt | |
690 | if(order != HLTA && lastorder == PPS) { happened2++; } | |
691 | ||
692 | if(cmdsRecvd > 999) { | |
693 | DbpString("1000 commands later..."); | |
694 | break; | |
695 | } | |
696 | cmdsRecvd++; | |
697 | ||
698 | if (p_response != NULL) { | |
699 | EmSendCmd14443aRaw(p_response->modulation, p_response->modulation_n, receivedCmd[0] == 0x52); | |
700 | // do the tracing for the previous reader request and this tag answer: | |
701 | uint8_t par[MAX_PARITY_SIZE] = {0x00}; | |
702 | GetParity(p_response->response, p_response->response_n, par); | |
703 | ||
704 | EmLogTrace(Uart.output, | |
705 | Uart.len, | |
706 | Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, | |
707 | Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, | |
708 | Uart.parity, | |
709 | p_response->response, | |
710 | p_response->response_n, | |
711 | LastTimeProxToAirStart*16 + DELAY_ARM2AIR_AS_TAG, | |
712 | (LastTimeProxToAirStart + p_response->ProxToAirDuration)*16 + DELAY_ARM2AIR_AS_TAG, | |
713 | par); | |
714 | } | |
715 | ||
716 | if (!tracing) { | |
717 | Dbprintf("Trace Full. Simulation stopped."); | |
718 | break; | |
719 | } | |
720 | } | |
721 | ||
722 | Dbprintf("%x %x %x", happened, happened2, cmdsRecvd); | |
723 | LED_A_OFF(); | |
724 | BigBuf_free_keep_EM(); | |
3e83ff21 | 725 | |
726 | */ | |
9206d3b0 | 727 | } |