]> git.zerfleddert.de Git - proxmark3-svn/blame - armsrc/mifarecmd.c
Fixed error in hash1 (from loclass), now it possibly calculates the KSel correctly...
[proxmark3-svn] / armsrc / mifarecmd.c
CommitLineData
8556b852 1//-----------------------------------------------------------------------------\r
b62a5a84 2// Merlok - June 2011, 2012\r
8556b852
M
3// Gerhard de Koning Gans - May 2008\r
4// Hagen Fritsch - June 2010\r
5//\r
6// This code is licensed to you under the terms of the GNU GPL, version 2 or,\r
7// at your option, any later version. See the LICENSE.txt file for the text of\r
8// the license.\r
9//-----------------------------------------------------------------------------\r
10// Routines to support ISO 14443 type A.\r
11//-----------------------------------------------------------------------------\r
12\r
13#include "mifarecmd.h"\r
14#include "apps.h"\r
15\r
16//-----------------------------------------------------------------------------\r
baeaf579 17// Select, Authenticate, Read a MIFARE tag. \r
8556b852
M
18// read block\r
19//-----------------------------------------------------------------------------\r
20void MifareReadBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain)\r
21{\r
22 // params\r
23 uint8_t blockNo = arg0;\r
24 uint8_t keyType = arg1;\r
25 uint64_t ui64Key = 0;\r
26 ui64Key = bytes_to_num(datain, 6);\r
27 \r
28 // variables\r
29 byte_t isOK = 0;\r
30 byte_t dataoutbuf[16];\r
1c611bbd 31 uint8_t uid[10];\r
8556b852
M
32 uint32_t cuid;\r
33 struct Crypto1State mpcs = {0, 0};\r
34 struct Crypto1State *pcs;\r
35 pcs = &mpcs;\r
36\r
37 // clear trace\r
baeaf579 38 iso14a_clear_trace();\r
8556b852
M
39// iso14a_set_tracing(false);\r
40\r
7bc95e2e 41 iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);\r
8556b852
M
42\r
43 LED_A_ON();\r
44 LED_B_OFF();\r
45 LED_C_OFF();\r
46\r
47 while (true) {\r
48 if(!iso14443a_select_card(uid, NULL, &cuid)) {\r
baeaf579 49 if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card");\r
8556b852
M
50 break;\r
51 };\r
52\r
53 if(mifare_classic_auth(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST)) {\r
baeaf579 54 if (MF_DBGLEVEL >= 1) Dbprintf("Auth error");\r
8556b852
M
55 break;\r
56 };\r
57 \r
58 if(mifare_classic_readblock(pcs, cuid, blockNo, dataoutbuf)) {\r
baeaf579 59 if (MF_DBGLEVEL >= 1) Dbprintf("Read block error");\r
8556b852
M
60 break;\r
61 };\r
62\r
63 if(mifare_classic_halt(pcs, cuid)) {\r
baeaf579 64 if (MF_DBGLEVEL >= 1) Dbprintf("Halt error");\r
8556b852
M
65 break;\r
66 };\r
67 \r
68 isOK = 1;\r
69 break;\r
70 }\r
71 \r
72 // ----------------------------- crypto1 destroy\r
73 crypto1_destroy(pcs);\r
74 \r
75 if (MF_DBGLEVEL >= 2) DbpString("READ BLOCK FINISHED");\r
76\r
8556b852 77 LED_B_ON();\r
baeaf579 78 cmd_send(CMD_ACK,isOK,0,0,dataoutbuf,16);\r
8556b852
M
79 LED_B_OFF();\r
80\r
baeaf579 81 // Thats it...\r
8556b852
M
82 FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);\r
83 LEDsoff();\r
84// iso14a_set_tracing(TRUE);\r
7cc204bf 85\r
86}\r
87\r
88void MifareUReadBlock(uint8_t arg0,uint8_t *datain)\r
89{\r
90 // params\r
91 uint8_t blockNo = arg0;\r
92 \r
93 // variables\r
94 byte_t isOK = 0;\r
95 byte_t dataoutbuf[16];\r
96 uint8_t uid[10];\r
97 uint32_t cuid;\r
98 \r
99 // clear trace\r
100 iso14a_clear_trace();\r
7bc95e2e 101 iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);\r
7cc204bf 102 \r
103 LED_A_ON();\r
104 LED_B_OFF();\r
105 LED_C_OFF();\r
106 \r
107 while (true) {\r
108 if(!iso14443a_select_card(uid, NULL, &cuid)) {\r
109 if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card");\r
110 break;\r
111 };\r
112 \r
113 if(mifare_ultra_readblock(cuid, blockNo, dataoutbuf)) {\r
114 if (MF_DBGLEVEL >= 1) Dbprintf("Read block error");\r
115 break;\r
116 };\r
117 \r
118 if(mifare_ultra_halt(cuid)) {\r
119 if (MF_DBGLEVEL >= 1) Dbprintf("Halt error");\r
120 break;\r
121 };\r
122 \r
123 isOK = 1;\r
124 break;\r
125 }\r
126 \r
127 if (MF_DBGLEVEL >= 2) DbpString("READ BLOCK FINISHED");\r
128 \r
129 // add trace trailer\r
130 memset(uid, 0x44, 4);\r
131 LogTrace(uid, 4, 0, 0, TRUE);\r
132 LED_B_ON();\r
133 cmd_send(CMD_ACK,isOK,0,0,dataoutbuf,16);\r
134 LED_B_OFF();\r
135 \r
136 \r
137 // Thats it...\r
138 FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);\r
139 LEDsoff();\r
140}\r
141\r
baeaf579 142\r
7cc204bf 143//-----------------------------------------------------------------------------\r
baeaf579 144// Select, Authenticate, Read a MIFARE tag. \r
145// read sector (data = 4 x 16 bytes = 64 bytes, or 16 x 16 bytes = 256 bytes)\r
8556b852
M
146//-----------------------------------------------------------------------------\r
147void MifareReadSector(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain)\r
148{\r
149 // params\r
150 uint8_t sectorNo = arg0;\r
151 uint8_t keyType = arg1;\r
152 uint64_t ui64Key = 0;\r
153 ui64Key = bytes_to_num(datain, 6);\r
154 \r
155 // variables\r
baeaf579 156 byte_t isOK;\r
157 byte_t dataoutbuf[16 * 16];\r
1c611bbd 158 uint8_t uid[10];\r
8556b852
M
159 uint32_t cuid;\r
160 struct Crypto1State mpcs = {0, 0};\r
161 struct Crypto1State *pcs;\r
162 pcs = &mpcs;\r
163\r
164 // clear trace\r
baeaf579 165 iso14a_clear_trace();\r
8556b852
M
166// iso14a_set_tracing(false);\r
167\r
7bc95e2e 168 iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);\r
8556b852
M
169\r
170 LED_A_ON();\r
171 LED_B_OFF();\r
172 LED_C_OFF();\r
173\r
baeaf579 174 isOK = 1;\r
175 if(!iso14443a_select_card(uid, NULL, &cuid)) {\r
176 isOK = 0;\r
8556b852 177 if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card");\r
baeaf579 178 }\r
8556b852 179\r
baeaf579 180 \r
181 if(isOK && mifare_classic_auth(pcs, cuid, FirstBlockOfSector(sectorNo), keyType, ui64Key, AUTH_FIRST)) {\r
182 isOK = 0;\r
8556b852 183 if (MF_DBGLEVEL >= 1) Dbprintf("Auth error");\r
baeaf579 184 }\r
185 \r
186 for (uint8_t blockNo = 0; isOK && blockNo < NumBlocksPerSector(sectorNo); blockNo++) {\r
187 if(mifare_classic_readblock(pcs, cuid, FirstBlockOfSector(sectorNo) + blockNo, dataoutbuf + 16 * blockNo)) {\r
188 isOK = 0;\r
189 if (MF_DBGLEVEL >= 1) Dbprintf("Read sector %2d block %2d error", sectorNo, blockNo);\r
8556b852 190 break;\r
baeaf579 191 }\r
192 }\r
8556b852 193 \r
baeaf579 194 if(mifare_classic_halt(pcs, cuid)) {\r
8556b852 195 if (MF_DBGLEVEL >= 1) Dbprintf("Halt error");\r
8556b852 196 }\r
baeaf579 197\r
8556b852
M
198 \r
199 // ----------------------------- crypto1 destroy\r
200 crypto1_destroy(pcs);\r
201 \r
202 if (MF_DBGLEVEL >= 2) DbpString("READ SECTOR FINISHED");\r
203\r
8556b852 204 LED_B_ON();\r
baeaf579 205 cmd_send(CMD_ACK,isOK,0,0,dataoutbuf,16*NumBlocksPerSector(sectorNo));\r
6e82300d 206 LED_B_OFF();\r
8556b852
M
207\r
208 // Thats it...\r
209 FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);\r
210 LEDsoff();\r
211// iso14a_set_tracing(TRUE);\r
7cc204bf 212}\r
213\r
baeaf579 214\r
7cc204bf 215void MifareUReadCard(uint8_t arg0, uint8_t *datain)\r
216{\r
217 // params\r
218 uint8_t sectorNo = arg0;\r
219 \r
220 // variables\r
221 byte_t isOK = 0;\r
222 byte_t dataoutbuf[16 * 4];\r
223 uint8_t uid[10];\r
224 uint32_t cuid;\r
225\r
226 // clear trace\r
227 iso14a_clear_trace();\r
228// iso14a_set_tracing(false);\r
229\r
7bc95e2e 230 iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);\r
7cc204bf 231\r
232 LED_A_ON();\r
233 LED_B_OFF();\r
234 LED_C_OFF();\r
235\r
236 while (true) {\r
237 if(!iso14443a_select_card(uid, NULL, &cuid)) {\r
238 if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card");\r
239 break;\r
240 };\r
241 for(int sec=0;sec<16;sec++){\r
242 if(mifare_ultra_readblock(cuid, sectorNo * 4 + sec, dataoutbuf + 4 * sec)) {\r
243 if (MF_DBGLEVEL >= 1) Dbprintf("Read block %d error",sec);\r
244 break;\r
245 };\r
246 }\r
247 if(mifare_ultra_halt(cuid)) {\r
248 if (MF_DBGLEVEL >= 1) Dbprintf("Halt error");\r
249 break;\r
250 };\r
251\r
252 isOK = 1;\r
253 break;\r
254 }\r
255 \r
256 if (MF_DBGLEVEL >= 2) DbpString("READ CARD FINISHED");\r
257\r
7cc204bf 258 LED_B_ON();\r
259 cmd_send(CMD_ACK,isOK,0,0,dataoutbuf,64);\r
7cc204bf 260 LED_B_OFF();\r
261\r
262 // Thats it...\r
263 FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);\r
264 LEDsoff();\r
7cc204bf 265\r
266}\r
267\r
268\r
269//-----------------------------------------------------------------------------\r
baeaf579 270// Select, Authenticate, Write a MIFARE tag. \r
7cc204bf 271// read block\r
8556b852
M
272//-----------------------------------------------------------------------------\r
273void MifareWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain)\r
274{\r
275 // params\r
276 uint8_t blockNo = arg0;\r
277 uint8_t keyType = arg1;\r
278 uint64_t ui64Key = 0;\r
279 byte_t blockdata[16];\r
280\r
281 ui64Key = bytes_to_num(datain, 6);\r
282 memcpy(blockdata, datain + 10, 16);\r
283 \r
284 // variables\r
285 byte_t isOK = 0;\r
1c611bbd 286 uint8_t uid[10];\r
8556b852
M
287 uint32_t cuid;\r
288 struct Crypto1State mpcs = {0, 0};\r
289 struct Crypto1State *pcs;\r
290 pcs = &mpcs;\r
291\r
292 // clear trace\r
d19929cb 293 iso14a_clear_trace();\r
8556b852
M
294// iso14a_set_tracing(false);\r
295\r
7bc95e2e 296 iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);\r
8556b852
M
297\r
298 LED_A_ON();\r
299 LED_B_OFF();\r
300 LED_C_OFF();\r
301\r
302 while (true) {\r
303 if(!iso14443a_select_card(uid, NULL, &cuid)) {\r
304 if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card");\r
305 break;\r
306 };\r
307\r
308 if(mifare_classic_auth(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST)) {\r
309 if (MF_DBGLEVEL >= 1) Dbprintf("Auth error");\r
310 break;\r
311 };\r
312 \r
313 if(mifare_classic_writeblock(pcs, cuid, blockNo, blockdata)) {\r
314 if (MF_DBGLEVEL >= 1) Dbprintf("Write block error");\r
315 break;\r
316 };\r
317\r
318 if(mifare_classic_halt(pcs, cuid)) {\r
319 if (MF_DBGLEVEL >= 1) Dbprintf("Halt error");\r
320 break;\r
321 };\r
322 \r
323 isOK = 1;\r
324 break;\r
325 }\r
326 \r
327 // ----------------------------- crypto1 destroy\r
328 crypto1_destroy(pcs);\r
329 \r
330 if (MF_DBGLEVEL >= 2) DbpString("WRITE BLOCK FINISHED");\r
331\r
8556b852 332 LED_B_ON();\r
9492e0b0 333 cmd_send(CMD_ACK,isOK,0,0,0,0);\r
6e82300d 334 LED_B_OFF();\r
8556b852
M
335\r
336\r
337 // Thats it...\r
338 FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);\r
339 LEDsoff();\r
340// iso14a_set_tracing(TRUE);\r
7cc204bf 341\r
342}\r
343\r
baeaf579 344\r
7cc204bf 345void MifareUWriteBlock(uint8_t arg0, uint8_t *datain)\r
346{\r
347 // params\r
348 uint8_t blockNo = arg0;\r
349 byte_t blockdata[16];\r
350\r
351 memset(blockdata,'\0',16);\r
352 memcpy(blockdata, datain,16);\r
353 \r
354 // variables\r
355 byte_t isOK = 0;\r
356 uint8_t uid[10];\r
357 uint32_t cuid;\r
358\r
359 // clear trace\r
360 iso14a_clear_trace();\r
361 // iso14a_set_tracing(false);\r
362\r
7bc95e2e 363 iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);\r
7cc204bf 364\r
365 LED_A_ON();\r
366 LED_B_OFF();\r
367 LED_C_OFF();\r
368\r
369 while (true) {\r
370 if(!iso14443a_select_card(uid, NULL, &cuid)) {\r
371 if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card");\r
372 break;\r
373 };\r
374\r
375 if(mifare_ultra_writeblock(cuid, blockNo, blockdata)) {\r
376 if (MF_DBGLEVEL >= 1) Dbprintf("Write block error");\r
377 break;\r
378 };\r
379\r
380 if(mifare_ultra_halt(cuid)) {\r
381 if (MF_DBGLEVEL >= 1) Dbprintf("Halt error");\r
382 break;\r
383 };\r
384 \r
385 isOK = 1;\r
386 break;\r
387 }\r
388 \r
389 if (MF_DBGLEVEL >= 2) DbpString("WRITE BLOCK FINISHED");\r
390\r
7cc204bf 391 LED_B_ON();\r
baeaf579 392 cmd_send(CMD_ACK,isOK,0,0,0,0);\r
7cc204bf 393 LED_B_OFF();\r
394\r
395\r
396 // Thats it...\r
397 FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);\r
398 LEDsoff();\r
399// iso14a_set_tracing(TRUE);\r
7cc204bf 400}\r
401\r
baeaf579 402\r
7cc204bf 403void MifareUWriteBlock_Special(uint8_t arg0, uint8_t *datain)\r
404{\r
baeaf579 405 // params\r
406 uint8_t blockNo = arg0;\r
407 byte_t blockdata[4];\r
408 \r
7cc204bf 409 memcpy(blockdata, datain,4);\r
410\r
baeaf579 411 // variables\r
412 byte_t isOK = 0;\r
413 uint8_t uid[10];\r
414 uint32_t cuid;\r
7cc204bf 415\r
baeaf579 416 // clear trace\r
417 iso14a_clear_trace();\r
418 // iso14a_set_tracing(false);\r
7cc204bf 419\r
baeaf579 420 iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);\r
7cc204bf 421\r
baeaf579 422 LED_A_ON();\r
423 LED_B_OFF();\r
424 LED_C_OFF();\r
7cc204bf 425\r
baeaf579 426 while (true) {\r
427 if(!iso14443a_select_card(uid, NULL, &cuid)) {\r
428 if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card");\r
429 break;\r
430 };\r
7cc204bf 431\r
baeaf579 432 if(mifare_ultra_special_writeblock(cuid, blockNo, blockdata)) {\r
433 if (MF_DBGLEVEL >= 1) Dbprintf("Write block error");\r
434 break;\r
435 };\r
7cc204bf 436\r
baeaf579 437 if(mifare_ultra_halt(cuid)) {\r
438 if (MF_DBGLEVEL >= 1) Dbprintf("Halt error");\r
439 break;\r
440 };\r
7cc204bf 441\r
baeaf579 442 isOK = 1;\r
443 break;\r
444 }\r
7cc204bf 445\r
baeaf579 446 if (MF_DBGLEVEL >= 2) DbpString("WRITE BLOCK FINISHED");\r
7cc204bf 447\r
baeaf579 448 LED_B_ON();\r
449 cmd_send(CMD_ACK,isOK,0,0,0,0);\r
450 LED_B_OFF();\r
7cc204bf 451\r
452\r
baeaf579 453 // Thats it...\r
454 FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);\r
455 LEDsoff();\r
7cc204bf 456// iso14a_set_tracing(TRUE);\r
457\r
458}\r
459\r
baeaf579 460\r
7cc204bf 461// Return 1 if the nonce is invalid else return 0\r
462int valid_nonce(uint32_t Nt, uint32_t NtEnc, uint32_t Ks1, byte_t * parity) {\r
463 return ((oddparity((Nt >> 24) & 0xFF) == ((parity[0]) ^ oddparity((NtEnc >> 24) & 0xFF) ^ BIT(Ks1,16))) & \\r
8556b852
M
464 (oddparity((Nt >> 16) & 0xFF) == ((parity[1]) ^ oddparity((NtEnc >> 16) & 0xFF) ^ BIT(Ks1,8))) & \\r
465 (oddparity((Nt >> 8) & 0xFF) == ((parity[2]) ^ oddparity((NtEnc >> 8) & 0xFF) ^ BIT(Ks1,0)))) ? 1 : 0;\r
466}\r
467\r
9492e0b0 468\r
8556b852
M
469//-----------------------------------------------------------------------------\r
470// MIFARE nested authentication. \r
471// \r
472//-----------------------------------------------------------------------------\r
9492e0b0 473void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t calibrate, uint8_t *datain)\r
8556b852
M
474{\r
475 // params\r
9492e0b0 476 uint8_t blockNo = arg0 & 0xff;\r
477 uint8_t keyType = (arg0 >> 8) & 0xff;\r
478 uint8_t targetBlockNo = arg1 & 0xff;\r
479 uint8_t targetKeyType = (arg1 >> 8) & 0xff;\r
8556b852
M
480 uint64_t ui64Key = 0;\r
481\r
482 ui64Key = bytes_to_num(datain, 6);\r
483 \r
484 // variables\r
9492e0b0 485 uint16_t rtr, i, j, len;\r
486 uint16_t davg;\r
487 static uint16_t dmin, dmax;\r
1c611bbd 488 uint8_t uid[10];\r
8556b852 489 uint32_t cuid, nt1, nt2, nttmp, nttest, par, ks1;\r
9492e0b0 490 uint32_t target_nt[2], target_ks[2];\r
491 \r
8556b852 492 uint8_t par_array[4];\r
9492e0b0 493 uint16_t ncount = 0;\r
8556b852
M
494 struct Crypto1State mpcs = {0, 0};\r
495 struct Crypto1State *pcs;\r
496 pcs = &mpcs;\r
497 uint8_t* receivedAnswer = mifare_get_bigbufptr();\r
498\r
9492e0b0 499 uint32_t auth1_time, auth2_time;\r
500 static uint16_t delta_time;\r
501\r
8556b852 502 // clear trace\r
d19929cb 503 iso14a_clear_trace();\r
9492e0b0 504 iso14a_set_tracing(false);\r
8556b852 505 \r
7bc95e2e 506 iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);\r
8556b852
M
507\r
508 LED_A_ON();\r
8556b852
M
509 LED_C_OFF();\r
510\r
8556b852 511\r
9492e0b0 512 // statistics on nonce distance\r
513 if (calibrate) { // for first call only. Otherwise reuse previous calibration\r
514 LED_B_ON();\r
8556b852 515\r
9492e0b0 516 davg = dmax = 0;\r
517 dmin = 2000;\r
518 delta_time = 0;\r
8556b852 519 \r
9492e0b0 520 for (rtr = 0; rtr < 17; rtr++) {\r
8556b852 521\r
9492e0b0 522 // prepare next select. No need to power down the card.\r
523 if(mifare_classic_halt(pcs, cuid)) {\r
524 if (MF_DBGLEVEL >= 1) Dbprintf("Nested: Halt error");\r
525 rtr--;\r
526 continue;\r
527 }\r
528\r
529 if(!iso14443a_select_card(uid, NULL, &cuid)) {\r
530 if (MF_DBGLEVEL >= 1) Dbprintf("Nested: Can't select card");\r
531 rtr--;\r
532 continue;\r
533 };\r
534\r
535 auth1_time = 0;\r
536 if(mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST, &nt1, &auth1_time)) {\r
537 if (MF_DBGLEVEL >= 1) Dbprintf("Nested: Auth1 error");\r
538 rtr--;\r
539 continue;\r
540 };\r
541\r
542 if (delta_time) {\r
543 auth2_time = auth1_time + delta_time;\r
544 } else {\r
545 auth2_time = 0;\r
546 }\r
547 if(mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_NESTED, &nt2, &auth2_time)) {\r
548 if (MF_DBGLEVEL >= 1) Dbprintf("Nested: Auth2 error");\r
549 rtr--;\r
550 continue;\r
551 };\r
552\r
b19bd5d6 553 nttmp = prng_successor(nt1, 100); //NXP Mifare is typical around 840,but for some unlicensed/compatible mifare card this can be 160\r
554 for (i = 101; i < 1200; i++) {\r
9492e0b0 555 nttmp = prng_successor(nttmp, 1);\r
556 if (nttmp == nt2) break;\r
557 }\r
558\r
559 if (i != 1200) {\r
560 if (rtr != 0) {\r
561 davg += i;\r
562 dmin = MIN(dmin, i);\r
563 dmax = MAX(dmax, i);\r
564 }\r
565 else {\r
566 delta_time = auth2_time - auth1_time + 32; // allow some slack for proper timing\r
567 }\r
568 if (MF_DBGLEVEL >= 3) Dbprintf("Nested: calibrating... ntdist=%d", i);\r
569 }\r
8556b852
M
570 }\r
571 \r
9492e0b0 572 if (rtr <= 1) return;\r
8556b852 573\r
9492e0b0 574 davg = (davg + (rtr - 1)/2) / (rtr - 1);\r
575 \r
576 if (MF_DBGLEVEL >= 3) Dbprintf("min=%d max=%d avg=%d, delta_time=%d", dmin, dmax, davg, delta_time);\r
8556b852 577\r
9492e0b0 578 dmin = davg - 2;\r
579 dmax = davg + 2;\r
580 \r
581 LED_B_OFF();\r
582 \r
583 }\r
8556b852
M
584// ------------------------------------------------------------------------------------------------- \r
585 \r
586 LED_C_ON();\r
587\r
588 // get crypted nonces for target sector\r
9492e0b0 589 for(i=0; i < 2; i++) { // look for exactly two different nonces\r
8556b852 590\r
9492e0b0 591 target_nt[i] = 0;\r
592 while(target_nt[i] == 0) { // continue until we have an unambiguous nonce\r
8556b852 593 \r
9492e0b0 594 // prepare next select. No need to power down the card.\r
595 if(mifare_classic_halt(pcs, cuid)) {\r
596 if (MF_DBGLEVEL >= 1) Dbprintf("Nested: Halt error");\r
597 continue;\r
598 }\r
8556b852 599\r
9492e0b0 600 if(!iso14443a_select_card(uid, NULL, &cuid)) {\r
601 if (MF_DBGLEVEL >= 1) Dbprintf("Nested: Can't select card");\r
602 continue;\r
603 };\r
8556b852 604 \r
9492e0b0 605 auth1_time = 0;\r
606 if(mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST, &nt1, &auth1_time)) {\r
607 if (MF_DBGLEVEL >= 1) Dbprintf("Nested: Auth1 error");\r
608 continue;\r
609 };\r
8556b852 610\r
9492e0b0 611 // nested authentication\r
612 auth2_time = auth1_time + delta_time;\r
613 len = mifare_sendcmd_shortex(pcs, AUTH_NESTED, 0x60 + (targetKeyType & 0x01), targetBlockNo, receivedAnswer, &par, &auth2_time);\r
614 if (len != 4) {\r
615 if (MF_DBGLEVEL >= 1) Dbprintf("Nested: Auth2 error len=%d", len);\r
616 continue;\r
617 };\r
8556b852 618 \r
9492e0b0 619 nt2 = bytes_to_num(receivedAnswer, 4); \r
620 if (MF_DBGLEVEL >= 3) Dbprintf("Nonce#%d: Testing nt1=%08x nt2enc=%08x nt2par=%02x", i+1, nt1, nt2, par);\r
8556b852 621 \r
9492e0b0 622 // Parity validity check\r
623 for (j = 0; j < 4; j++) {\r
624 par_array[j] = (oddparity(receivedAnswer[j]) != ((par & 0x08) >> 3));\r
625 par = par << 1;\r
626 }\r
627 \r
628 ncount = 0;\r
629 nttest = prng_successor(nt1, dmin - 1);\r
630 for (j = dmin; j < dmax + 1; j++) {\r
631 nttest = prng_successor(nttest, 1);\r
632 ks1 = nt2 ^ nttest;\r
633\r
634 if (valid_nonce(nttest, nt2, ks1, par_array)){\r
635 if (ncount > 0) { // we are only interested in disambiguous nonces, try again\r
636 if (MF_DBGLEVEL >= 3) Dbprintf("Nonce#%d: dismissed (ambigous), ntdist=%d", i+1, j);\r
637 target_nt[i] = 0;\r
638 break;\r
639 }\r
640 target_nt[i] = nttest;\r
641 target_ks[i] = ks1;\r
642 ncount++;\r
643 if (i == 1 && target_nt[1] == target_nt[0]) { // we need two different nonces\r
644 target_nt[i] = 0;\r
645 if (MF_DBGLEVEL >= 3) Dbprintf("Nonce#2: dismissed (= nonce#1), ntdist=%d", j);\r
8556b852
M
646 break;\r
647 }\r
9492e0b0 648 if (MF_DBGLEVEL >= 3) Dbprintf("Nonce#%d: valid, ntdist=%d", i+1, j);\r
8556b852 649 }\r
8556b852 650 }\r
9492e0b0 651 if (target_nt[i] == 0 && j == dmax+1 && MF_DBGLEVEL >= 3) Dbprintf("Nonce#%d: dismissed (all invalid)", i+1);\r
8556b852
M
652 }\r
653 }\r
654\r
655 LED_C_OFF();\r
656 \r
657 // ----------------------------- crypto1 destroy\r
658 crypto1_destroy(pcs);\r
659 \r
660 // add trace trailer\r
661 memset(uid, 0x44, 4);\r
662 LogTrace(uid, 4, 0, 0, TRUE);\r
663\r
9492e0b0 664 byte_t buf[4 + 4 * 4];\r
665 memcpy(buf, &cuid, 4);\r
666 memcpy(buf+4, &target_nt[0], 4);\r
667 memcpy(buf+8, &target_ks[0], 4);\r
668 memcpy(buf+12, &target_nt[1], 4);\r
669 memcpy(buf+16, &target_ks[1], 4);\r
8556b852
M
670 \r
671 LED_B_ON();\r
9492e0b0 672 cmd_send(CMD_ACK, 0, 2, targetBlockNo + (targetKeyType * 0x100), buf, sizeof(buf));\r
6e82300d 673 LED_B_OFF();\r
8556b852 674\r
9492e0b0 675 if (MF_DBGLEVEL >= 3) DbpString("NESTED FINISHED");\r
8556b852 676\r
8556b852
M
677 FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);\r
678 LEDsoff();\r
9492e0b0 679 iso14a_set_tracing(TRUE);\r
8556b852
M
680}\r
681\r
682//-----------------------------------------------------------------------------\r
9492e0b0 683// MIFARE check keys. key count up to 85. \r
8556b852
M
684// \r
685//-----------------------------------------------------------------------------\r
686void MifareChkKeys(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain)\r
687{\r
688 // params\r
689 uint8_t blockNo = arg0;\r
690 uint8_t keyType = arg1;\r
691 uint8_t keyCount = arg2;\r
692 uint64_t ui64Key = 0;\r
693 \r
694 // variables\r
695 int i;\r
696 byte_t isOK = 0;\r
1c611bbd 697 uint8_t uid[10];\r
8556b852
M
698 uint32_t cuid;\r
699 struct Crypto1State mpcs = {0, 0};\r
700 struct Crypto1State *pcs;\r
701 pcs = &mpcs;\r
702 \r
703 // clear debug level\r
704 int OLD_MF_DBGLEVEL = MF_DBGLEVEL; \r
705 MF_DBGLEVEL = MF_DBG_NONE;\r
706 \r
707 // clear trace\r
d19929cb 708 iso14a_clear_trace();\r
9492e0b0 709 iso14a_set_tracing(TRUE);\r
8556b852 710\r
7bc95e2e 711 iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);\r
8556b852
M
712\r
713 LED_A_ON();\r
714 LED_B_OFF();\r
715 LED_C_OFF();\r
716\r
8556b852 717 for (i = 0; i < keyCount; i++) {\r
9492e0b0 718 if(mifare_classic_halt(pcs, cuid)) {\r
719 if (MF_DBGLEVEL >= 1) Dbprintf("ChkKeys: Halt error");\r
720 }\r
8556b852
M
721\r
722 if(!iso14443a_select_card(uid, NULL, &cuid)) {\r
9492e0b0 723 if (OLD_MF_DBGLEVEL >= 1) Dbprintf("ChkKeys: Can't select card");\r
8556b852
M
724 break;\r
725 };\r
726\r
727 ui64Key = bytes_to_num(datain + i * 6, 6);\r
728 if(mifare_classic_auth(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST)) {\r
729 continue;\r
730 };\r
731 \r
732 isOK = 1;\r
733 break;\r
734 }\r
735 \r
736 // ----------------------------- crypto1 destroy\r
737 crypto1_destroy(pcs);\r
738 \r
8556b852 739 LED_B_ON();\r
6e82300d 740 cmd_send(CMD_ACK,isOK,0,0,datain + i * 6,6);\r
8556b852
M
741 LED_B_OFF();\r
742\r
743 // Thats it...\r
744 FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);\r
745 LEDsoff();\r
746\r
747 // restore debug level\r
748 MF_DBGLEVEL = OLD_MF_DBGLEVEL; \r
749}\r
750\r
751//-----------------------------------------------------------------------------\r
752// MIFARE commands set debug level\r
753// \r
754//-----------------------------------------------------------------------------\r
755void MifareSetDbgLvl(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain){\r
756 MF_DBGLEVEL = arg0;\r
757 Dbprintf("Debug level: %d", MF_DBGLEVEL);\r
758}\r
759\r
baeaf579 760\r
8556b852
M
761//-----------------------------------------------------------------------------\r
762// Work with emulator memory\r
763// \r
764//-----------------------------------------------------------------------------\r
765void MifareEMemClr(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain){\r
766 emlClearMem();\r
767}\r
768\r
baeaf579 769\r
8556b852
M
770void MifareEMemSet(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain){\r
771 emlSetMem(datain, arg0, arg1); // data, block num, blocks count\r
772}\r
773\r
baeaf579 774\r
8556b852 775void MifareEMemGet(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain){\r
8556b852 776\r
baeaf579 777 byte_t buf[48];\r
778 emlGetMem(buf, arg0, arg1); // data, block num, blocks count (max 4)\r
8556b852
M
779\r
780 LED_B_ON();\r
baeaf579 781 cmd_send(CMD_ACK,arg0,arg1,0,buf,48);\r
8556b852
M
782 LED_B_OFF();\r
783}\r
784\r
baeaf579 785\r
8556b852
M
786//-----------------------------------------------------------------------------\r
787// Load a card into the emulator memory\r
788// \r
789//-----------------------------------------------------------------------------\r
790void MifareECardLoad(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain){\r
baeaf579 791 uint8_t numSectors = arg0;\r
8556b852
M
792 uint8_t keyType = arg1;\r
793 uint64_t ui64Key = 0;\r
794 uint32_t cuid;\r
795 struct Crypto1State mpcs = {0, 0};\r
796 struct Crypto1State *pcs;\r
797 pcs = &mpcs;\r
798\r
799 // variables\r
800 byte_t dataoutbuf[16];\r
ab8b654e 801 byte_t dataoutbuf2[16];\r
1c611bbd 802 uint8_t uid[10];\r
8556b852
M
803\r
804 // clear trace\r
d19929cb 805 iso14a_clear_trace();\r
8556b852
M
806 iso14a_set_tracing(false);\r
807 \r
7bc95e2e 808 iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);\r
8556b852
M
809\r
810 LED_A_ON();\r
811 LED_B_OFF();\r
812 LED_C_OFF();\r
813 \r
baeaf579 814 bool isOK = true;\r
815\r
816 if(!iso14443a_select_card(uid, NULL, &cuid)) {\r
817 isOK = false;\r
818 if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card");\r
819 }\r
8556b852 820 \r
baeaf579 821 for (uint8_t sectorNo = 0; isOK && sectorNo < numSectors; sectorNo++) {\r
822 ui64Key = emlGetKey(sectorNo, keyType);\r
823 if (sectorNo == 0){\r
824 if(isOK && mifare_classic_auth(pcs, cuid, FirstBlockOfSector(sectorNo), keyType, ui64Key, AUTH_FIRST)) {\r
825 isOK = false;\r
826 if (MF_DBGLEVEL >= 1) Dbprintf("Sector[%2d]. Auth error", sectorNo);\r
8556b852 827 break;\r
baeaf579 828 }\r
829 } else {\r
830 if(isOK && mifare_classic_auth(pcs, cuid, FirstBlockOfSector(sectorNo), keyType, ui64Key, AUTH_NESTED)) {\r
831 isOK = false;\r
832 if (MF_DBGLEVEL >= 1) Dbprintf("Sector[%2d]. Auth nested error", sectorNo);\r
8556b852 833 break;\r
baeaf579 834 }\r
835 }\r
836 \r
837 for (uint8_t blockNo = 0; isOK && blockNo < NumBlocksPerSector(sectorNo); blockNo++) {\r
838 if(isOK && mifare_classic_readblock(pcs, cuid, FirstBlockOfSector(sectorNo) + blockNo, dataoutbuf)) {\r
839 isOK = false;\r
840 if (MF_DBGLEVEL >= 1) Dbprintf("Error reading sector %2d block %2d", sectorNo, blockNo);\r
ab8b654e
M
841 break;\r
842 };\r
baeaf579 843 if (isOK) {\r
844 if (blockNo < NumBlocksPerSector(sectorNo) - 1) {\r
845 emlSetMem(dataoutbuf, FirstBlockOfSector(sectorNo) + blockNo, 1);\r
846 } else { // sector trailer, keep the keys, set only the AC\r
847 emlGetMem(dataoutbuf2, FirstBlockOfSector(sectorNo) + blockNo, 1);\r
848 memcpy(&dataoutbuf2[6], &dataoutbuf[6], 4);\r
849 emlSetMem(dataoutbuf2, FirstBlockOfSector(sectorNo) + blockNo, 1);\r
850 }\r
851 }\r
8556b852
M
852 }\r
853\r
baeaf579 854 }\r
855\r
856 if(mifare_classic_halt(pcs, cuid)) {\r
857 if (MF_DBGLEVEL >= 1) Dbprintf("Halt error");\r
858 };\r
8556b852
M
859\r
860 // ----------------------------- crypto1 destroy\r
861 crypto1_destroy(pcs);\r
ab8b654e
M
862\r
863 FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);\r
864 LEDsoff();\r
8556b852
M
865 \r
866 if (MF_DBGLEVEL >= 2) DbpString("EMUL FILL SECTORS FINISHED");\r
867\r
8556b852
M
868}\r
869\r
0675f200
M
870\r
871//-----------------------------------------------------------------------------\r
872// Work with "magic Chinese" card (email him: ouyangweidaxian@live.cn)\r
873// \r
874//-----------------------------------------------------------------------------\r
875void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain){\r
876 \r
877 // params\r
878 uint8_t needWipe = arg0;\r
208a0166
M
879 // bit 0 - need get UID\r
880 // bit 1 - need wupC\r
881 // bit 2 - need HALT after sequence\r
882 // bit 3 - need init FPGA and field before sequence\r
883 // bit 4 - need reset FPGA and LED\r
884 uint8_t workFlags = arg1;\r
0675f200
M
885 uint8_t blockNo = arg2;\r
886 \r
887 // card commands\r
888 uint8_t wupC1[] = { 0x40 }; \r
889 uint8_t wupC2[] = { 0x43 }; \r
890 uint8_t wipeC[] = { 0x41 }; \r
891 \r
892 // variables\r
893 byte_t isOK = 0;\r
1c611bbd 894 uint8_t uid[10];\r
0675f200
M
895 uint8_t d_block[18];\r
896 uint32_t cuid;\r
897 \r
1c611bbd 898 memset(uid, 0x00, 10);\r
0675f200
M
899 uint8_t* receivedAnswer = mifare_get_bigbufptr();\r
900 \r
208a0166
M
901 if (workFlags & 0x08) {\r
902 // clear trace\r
d19929cb 903 iso14a_clear_trace();\r
208a0166 904 iso14a_set_tracing(TRUE);\r
0675f200 905\r
7bc95e2e 906 iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);\r
0675f200 907\r
208a0166
M
908 LED_A_ON();\r
909 LED_B_OFF();\r
910 LED_C_OFF();\r
0675f200 911 \r
208a0166
M
912 SpinDelay(300);\r
913 FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);\r
914 SpinDelay(100);\r
915 FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD);\r
916 }\r
0675f200
M
917\r
918 while (true) {\r
919 // get UID from chip\r
208a0166 920 if (workFlags & 0x01) {\r
0675f200
M
921 if(!iso14443a_select_card(uid, NULL, &cuid)) {\r
922 if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card");\r
923 break;\r
924 };\r
925\r
926 if(mifare_classic_halt(NULL, cuid)) {\r
927 if (MF_DBGLEVEL >= 1) Dbprintf("Halt error");\r
928 break;\r
929 };\r
930 };\r
931 \r
932 // reset chip\r
933 if (needWipe){\r
9492e0b0 934 ReaderTransmitBitsPar(wupC1,7,0, NULL);\r
0675f200
M
935 if(!ReaderReceive(receivedAnswer) || (receivedAnswer[0] != 0x0a)) {\r
936 if (MF_DBGLEVEL >= 1) Dbprintf("wupC1 error");\r
937 break;\r
938 };\r
939\r
9492e0b0 940 ReaderTransmit(wipeC, sizeof(wipeC), NULL);\r
0675f200
M
941 if(!ReaderReceive(receivedAnswer) || (receivedAnswer[0] != 0x0a)) {\r
942 if (MF_DBGLEVEL >= 1) Dbprintf("wipeC error");\r
943 break;\r
944 };\r
945\r
946 if(mifare_classic_halt(NULL, cuid)) {\r
947 if (MF_DBGLEVEL >= 1) Dbprintf("Halt error");\r
948 break;\r
949 };\r
950 }; \r
951\r
545a1f38 952 // write block\r
208a0166 953 if (workFlags & 0x02) {\r
9492e0b0 954 ReaderTransmitBitsPar(wupC1,7,0, NULL);\r
208a0166
M
955 if(!ReaderReceive(receivedAnswer) || (receivedAnswer[0] != 0x0a)) {\r
956 if (MF_DBGLEVEL >= 1) Dbprintf("wupC1 error");\r
957 break;\r
958 };\r
0675f200 959\r
9492e0b0 960 ReaderTransmit(wupC2, sizeof(wupC2), NULL);\r
208a0166
M
961 if(!ReaderReceive(receivedAnswer) || (receivedAnswer[0] != 0x0a)) {\r
962 if (MF_DBGLEVEL >= 1) Dbprintf("wupC2 error");\r
963 break;\r
964 };\r
965 }\r
0675f200 966\r
9492e0b0 967 if ((mifare_sendcmd_short(NULL, 0, 0xA0, blockNo, receivedAnswer, NULL) != 1) || (receivedAnswer[0] != 0x0a)) {\r
0675f200
M
968 if (MF_DBGLEVEL >= 1) Dbprintf("write block send command error");\r
969 break;\r
970 };\r
971 \r
972 memcpy(d_block, datain, 16);\r
973 AppendCrc14443a(d_block, 16);\r
974 \r
9492e0b0 975 ReaderTransmit(d_block, sizeof(d_block), NULL);\r
0675f200
M
976 if ((ReaderReceive(receivedAnswer) != 1) || (receivedAnswer[0] != 0x0a)) {\r
977 if (MF_DBGLEVEL >= 1) Dbprintf("write block send data error");\r
978 break;\r
979 }; \r
980 \r
208a0166
M
981 if (workFlags & 0x04) {\r
982 if (mifare_classic_halt(NULL, cuid)) {\r
983 if (MF_DBGLEVEL >= 1) Dbprintf("Halt error");\r
984 break;\r
985 };\r
986 }\r
0675f200
M
987 \r
988 isOK = 1;\r
989 break;\r
990 }\r
991 \r
0675f200 992 LED_B_ON();\r
baeaf579 993 cmd_send(CMD_ACK,isOK,0,0,uid,4);\r
0675f200
M
994 LED_B_OFF();\r
995\r
545a1f38 996 if ((workFlags & 0x10) || (!isOK)) {\r
208a0166
M
997 // Thats it...\r
998 FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);\r
999 LEDsoff();\r
1000 }\r
0675f200 1001}\r
545a1f38 1002\r
baeaf579 1003\r
545a1f38
M
1004void MifareCGetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain){\r
1005 \r
1006 // params\r
1007 // bit 1 - need wupC\r
1008 // bit 2 - need HALT after sequence\r
1009 // bit 3 - need init FPGA and field before sequence\r
1010 // bit 4 - need reset FPGA and LED\r
1011 uint8_t workFlags = arg0;\r
1012 uint8_t blockNo = arg2;\r
1013 \r
1014 // card commands\r
1015 uint8_t wupC1[] = { 0x40 }; \r
1016 uint8_t wupC2[] = { 0x43 }; \r
1017 \r
1018 // variables\r
1019 byte_t isOK = 0;\r
1020 uint8_t data[18];\r
1021 uint32_t cuid = 0;\r
1022 \r
1023 memset(data, 0x00, 18);\r
1024 uint8_t* receivedAnswer = mifare_get_bigbufptr();\r
1025 \r
1026 if (workFlags & 0x08) {\r
1027 // clear trace\r
d19929cb 1028 iso14a_clear_trace();\r
545a1f38
M
1029 iso14a_set_tracing(TRUE);\r
1030\r
7bc95e2e 1031 iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);\r
545a1f38
M
1032\r
1033 LED_A_ON();\r
1034 LED_B_OFF();\r
1035 LED_C_OFF();\r
1036 \r
1037 SpinDelay(300);\r
1038 FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);\r
1039 SpinDelay(100);\r
1040 FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD);\r
1041 }\r
1042\r
1043 while (true) {\r
1044 if (workFlags & 0x02) {\r
7bc95e2e 1045 ReaderTransmitBitsPar(wupC1,7,0, NULL);\r
545a1f38
M
1046 if(!ReaderReceive(receivedAnswer) || (receivedAnswer[0] != 0x0a)) {\r
1047 if (MF_DBGLEVEL >= 1) Dbprintf("wupC1 error");\r
1048 break;\r
1049 };\r
1050\r
9492e0b0 1051 ReaderTransmit(wupC2, sizeof(wupC2), NULL);\r
545a1f38
M
1052 if(!ReaderReceive(receivedAnswer) || (receivedAnswer[0] != 0x0a)) {\r
1053 if (MF_DBGLEVEL >= 1) Dbprintf("wupC2 error");\r
1054 break;\r
1055 };\r
1056 }\r
1057\r
1058 // read block\r
9492e0b0 1059 if ((mifare_sendcmd_short(NULL, 0, 0x30, blockNo, receivedAnswer, NULL) != 18)) {\r
545a1f38
M
1060 if (MF_DBGLEVEL >= 1) Dbprintf("read block send command error");\r
1061 break;\r
1062 };\r
1063 memcpy(data, receivedAnswer, 18);\r
1064 \r
1065 if (workFlags & 0x04) {\r
1066 if (mifare_classic_halt(NULL, cuid)) {\r
1067 if (MF_DBGLEVEL >= 1) Dbprintf("Halt error");\r
1068 break;\r
1069 };\r
1070 }\r
1071 \r
1072 isOK = 1;\r
1073 break;\r
1074 }\r
1075 \r
545a1f38 1076 LED_B_ON();\r
baeaf579 1077 cmd_send(CMD_ACK,isOK,0,0,data,18);\r
545a1f38
M
1078 LED_B_OFF();\r
1079\r
1080 if ((workFlags & 0x10) || (!isOK)) {\r
1081 // Thats it...\r
1082 FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);\r
1083 LEDsoff();\r
1084 }\r
1085}\r
1086\r
Impressum, Datenschutz