]> cvs.zerfleddert.de Git - proxmark3-svn/blame_incremental - armsrc/iso15693.c
ADD: HF 14A READER is now able to see if a presented card responses to the chinese...
[proxmark3-svn] / armsrc / iso15693.c
... / ...
CommitLineData
1//-----------------------------------------------------------------------------
2// Jonathan Westhues, split Nov 2006
3// Modified by Greg Jones, Jan 2009
4// Modified by Adrian Dabrowski "atrox", Mar-Sept 2010,Oct 2011
5//
6// This code is licensed to you under the terms of the GNU GPL, version 2 or,
7// at your option, any later version. See the LICENSE.txt file for the text of
8// the license.
9//-----------------------------------------------------------------------------
10// Routines to support ISO 15693. This includes both the reader software and
11// the `fake tag' modes, but at the moment I've implemented only the reader
12// stuff, and that barely.
13// Modified to perform modulation onboard in arm rather than on PC
14// Also added additional reader commands (SELECT, READ etc.)
15//-----------------------------------------------------------------------------
16// The ISO 15693 describes two transmission modes from reader to tag, and 4
17// transmission modes from tag to reader. As of Mar 2010 this code only
18// supports one of each: "1of4" mode from reader to tag, and the highspeed
19// variant with one subcarrier from card to reader.
20// As long, as the card fully support ISO 15693 this is no problem, since the
21// reader chooses both data rates, but some non-standard tags do not. Further for
22// the simulation to work, we will need to support all data rates.
23//
24// VCD (reader) -> VICC (tag)
25// 1 out of 256:
26// data rate: 1,66 kbit/s (fc/8192)
27// used for long range
28// 1 out of 4:
29// data rate: 26,48 kbit/s (fc/512)
30// used for short range, high speed
31//
32// VICC (tag) -> VCD (reader)
33// Modulation:
34// ASK / one subcarrier (423,75 khz)
35// FSK / two subcarriers (423,75 khz && 484,28 khz)
36// Data Rates / Modes:
37// low ASK: 6,62 kbit/s
38// low FSK: 6.67 kbit/s
39// high ASK: 26,48 kbit/s
40// high FSK: 26,69 kbit/s
41//-----------------------------------------------------------------------------
42// added "1 out of 256" mode (for VCD->PICC) - atrox 20100911
43
44
45// Random Remarks:
46// *) UID is always used "transmission order" (LSB), which is reverse to display order
47
48// TODO / BUGS / ISSUES:
49// *) writing to tags takes longer: we miss the answer from the tag in most cases
50// -> tweak the read-timeout times
51// *) signal decoding from the card is still a bit shaky.
52// *) signal decoding is unable to detect collissions.
53// *) add anti-collission support for inventory-commands
54// *) read security status of a block
55// *) sniffing and simulation do only support one transmission mode. need to support
56// all 8 transmission combinations
57// *) remove or refactor code under "depricated"
58// *) document all the functions
59
60
61#include "../include/proxmark3.h"
62#include "util.h"
63#include "apps.h"
64#include "string.h"
65#include "../common/iso15693tools.h"
66#include "../common/cmd.h"
67#include "crapto1.h"
68#include "mifareutil.h"
69
70#define arraylen(x) (sizeof(x)/sizeof((x)[0]))
71
72///////////////////////////////////////////////////////////////////////
73// ISO 15693 Part 2 - Air Interface
74// This section basicly contains transmission and receiving of bits
75///////////////////////////////////////////////////////////////////////
76
77#define FrameSOF Iso15693FrameSOF
78#define Logic0 Iso15693Logic0
79#define Logic1 Iso15693Logic1
80#define FrameEOF Iso15693FrameEOF
81
82#define Crc(data,datalen) Iso15693Crc(data,datalen)
83#define AddCrc(data,datalen) Iso15693AddCrc(data,datalen)
84#define sprintUID(target,uid) Iso15693sprintUID(target,uid)
85
86int DEBUG=0;
87
88
89// ---------------------------
90// Signal Processing
91// ---------------------------
92
93// prepare data using "1 out of 4" code for later transmission
94// resulting data rate is 26,48 kbit/s (fc/512)
95// cmd ... data
96// n ... length of data
97static void CodeIso15693AsReader(uint8_t *cmd, int n)
98{
99 int i, j;
100
101 ToSendReset();
102
103 // Give it a bit of slack at the beginning
104 for(i = 0; i < 24; i++) {
105 ToSendStuffBit(1);
106 }
107
108 // SOF for 1of4
109 ToSendStuffBit(0);
110 ToSendStuffBit(1);
111 ToSendStuffBit(1);
112 ToSendStuffBit(1);
113 ToSendStuffBit(1);
114 ToSendStuffBit(0);
115 ToSendStuffBit(1);
116 ToSendStuffBit(1);
117 for(i = 0; i < n; i++) {
118 for(j = 0; j < 8; j += 2) {
119 int these = (cmd[i] >> j) & 3;
120 switch(these) {
121 case 0:
122 ToSendStuffBit(1);
123 ToSendStuffBit(0);
124 ToSendStuffBit(1);
125 ToSendStuffBit(1);
126 ToSendStuffBit(1);
127 ToSendStuffBit(1);
128 ToSendStuffBit(1);
129 ToSendStuffBit(1);
130 break;
131 case 1:
132 ToSendStuffBit(1);
133 ToSendStuffBit(1);
134 ToSendStuffBit(1);
135 ToSendStuffBit(0);
136 ToSendStuffBit(1);
137 ToSendStuffBit(1);
138 ToSendStuffBit(1);
139 ToSendStuffBit(1);
140 break;
141 case 2:
142 ToSendStuffBit(1);
143 ToSendStuffBit(1);
144 ToSendStuffBit(1);
145 ToSendStuffBit(1);
146 ToSendStuffBit(1);
147 ToSendStuffBit(0);
148 ToSendStuffBit(1);
149 ToSendStuffBit(1);
150 break;
151 case 3:
152 ToSendStuffBit(1);
153 ToSendStuffBit(1);
154 ToSendStuffBit(1);
155 ToSendStuffBit(1);
156 ToSendStuffBit(1);
157 ToSendStuffBit(1);
158 ToSendStuffBit(1);
159 ToSendStuffBit(0);
160 break;
161 }
162 }
163 }
164 // EOF
165 ToSendStuffBit(1);
166 ToSendStuffBit(1);
167 ToSendStuffBit(0);
168 ToSendStuffBit(1);
169
170 // And slack at the end, too.
171 for(i = 0; i < 24; i++) {
172 ToSendStuffBit(1);
173 }
174}
175
176// encode data using "1 out of 256" sheme
177// data rate is 1,66 kbit/s (fc/8192)
178// is designed for more robust communication over longer distances
179static void CodeIso15693AsReader256(uint8_t *cmd, int n)
180{
181 int i, j;
182
183 ToSendReset();
184
185 // Give it a bit of slack at the beginning
186 for(i = 0; i < 24; i++) {
187 ToSendStuffBit(1);
188 }
189
190 // SOF for 1of256
191 ToSendStuffBit(0);
192 ToSendStuffBit(1);
193 ToSendStuffBit(1);
194 ToSendStuffBit(1);
195 ToSendStuffBit(1);
196 ToSendStuffBit(1);
197 ToSendStuffBit(1);
198 ToSendStuffBit(0);
199
200 for(i = 0; i < n; i++) {
201 for (j = 0; j<=255; j++) {
202 if (cmd[i]==j) {
203 ToSendStuffBit(1);
204 ToSendStuffBit(0);
205 } else {
206 ToSendStuffBit(1);
207 ToSendStuffBit(1);
208 }
209 }
210 }
211 // EOF
212 ToSendStuffBit(1);
213 ToSendStuffBit(1);
214 ToSendStuffBit(0);
215 ToSendStuffBit(1);
216
217 // And slack at the end, too.
218 for(i = 0; i < 24; i++) {
219 ToSendStuffBit(1);
220 }
221}
222
223
224// Transmit the command (to the tag) that was placed in ToSend[].
225static void TransmitTo15693Tag(const uint8_t *cmd, int len, int *samples, int *wait)
226{
227 int c;
228
229// FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD);
230 FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX);
231 if(*wait < 10) { *wait = 10; }
232
233// for(c = 0; c < *wait;) {
234// if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) {
235// AT91C_BASE_SSC->SSC_THR = 0x00; // For exact timing!
236// c++;
237// }
238// if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) {
239// volatile uint32_t r = AT91C_BASE_SSC->SSC_RHR;
240// (void)r;
241// }
242// WDT_HIT();
243// }
244
245 c = 0;
246 for(;;) {
247 if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) {
248 AT91C_BASE_SSC->SSC_THR = cmd[c];
249 c++;
250 if(c >= len) {
251 break;
252 }
253 }
254 if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) {
255 volatile uint32_t r = AT91C_BASE_SSC->SSC_RHR;
256 (void)r;
257 }
258 WDT_HIT();
259 }
260 *samples = (c + *wait) << 3;
261}
262
263//-----------------------------------------------------------------------------
264// Transmit the command (to the reader) that was placed in ToSend[].
265//-----------------------------------------------------------------------------
266static void TransmitTo15693Reader(const uint8_t *cmd, int len, int *samples, int *wait)
267{
268 int c = 0;
269 FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR|FPGA_HF_SIMULATOR_MODULATE_424K);
270 if(*wait < 10) { *wait = 10; }
271
272 for(;;) {
273 if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) {
274 AT91C_BASE_SSC->SSC_THR = cmd[c];
275 c++;
276 if(c >= len) {
277 break;
278 }
279 }
280 if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) {
281 volatile uint32_t r = AT91C_BASE_SSC->SSC_RHR;
282 (void)r;
283 }
284 WDT_HIT();
285 }
286 *samples = (c + *wait) << 3;
287}
288
289
290// Read from Tag
291// Parameters:
292// receivedResponse
293// maxLen
294// samples
295// elapsed
296// returns:
297// number of decoded bytes
298static int GetIso15693AnswerFromTag(uint8_t *receivedResponse, int maxLen, int *samples, int *elapsed)
299{
300 int c = 0;
301 uint8_t *dest = (uint8_t *)BigBuf;
302 int getNext = 0;
303
304 int8_t prev = 0;
305
306// NOW READ RESPONSE
307 FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR);
308 //spindelay(60); // greg - experiment to get rid of some of the 0 byte/failed reads
309 c = 0;
310 getNext = FALSE;
311 for(;;) {
312 if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) {
313 AT91C_BASE_SSC->SSC_THR = 0x43;
314 }
315 if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) {
316 int8_t b;
317 b = (int8_t)AT91C_BASE_SSC->SSC_RHR;
318
319 // The samples are correlations against I and Q versions of the
320 // tone that the tag AM-modulates, so every other sample is I,
321 // every other is Q. We just want power, so abs(I) + abs(Q) is
322 // close to what we want.
323 if(getNext) {
324 int8_t r;
325
326 if(b < 0) {
327 r = -b;
328 } else {
329 r = b;
330 }
331 if(prev < 0) {
332 r -= prev;
333 } else {
334 r += prev;
335 }
336
337 dest[c++] = (uint8_t)r;
338
339 if(c >= 2000) {
340 break;
341 }
342 } else {
343 prev = b;
344 }
345
346 getNext = !getNext;
347 }
348 }
349
350 //////////////////////////////////////////
351 /////////// DEMODULATE ///////////////////
352 //////////////////////////////////////////
353
354 int i, j;
355 int max = 0, maxPos=0;
356
357 int skip = 4;
358
359 // if(GraphTraceLen < 1000) return; // THIS CHECKS FOR A BUFFER TO SMALL
360
361 // First, correlate for SOF
362 for(i = 0; i < 100; i++) {
363 int corr = 0;
364 for(j = 0; j < arraylen(FrameSOF); j += skip) {
365 corr += FrameSOF[j]*dest[i+(j/skip)];
366 }
367 if(corr > max) {
368 max = corr;
369 maxPos = i;
370 }
371 }
372 // DbpString("SOF at %d, correlation %d", maxPos,max/(arraylen(FrameSOF)/skip));
373
374 int k = 0; // this will be our return value
375
376 // greg - If correlation is less than 1 then there's little point in continuing
377 if ((max/(arraylen(FrameSOF)/skip)) >= 1)
378 {
379
380 i = maxPos + arraylen(FrameSOF)/skip;
381
382 uint8_t outBuf[20];
383 memset(outBuf, 0, sizeof(outBuf));
384 uint8_t mask = 0x01;
385 for(;;) {
386 int corr0 = 0, corr1 = 0, corrEOF = 0;
387 for(j = 0; j < arraylen(Logic0); j += skip) {
388 corr0 += Logic0[j]*dest[i+(j/skip)];
389 }
390 for(j = 0; j < arraylen(Logic1); j += skip) {
391 corr1 += Logic1[j]*dest[i+(j/skip)];
392 }
393 for(j = 0; j < arraylen(FrameEOF); j += skip) {
394 corrEOF += FrameEOF[j]*dest[i+(j/skip)];
395 }
396 // Even things out by the length of the target waveform.
397 corr0 *= 4;
398 corr1 *= 4;
399
400 if(corrEOF > corr1 && corrEOF > corr0) {
401 // DbpString("EOF at %d", i);
402 break;
403 } else if(corr1 > corr0) {
404 i += arraylen(Logic1)/skip;
405 outBuf[k] |= mask;
406 } else {
407 i += arraylen(Logic0)/skip;
408 }
409 mask <<= 1;
410 if(mask == 0) {
411 k++;
412 mask = 0x01;
413 }
414 if((i+(int)arraylen(FrameEOF)) >= 2000) {
415 DbpString("ran off end!");
416 break;
417 }
418 }
419 if(mask != 0x01) { // this happens, when we miss the EOF
420 // TODO: for some reason this happens quite often
421 if (DEBUG) Dbprintf("error, uneven octet! (extra bits!) mask=%02x", mask);
422 if (mask<0x08) k--; // discard the last uneven octet;
423 // 0x08 is an assumption - but works quite often
424 }
425 // uint8_t str1 [8];
426 // itoa(k,str1);
427 // strncat(str1," octets read",8);
428
429 // DbpString( str1); // DbpString("%d octets", k);
430
431 // for(i = 0; i < k; i+=3) {
432 // //DbpString("# %2d: %02x ", i, outBuf[i]);
433 // DbpIntegers(outBuf[i],outBuf[i+1],outBuf[i+2]);
434 // }
435
436 for(i = 0; i < k; i++) {
437 receivedResponse[i] = outBuf[i];
438 }
439 } // "end if correlation > 0" (max/(arraylen(FrameSOF)/skip))
440 return k; // return the number of bytes demodulated
441
442/// DbpString("CRC=%04x", Iso15693Crc(outBuf, k-2));
443
444}
445
446
447// Now the GetISO15693 message from sniffing command
448static int GetIso15693AnswerFromSniff(uint8_t *receivedResponse, int maxLen, int *samples, int *elapsed)
449{
450 int c = 0;
451 uint8_t *dest = (uint8_t *)BigBuf;
452 int getNext = 0;
453
454 int8_t prev = 0;
455
456 // NOW READ RESPONSE
457 FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR);
458 //spindelay(60); // greg - experiment to get rid of some of the 0 byte/failed reads
459 c = 0;
460 getNext = FALSE;
461 for(;;) {
462 if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) {
463 AT91C_BASE_SSC->SSC_THR = 0x43;
464 }
465 if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) {
466 int8_t b = (int8_t)AT91C_BASE_SSC->SSC_RHR;
467
468 // The samples are correlations against I and Q versions of the
469 // tone that the tag AM-modulates, so every other sample is I,
470 // every other is Q. We just want power, so abs(I) + abs(Q) is
471 // close to what we want.
472 if (getNext) {
473
474 dest[c++] = abs(b) + abs(prev);
475
476 if(c >= 20000) {
477 break;
478 }
479 } else {
480 prev = b;
481 }
482
483 getNext = !getNext;
484 }
485 }
486
487 //////////////////////////////////////////
488 /////////// DEMODULATE ///////////////////
489 //////////////////////////////////////////
490
491 int i, j;
492 int max = 0, maxPos=0;
493
494 int skip = 4;
495
496// if(GraphTraceLen < 1000) return; // THIS CHECKS FOR A BUFFER TO SMALL
497
498 // First, correlate for SOF
499 for(i = 0; i < 19000; i++) {
500 int corr = 0;
501 for(j = 0; j < arraylen(FrameSOF); j += skip) {
502 corr += FrameSOF[j]*dest[i+(j/skip)];
503 }
504 if(corr > max) {
505 max = corr;
506 maxPos = i;
507 }
508 }
509// DbpString("SOF at %d, correlation %d", maxPos,max/(arraylen(FrameSOF)/skip));
510
511 int k = 0; // this will be our return value
512
513 // greg - If correlation is less than 1 then there's little point in continuing
514 if ((max/(arraylen(FrameSOF)/skip)) >= 1) // THIS SHOULD BE 1
515 {
516
517 i = maxPos + arraylen(FrameSOF)/skip;
518
519 uint8_t outBuf[20];
520 memset(outBuf, 0, sizeof(outBuf));
521 uint8_t mask = 0x01;
522 for(;;) {
523 int corr0 = 0, corr1 = 0, corrEOF = 0;
524 for(j = 0; j < arraylen(Logic0); j += skip) {
525 corr0 += Logic0[j]*dest[i+(j/skip)];
526 }
527 for(j = 0; j < arraylen(Logic1); j += skip) {
528 corr1 += Logic1[j]*dest[i+(j/skip)];
529 }
530 for(j = 0; j < arraylen(FrameEOF); j += skip) {
531 corrEOF += FrameEOF[j]*dest[i+(j/skip)];
532 }
533 // Even things out by the length of the target waveform.
534 corr0 *= 4;
535 corr1 *= 4;
536
537 if(corrEOF > corr1 && corrEOF > corr0) {
538 // DbpString("EOF at %d", i);
539 break;
540 } else if(corr1 > corr0) {
541 i += arraylen(Logic1)/skip;
542 outBuf[k] |= mask;
543 } else {
544 i += arraylen(Logic0)/skip;
545 }
546 mask <<= 1;
547 if(mask == 0) {
548 k++;
549 mask = 0x01;
550 }
551 if((i+(int)arraylen(FrameEOF)) >= 2000) {
552 DbpString("ran off end!");
553 break;
554 }
555 }
556 if(mask != 0x01) {
557 DbpString("sniff: error, uneven octet! (discard extra bits!)");
558 /// DbpString(" mask=%02x", mask);
559 }
560 // uint8_t str1 [8];
561 // itoa(k,str1);
562 // strncat(str1," octets read",8);
563
564 // DbpString( str1); // DbpString("%d octets", k);
565
566 // for(i = 0; i < k; i+=3) {
567 // //DbpString("# %2d: %02x ", i, outBuf[i]);
568 // DbpIntegers(outBuf[i],outBuf[i+1],outBuf[i+2]);
569 // }
570
571 for(i = 0; i < k; i++) {
572 receivedResponse[i] = outBuf[i];
573 }
574 } // "end if correlation > 0" (max/(arraylen(FrameSOF)/skip))
575 return k; // return the number of bytes demodulated
576
577/// DbpString("CRC=%04x", Iso15693Crc(outBuf, k-2));
578}
579
580
581static void BuildIdentifyRequest(void);
582//-----------------------------------------------------------------------------
583// Start to read an ISO 15693 tag. We send an identify request, then wait
584// for the response. The response is not demodulated, just left in the buffer
585// so that it can be downloaded to a PC and processed there.
586//-----------------------------------------------------------------------------
587void AcquireRawAdcSamplesIso15693(void)
588{
589 uint8_t *dest = get_bigbufptr_recvrespbuf();
590
591 int c = 0;
592 int getNext = 0;
593 int8_t prev = 0;
594
595 FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
596 BuildIdentifyRequest();
597
598 SetAdcMuxFor(GPIO_MUXSEL_HIPKD);
599
600 // Give the tags time to energize
601 FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR);
602 SpinDelay(100);
603
604 // Now send the command
605 FpgaSetupSsc();
606 FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX);
607
608 c = 0;
609 for(;;) {
610 if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) {
611 AT91C_BASE_SSC->SSC_THR = ToSend[c];
612 c++;
613 if(c == ToSendMax+3) {
614 break;
615 }
616 }
617 if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) {
618 volatile uint32_t r = AT91C_BASE_SSC->SSC_RHR;
619 (void)r;
620 }
621 WDT_HIT();
622 }
623
624 FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR);
625
626 c = 0;
627 getNext = FALSE;
628 for(;;) {
629 if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) {
630 AT91C_BASE_SSC->SSC_THR = 0x43;
631 }
632 if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) {
633 int8_t b;
634 b = (int8_t)AT91C_BASE_SSC->SSC_RHR;
635
636 // The samples are correlations against I and Q versions of the
637 // tone that the tag AM-modulates, so every other sample is I,
638 // every other is Q. We just want power, so abs(I) + abs(Q) is
639 // close to what we want.
640 if(getNext) {
641 int8_t r;
642
643 if(b < 0) {
644 r = -b;
645 } else {
646 r = b;
647 }
648 if(prev < 0) {
649 r -= prev;
650 } else {
651 r += prev;
652 }
653
654 dest[c++] = (uint8_t)r;
655
656 if(c >= 2000) {
657 break;
658 }
659 } else {
660 prev = b;
661 }
662
663 getNext = !getNext;
664 }
665 }
666}
667
668
669void RecordRawAdcSamplesIso15693(void)
670{
671 uint8_t *dest = get_bigbufptr_recvrespbuf();
672
673 int c = 0;
674 int getNext = 0;
675 int8_t prev = 0;
676
677 FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
678 // Setup SSC
679 FpgaSetupSsc();
680
681 // Start from off (no field generated)
682 FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
683 SpinDelay(200);
684
685 SetAdcMuxFor(GPIO_MUXSEL_HIPKD);
686
687 SpinDelay(100);
688
689 FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR);
690
691 c = 0;
692 getNext = FALSE;
693 for(;;) {
694 if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) {
695 AT91C_BASE_SSC->SSC_THR = 0x43;
696 }
697 if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) {
698 int8_t b;
699 b = (int8_t)AT91C_BASE_SSC->SSC_RHR;
700
701 // The samples are correlations against I and Q versions of the
702 // tone that the tag AM-modulates, so every other sample is I,
703 // every other is Q. We just want power, so abs(I) + abs(Q) is
704 // close to what we want.
705 if(getNext) {
706 int8_t r;
707
708 if(b < 0) {
709 r = -b;
710 } else {
711 r = b;
712 }
713 if(prev < 0) {
714 r -= prev;
715 } else {
716 r += prev;
717 }
718
719 dest[c++] = (uint8_t)r;
720
721 if(c >= 7000) {
722 break;
723 }
724 } else {
725 prev = b;
726 }
727
728 getNext = !getNext;
729 WDT_HIT();
730 }
731 }
732 Dbprintf("fin record");
733}
734
735
736// Initialize the proxmark as iso15k reader
737// (this might produces glitches that confuse some tags
738void Iso15693InitReader() {
739 LED_A_ON();
740 LED_B_ON();
741 LED_C_OFF();
742 LED_D_OFF();
743
744 FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
745 // Setup SSC
746 // FpgaSetupSsc();
747
748 // Start from off (no field generated)
749 FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
750 SpinDelay(10);
751
752 SetAdcMuxFor(GPIO_MUXSEL_HIPKD);
753 FpgaSetupSsc();
754
755 // Give the tags time to energize
756 FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR);
757 SpinDelay(250);
758
759 LED_A_ON();
760 LED_B_OFF();
761 LED_C_OFF();
762 LED_D_OFF();
763}
764
765///////////////////////////////////////////////////////////////////////
766// ISO 15693 Part 3 - Air Interface
767// This section basicly contains transmission and receiving of bits
768///////////////////////////////////////////////////////////////////////
769
770// Encode (into the ToSend buffers) an identify request, which is the first
771// thing that you must send to a tag to get a response.
772static void BuildIdentifyRequest(void)
773{
774 uint8_t cmd[5];
775
776 uint16_t crc;
777 // one sub-carrier, inventory, 1 slot, fast rate
778 // AFI is at bit 5 (1<<4) when doing an INVENTORY
779 cmd[0] = (1 << 2) | (1 << 5) | (1 << 1);
780 // inventory command code
781 cmd[1] = 0x01;
782 // no mask
783 cmd[2] = 0x00;
784 //Now the CRC
785 crc = Crc(cmd, 3);
786 cmd[3] = crc & 0xff;
787 cmd[4] = crc >> 8;
788
789 CodeIso15693AsReader(cmd, sizeof(cmd));
790}
791
792// uid is in transmission order (which is reverse of display order)
793static void BuildReadBlockRequest(uint8_t *uid, uint8_t blockNumber )
794{
795 uint8_t cmd[13];
796
797 uint16_t crc;
798 // If we set the Option_Flag in this request, the VICC will respond with the secuirty status of the block
799 // followed by teh block data
800 // one sub-carrier, inventory, 1 slot, fast rate
801 cmd[0] = (1 << 6)| (1 << 5) | (1 << 1); // no SELECT bit, ADDR bit, OPTION bit
802 // READ BLOCK command code
803 cmd[1] = 0x20;
804 // UID may be optionally specified here
805 // 64-bit UID
806 cmd[2] = uid[0];
807 cmd[3] = uid[1];
808 cmd[4] = uid[2];
809 cmd[5] = uid[3];
810 cmd[6] = uid[4];
811 cmd[7] = uid[5];
812 cmd[8] = uid[6];
813 cmd[9] = uid[7]; // 0xe0; // always e0 (not exactly unique)
814 // Block number to read
815 cmd[10] = blockNumber;//0x00;
816 //Now the CRC
817 crc = Crc(cmd, 11); // the crc needs to be calculated over 12 bytes
818 cmd[11] = crc & 0xff;
819 cmd[12] = crc >> 8;
820
821 CodeIso15693AsReader(cmd, sizeof(cmd));
822}
823
824// Now the VICC>VCD responses when we are simulating a tag
825 static void BuildInventoryResponse( uint8_t *uid)
826{
827 uint8_t cmd[12];
828
829 uint16_t crc;
830 // one sub-carrier, inventory, 1 slot, fast rate
831 // AFI is at bit 5 (1<<4) when doing an INVENTORY
832 //(1 << 2) | (1 << 5) | (1 << 1);
833 cmd[0] = 0; //
834 cmd[1] = 0; // DSFID (data storage format identifier). 0x00 = not supported
835 // 64-bit UID
836 cmd[2] = uid[7]; //0x32;
837 cmd[3] = uid[6]; //0x4b;
838 cmd[4] = uid[5]; //0x03;
839 cmd[5] = uid[4]; //0x01;
840 cmd[6] = uid[3]; //0x00;
841 cmd[7] = uid[2]; //0x10;
842 cmd[8] = uid[1]; //0x05;
843 cmd[9] = uid[0]; //0xe0;
844 //Now the CRC
845 crc = Crc(cmd, 10);
846 cmd[10] = crc & 0xff;
847 cmd[11] = crc >> 8;
848
849 CodeIso15693AsReader(cmd, sizeof(cmd));
850}
851
852// Universal Method for sending to and recv bytes from a tag
853// init ... should we initialize the reader?
854// speed ... 0 low speed, 1 hi speed
855// **recv will return you a pointer to the received data
856// If you do not need the answer use NULL for *recv[]
857// return: lenght of received data
858int SendDataTag(uint8_t *send, int sendlen, int init, int speed, uint8_t **recv) {
859
860 int samples = 0;
861 int tsamples = 0;
862 int wait = 0;
863 int elapsed = 0;
864
865 LED_A_ON();
866 LED_B_ON();
867 LED_C_OFF();
868 LED_D_OFF();
869
870 int answerLen=0;
871 uint8_t *answer = (((uint8_t *)BigBuf) + 3660);
872 if (recv!=NULL) memset(BigBuf + 3660, 0, 100);
873
874 if (init) Iso15693InitReader();
875
876 if (!speed) {
877 // low speed (1 out of 256)
878 CodeIso15693AsReader256(send, sendlen);
879 } else {
880 // high speed (1 out of 4)
881 CodeIso15693AsReader(send, sendlen);
882 }
883
884 LED_A_ON();
885 LED_B_OFF();
886
887 TransmitTo15693Tag(ToSend,ToSendMax,&tsamples, &wait);
888 // Now wait for a response
889 if (recv!=NULL) {
890 LED_A_OFF();
891 LED_B_ON();
892 answerLen = GetIso15693AnswerFromTag(answer, 100, &samples, &elapsed) ;
893 *recv=answer;
894 }
895
896 LED_A_OFF();
897 LED_B_OFF();
898 LED_C_OFF();
899 LED_D_OFF();
900
901 return answerLen;
902}
903
904
905// --------------------------------------------------------------------
906// Debug Functions
907// --------------------------------------------------------------------
908
909// Decodes a message from a tag and displays its metadata and content
910#define DBD15STATLEN 48
911void DbdecodeIso15693Answer(int len, uint8_t *d) {
912 char status[DBD15STATLEN+1]={0};
913 uint16_t crc;
914
915 if (len>3) {
916 if (d[0]&(1<<3))
917 strncat(status,"ProtExt ",DBD15STATLEN);
918 if (d[0]&1) {
919 // error
920 strncat(status,"Error ",DBD15STATLEN);
921 switch (d[1]) {
922 case 0x01:
923 strncat(status,"01:notSupp",DBD15STATLEN);
924 break;
925 case 0x02:
926 strncat(status,"02:notRecog",DBD15STATLEN);
927 break;
928 case 0x03:
929 strncat(status,"03:optNotSupp",DBD15STATLEN);
930 break;
931 case 0x0f:
932 strncat(status,"0f:noInfo",DBD15STATLEN);
933 break;
934 case 0x10:
935 strncat(status,"10:dontExist",DBD15STATLEN);
936 break;
937 case 0x11:
938 strncat(status,"11:lockAgain",DBD15STATLEN);
939 break;
940 case 0x12:
941 strncat(status,"12:locked",DBD15STATLEN);
942 break;
943 case 0x13:
944 strncat(status,"13:progErr",DBD15STATLEN);
945 break;
946 case 0x14:
947 strncat(status,"14:lockErr",DBD15STATLEN);
948 break;
949 default:
950 strncat(status,"unknownErr",DBD15STATLEN);
951 }
952 strncat(status," ",DBD15STATLEN);
953 } else {
954 strncat(status,"NoErr ",DBD15STATLEN);
955 }
956
957 crc=Crc(d,len-2);
958 if ( (( crc & 0xff ) == d[len-2]) && (( crc >> 8 ) == d[len-1]) )
959 strncat(status,"CrcOK",DBD15STATLEN);
960 else
961 strncat(status,"CrcFail!",DBD15STATLEN);
962
963 Dbprintf("%s",status);
964 }
965}
966
967
968
969///////////////////////////////////////////////////////////////////////
970// Functions called via USB/Client
971///////////////////////////////////////////////////////////////////////
972
973void SetDebugIso15693(uint32_t debug) {
974 DEBUG=debug;
975 Dbprintf("Iso15693 Debug is now %s",DEBUG?"on":"off");
976 return;
977}
978
979
980
981//-----------------------------------------------------------------------------
982// Simulate an ISO15693 reader, perform anti-collision and then attempt to read a sector
983// all demodulation performed in arm rather than host. - greg
984//-----------------------------------------------------------------------------
985void ReaderIso15693(uint32_t parameter )
986{
987 LED_A_ON();
988 LED_B_ON();
989 LED_C_OFF();
990 LED_D_OFF();
991
992 uint8_t *answer1 = (((uint8_t *)BigBuf) + 3660); //
993 uint8_t *answer2 = (((uint8_t *)BigBuf) + 3760);
994 uint8_t *answer3 = (((uint8_t *)BigBuf) + 3860);
995
996 int answerLen1 = 0;
997 int answerLen2 = 0;
998 int answerLen3 = 0;
999 int i = 0;
1000 int samples = 0;
1001 int tsamples = 0;
1002 int wait = 0;
1003 int elapsed = 0;
1004 uint8_t TagUID[8] = {0x00};
1005
1006
1007 // Blank arrays
1008 memset(BigBuf + 3660, 0x00, 300);
1009
1010 FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
1011
1012 SetAdcMuxFor(GPIO_MUXSEL_HIPKD);
1013 // Setup SSC
1014 FpgaSetupSsc();
1015
1016 // Start from off (no field generated)
1017 FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
1018 SpinDelay(200);
1019
1020 // Give the tags time to energize
1021 FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR);
1022 SpinDelay(200);
1023
1024 LED_A_ON();
1025 LED_B_OFF();
1026 LED_C_OFF();
1027 LED_D_OFF();
1028
1029 // FIRST WE RUN AN INVENTORY TO GET THE TAG UID
1030 // THIS MEANS WE CAN PRE-BUILD REQUESTS TO SAVE CPU TIME
1031
1032 // Now send the IDENTIFY command
1033 BuildIdentifyRequest();
1034
1035 TransmitTo15693Tag(ToSend,ToSendMax,&tsamples, &wait);
1036
1037 // Now wait for a response
1038 answerLen1 = GetIso15693AnswerFromTag(answer1, 100, &samples, &elapsed) ;
1039
1040 if (answerLen1 >=12) // we should do a better check than this
1041 {
1042 TagUID[0] = answer1[2];
1043 TagUID[1] = answer1[3];
1044 TagUID[2] = answer1[4];
1045 TagUID[3] = answer1[5];
1046 TagUID[4] = answer1[6];
1047 TagUID[5] = answer1[7];
1048 TagUID[6] = answer1[8]; // IC Manufacturer code
1049 TagUID[7] = answer1[9]; // always E0
1050
1051 }
1052
1053 Dbprintf("%d octets read from IDENTIFY request:", answerLen1);
1054 DbdecodeIso15693Answer(answerLen1,answer1);
1055 Dbhexdump(answerLen1,answer1,true);
1056
1057 // UID is reverse
1058 if (answerLen1 >= 12)
1059 Dbprintf("UID = %02hX%02hX%02hX%02hX%02hX%02hX%02hX%02hX",
1060 TagUID[7],TagUID[6],TagUID[5],TagUID[4],
1061 TagUID[3],TagUID[2],TagUID[1],TagUID[0]);
1062
1063
1064 Dbprintf("%d octets read from SELECT request:", answerLen2);
1065 DbdecodeIso15693Answer(answerLen2,answer2);
1066 Dbhexdump(answerLen2,answer2,true);
1067
1068 Dbprintf("%d octets read from XXX request:", answerLen3);
1069 DbdecodeIso15693Answer(answerLen3,answer3);
1070 Dbhexdump(answerLen3,answer3,true);
1071
1072 // read all pages
1073 if (answerLen1 >= 12 && DEBUG) {
1074 i=0;
1075 while (i < 32) { // sanity check, assume max 32 pages
1076 BuildReadBlockRequest(TagUID,i);
1077 TransmitTo15693Tag(ToSend,ToSendMax,&tsamples, &wait);
1078 answerLen2 = GetIso15693AnswerFromTag(answer2, 100, &samples, &elapsed);
1079 if (answerLen2 > 0) {
1080 Dbprintf("READ SINGLE BLOCK %d returned %d octets:",i,answerLen2);
1081 DbdecodeIso15693Answer(answerLen2,answer2);
1082 Dbhexdump(answerLen2,answer2,true);
1083 if ( *((uint32_t*) answer2) == 0x07160101 ) break; // exit on NoPageErr
1084 }
1085 i++;
1086 }
1087 }
1088
1089 LED_A_OFF();
1090 LED_B_OFF();
1091 LED_C_OFF();
1092 LED_D_OFF();
1093}
1094
1095// Simulate an ISO15693 TAG, perform anti-collision and then print any reader commands
1096// all demodulation performed in arm rather than host. - greg
1097void SimTagIso15693(uint32_t parameter, uint8_t *uid)
1098{
1099 LED_A_ON();
1100 LED_B_ON();
1101 LED_C_OFF();
1102 LED_D_OFF();
1103
1104 uint8_t *buf = (((uint8_t *)BigBuf) + 3660); //
1105
1106 int answerLen1 = 0;
1107 int samples = 0;
1108 int tsamples = 0;
1109 int wait = 0;
1110 int elapsed = 0;
1111
1112 memset(buf, 0x00, 100);
1113
1114 FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
1115
1116 SetAdcMuxFor(GPIO_MUXSEL_HIPKD);
1117
1118 FpgaSetupSsc();
1119
1120 // Start from off (no field generated)
1121 FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
1122 SpinDelay(200);
1123
1124 LED_A_OFF();
1125 LED_B_OFF();
1126 LED_C_ON();
1127 LED_D_OFF();
1128
1129 // Listen to reader
1130 answerLen1 = GetIso15693AnswerFromSniff(buf, 100, &samples, &elapsed) ;
1131
1132 if (answerLen1 >=1) // we should do a better check than this
1133 {
1134 // Build a suitable reponse to the reader INVENTORY cocmmand
1135 // not so obsvious, but in the call to BuildInventoryResponse, the command is copied to the global ToSend buffer used below.
1136
1137 BuildInventoryResponse(uid);
1138
1139 TransmitTo15693Reader(ToSend, ToSendMax, &tsamples, &wait);
1140 }
1141
1142 Dbprintf("%d octets read from reader command: %x %x %x %x %x %x %x %x %x", answerLen1,
1143 buf[0], buf[1], buf[2], buf[3],
1144 buf[4], buf[5], buf[6], buf[7], buf[8]);
1145
1146 Dbprintf("Simulationg uid: %x %x %x %x %x %x %x %x",
1147 uid[0], uid[1], uid[2], uid[3],
1148 uid[4], uid[5], uid[6], uid[7]);
1149
1150 LED_A_OFF();
1151 LED_B_OFF();
1152 LED_C_OFF();
1153 LED_D_OFF();
1154}
1155
1156
1157// Since there is no standardized way of reading the AFI out of a tag, we will brute force it
1158// (some manufactures offer a way to read the AFI, though)
1159void BruteforceIso15693Afi(uint32_t speed)
1160{
1161 uint8_t data[20];
1162 uint8_t *recv=data;
1163 int datalen=0, recvlen=0;
1164
1165 Iso15693InitReader();
1166
1167 // first without AFI
1168 // Tags should respond wihtout AFI and with AFI=0 even when AFI is active
1169
1170 data[0]=ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH |
1171 ISO15_REQ_INVENTORY | ISO15_REQINV_SLOT1;
1172 data[1]=ISO15_CMD_INVENTORY;
1173 data[2]=0; // mask length
1174 datalen=AddCrc(data,3);
1175 recvlen=SendDataTag(data,datalen,0,speed,&recv);
1176 WDT_HIT();
1177 if (recvlen>=12) {
1178 Dbprintf("NoAFI UID=%s",sprintUID(NULL,&recv[2]));
1179 }
1180
1181 // now with AFI
1182
1183 data[0]=ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH |
1184 ISO15_REQ_INVENTORY | ISO15_REQINV_AFI | ISO15_REQINV_SLOT1;
1185 data[1]=ISO15_CMD_INVENTORY;
1186 data[2]=0; // AFI
1187 data[3]=0; // mask length
1188
1189 for (int i=0;i<256;i++) {
1190 data[2]=i & 0xFF;
1191 datalen=AddCrc(data,4);
1192 recvlen=SendDataTag(data,datalen,0,speed,&recv);
1193 WDT_HIT();
1194 if (recvlen>=12) {
1195 Dbprintf("AFI=%i UID=%s",i,sprintUID(NULL,&recv[2]));
1196 }
1197 }
1198 Dbprintf("AFI Bruteforcing done.");
1199
1200}
1201
1202// Allows to directly send commands to the tag via the client
1203void DirectTag15693Command(uint32_t datalen,uint32_t speed, uint32_t recv, uint8_t data[]) {
1204
1205 int recvlen=0;
1206 uint8_t *recvbuf=(uint8_t *)BigBuf;
1207// UsbCommand n;
1208
1209 if (DEBUG) {
1210 Dbprintf("SEND");
1211 Dbhexdump(datalen,data,true);
1212 }
1213
1214 recvlen=SendDataTag(data,datalen,1,speed,(recv?&recvbuf:NULL));
1215
1216 if (recv) {
1217 LED_B_ON();
1218 cmd_send(CMD_ACK,recvlen>48?48:recvlen,0,0,recvbuf,48);
1219 LED_B_OFF();
1220
1221 if (DEBUG) {
1222 Dbprintf("RECV");
1223 DbdecodeIso15693Answer(recvlen,recvbuf);
1224 Dbhexdump(recvlen,recvbuf,true);
1225 }
1226 }
1227
1228}
1229
1230
1231
1232
1233// --------------------------------------------------------------------
1234// -- Misc & deprecated functions
1235// --------------------------------------------------------------------
1236
1237/*
1238
1239// do not use; has a fix UID
1240static void __attribute__((unused)) BuildSysInfoRequest(uint8_t *uid)
1241{
1242 uint8_t cmd[12];
1243
1244 uint16_t crc;
1245 // If we set the Option_Flag in this request, the VICC will respond with the secuirty status of the block
1246 // followed by teh block data
1247 // one sub-carrier, inventory, 1 slot, fast rate
1248 cmd[0] = (1 << 5) | (1 << 1); // no SELECT bit
1249 // System Information command code
1250 cmd[1] = 0x2B;
1251 // UID may be optionally specified here
1252 // 64-bit UID
1253 cmd[2] = 0x32;
1254 cmd[3]= 0x4b;
1255 cmd[4] = 0x03;
1256 cmd[5] = 0x01;
1257 cmd[6] = 0x00;
1258 cmd[7] = 0x10;
1259 cmd[8] = 0x05;
1260 cmd[9]= 0xe0; // always e0 (not exactly unique)
1261 //Now the CRC
1262 crc = Crc(cmd, 10); // the crc needs to be calculated over 2 bytes
1263 cmd[10] = crc & 0xff;
1264 cmd[11] = crc >> 8;
1265
1266 CodeIso15693AsReader(cmd, sizeof(cmd));
1267}
1268
1269
1270// do not use; has a fix UID
1271static void __attribute__((unused)) BuildReadMultiBlockRequest(uint8_t *uid)
1272{
1273 uint8_t cmd[14];
1274
1275 uint16_t crc;
1276 // If we set the Option_Flag in this request, the VICC will respond with the secuirty status of the block
1277 // followed by teh block data
1278 // one sub-carrier, inventory, 1 slot, fast rate
1279 cmd[0] = (1 << 5) | (1 << 1); // no SELECT bit
1280 // READ Multi BLOCK command code
1281 cmd[1] = 0x23;
1282 // UID may be optionally specified here
1283 // 64-bit UID
1284 cmd[2] = 0x32;
1285 cmd[3]= 0x4b;
1286 cmd[4] = 0x03;
1287 cmd[5] = 0x01;
1288 cmd[6] = 0x00;
1289 cmd[7] = 0x10;
1290 cmd[8] = 0x05;
1291 cmd[9]= 0xe0; // always e0 (not exactly unique)
1292 // First Block number to read
1293 cmd[10] = 0x00;
1294 // Number of Blocks to read
1295 cmd[11] = 0x2f; // read quite a few
1296 //Now the CRC
1297 crc = Crc(cmd, 12); // the crc needs to be calculated over 2 bytes
1298 cmd[12] = crc & 0xff;
1299 cmd[13] = crc >> 8;
1300
1301 CodeIso15693AsReader(cmd, sizeof(cmd));
1302}
1303
1304// do not use; has a fix UID
1305static void __attribute__((unused)) BuildArbitraryRequest(uint8_t *uid,uint8_t CmdCode)
1306{
1307 uint8_t cmd[14];
1308
1309 uint16_t crc;
1310 // If we set the Option_Flag in this request, the VICC will respond with the secuirty status of the block
1311 // followed by teh block data
1312 // one sub-carrier, inventory, 1 slot, fast rate
1313 cmd[0] = (1 << 5) | (1 << 1); // no SELECT bit
1314 // READ BLOCK command code
1315 cmd[1] = CmdCode;
1316 // UID may be optionally specified here
1317 // 64-bit UID
1318 cmd[2] = 0x32;
1319 cmd[3]= 0x4b;
1320 cmd[4] = 0x03;
1321 cmd[5] = 0x01;
1322 cmd[6] = 0x00;
1323 cmd[7] = 0x10;
1324 cmd[8] = 0x05;
1325 cmd[9]= 0xe0; // always e0 (not exactly unique)
1326 // Parameter
1327 cmd[10] = 0x00;
1328 cmd[11] = 0x0a;
1329
1330// cmd[12] = 0x00;
1331// cmd[13] = 0x00; //Now the CRC
1332 crc = Crc(cmd, 12); // the crc needs to be calculated over 2 bytes
1333 cmd[12] = crc & 0xff;
1334 cmd[13] = crc >> 8;
1335
1336 CodeIso15693AsReader(cmd, sizeof(cmd));
1337}
1338
1339// do not use; has a fix UID
1340static void __attribute__((unused)) BuildArbitraryCustomRequest(uint8_t uid[], uint8_t CmdCode)
1341{
1342 uint8_t cmd[14];
1343
1344 uint16_t crc;
1345 // If we set the Option_Flag in this request, the VICC will respond with the secuirty status of the block
1346 // followed by teh block data
1347 // one sub-carrier, inventory, 1 slot, fast rate
1348 cmd[0] = (1 << 5) | (1 << 1); // no SELECT bit
1349 // READ BLOCK command code
1350 cmd[1] = CmdCode;
1351 // UID may be optionally specified here
1352 // 64-bit UID
1353 cmd[2] = 0x32;
1354 cmd[3]= 0x4b;
1355 cmd[4] = 0x03;
1356 cmd[5] = 0x01;
1357 cmd[6] = 0x00;
1358 cmd[7] = 0x10;
1359 cmd[8] = 0x05;
1360 cmd[9]= 0xe0; // always e0 (not exactly unique)
1361 // Parameter
1362 cmd[10] = 0x05; // for custom codes this must be manufcturer code
1363 cmd[11] = 0x00;
1364
1365// cmd[12] = 0x00;
1366// cmd[13] = 0x00; //Now the CRC
1367 crc = Crc(cmd, 12); // the crc needs to be calculated over 2 bytes
1368 cmd[12] = crc & 0xff;
1369 cmd[13] = crc >> 8;
1370
1371 CodeIso15693AsReader(cmd, sizeof(cmd));
1372}
1373
1374
1375
1376
1377*/
1378
1379
Impressum, Datenschutz