1 //-----------------------------------------------------------------------------
2 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // See LICENSE.txt for the text of the license.
15 //-----------------------------------------------------------------------------
16 // Low frequency TI commands
17 //-----------------------------------------------------------------------------
22 #include <string.h> // strncpy
24 #include "cmdparser.h" // command_t
25 #include "commonutil.h"
31 #include "cliparser.h"
33 static int CmdHelp(const char *Cmd
);
35 int demodTI(bool verbose
) {
36 (void) verbose
; // unused so far
38 f_s = 2000000; % sampling frequency
39 f_l = 123200; % low FSK tone
40 f_h = 134200; % high FSK tone
42 T_l = 119e-6; % low bit duration
43 T_h = 130e-6; % high bit duration
45 l = 2*pi*ones(1, floor(f_s*T_l))*(f_l/f_s);
46 h = 2*pi*ones(1, floor(f_s*T_h))*(f_h/f_s);
48 l = sign(sin(cumsum(l)));
49 h = sign(sin(cumsum(h)));
53 static const int LowTone
[] = {
54 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
55 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
56 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
57 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
58 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
59 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
60 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
61 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
62 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
63 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
64 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
65 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
66 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
67 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
68 1, 1, 1, 1, 1, 1, 1, 1, -1, -1
71 static const int HighTone
[] = {
72 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1,
73 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1,
74 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
75 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
76 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
77 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1,
78 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1,
79 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1,
80 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
81 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
82 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1,
83 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1,
84 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1,
85 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1,
86 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
87 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
88 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1,
89 1, 1, 1, 1, 1, 1, 1, 1
92 buffer_savestate_t saveState
= save_bufferS32(g_GraphBuffer
, g_GraphTraceLen
);
93 saveState
.offset
= g_GridOffset
;
95 int lowLen
= ARRAYLEN(LowTone
);
96 int highLen
= ARRAYLEN(HighTone
);
97 int convLen
= (highLen
> lowLen
) ? highLen
: lowLen
;
99 int lowSum
= 0, highSum
= 0;
100 int lowTot
= 0, highTot
= 0;
101 int retval
= PM3_ESOFT
;
103 if (g_GraphTraceLen
< convLen
) {
106 for (i
= 0; i
< g_GraphTraceLen
- convLen
; i
++) {
110 for (j
= 0; j
< lowLen
; j
++) {
111 lowSum
+= LowTone
[j
] * g_GraphBuffer
[i
+ j
];
113 for (j
= 0; j
< highLen
; j
++) {
114 highSum
+= HighTone
[j
] * g_GraphBuffer
[i
+ j
];
116 lowSum
= abs((100 * lowSum
) / lowLen
);
117 highSum
= abs((100 * highSum
) / highLen
);
118 lowSum
= (lowSum
< 0) ? -lowSum
: lowSum
;
119 highSum
= (highSum
< 0) ? -highSum
: highSum
;
121 g_GraphBuffer
[i
] = (highSum
<< 16) | lowSum
;
124 for (i
= 0; i
< g_GraphTraceLen
- convLen
- 16; i
++) {
127 // 16 and 15 are f_s divided by f_l and f_h, rounded
128 for (j
= 0; j
< 16; j
++) {
129 lowTot
+= (g_GraphBuffer
[i
+ j
] & 0xffff);
131 for (j
= 0; j
< 15; j
++) {
132 highTot
+= (g_GraphBuffer
[i
+ j
] >> 16);
134 g_GraphBuffer
[i
] = lowTot
- highTot
;
137 g_GraphTraceLen
-= (convLen
+ 16);
139 RepaintGraphWindow();
141 // TI tag data format is 16 prebits, 8 start bits, 64 data bits,
142 // 16 crc CCITT bits, 8 stop bits, 15 end bits
144 // the 16 prebits are always low
145 // the 8 start and stop bits of a tag must match
146 // the start/stop prebits of a ro tag are 01111110
147 // the start/stop prebits of a rw tag are 11111110
148 // the 15 end bits of a ro tag are all low
149 // the 15 end bits of a rw tag match bits 15-1 of the data bits
151 // Okay, so now we have unsliced soft decisions;
152 // find bit-sync, and then get some bits.
153 // look for 17 low bits followed by 6 highs (common pattern for ro and rw tags)
154 int max
= 0, maxPos
= 0;
155 for (i
= 0; i
< 6000; i
++) {
157 // searching 17 consecutive lows
158 for (j
= 0; j
< 17 * lowLen
; j
++) {
159 dec
-= g_GraphBuffer
[i
+ j
];
161 // searching 7 consecutive highs
162 for (; j
< 17 * lowLen
+ 6 * highLen
; j
++) {
163 dec
+= g_GraphBuffer
[i
+ j
];
171 // place a marker in the buffer to visually aid location
172 // of the start of sync
173 g_MarkerC
.pos
= maxPos
;
174 strcpy(g_MarkerC
.label
, "Sync Start");
176 // advance pointer to start of actual data stream (after 16 pre and 8 start bits)
177 maxPos
+= 17 * lowLen
;
178 maxPos
+= 6 * highLen
;
180 // place a marker in the buffer to visually aid location
181 // of the end of sync
182 g_MarkerD
.pos
= maxPos
;
183 strcpy(g_MarkerD
.label
, "Sync End");
185 PrintAndLogEx(DEBUG
, "actual data bits start at sample %d", maxPos
);
186 PrintAndLogEx(DEBUG
, "length %d/%d", highLen
, lowLen
);
188 uint8_t bits
[1 + 64 + 16 + 8 + 16];
189 bits
[sizeof(bits
) - 1] = '\0';
191 uint32_t shift3
= 0x7e000000, shift2
= 0, shift1
= 0, shift0
= 0;
193 for (i
= 0; i
< ARRAYLEN(bits
) - 1; i
++) {
194 int high
= 0, low
= 0;
195 for (j
= 0; j
< lowLen
; j
++) {
196 low
-= g_GraphBuffer
[maxPos
+ j
];
198 for (j
= 0; j
< highLen
; j
++) {
199 high
+= g_GraphBuffer
[maxPos
+ j
];
205 // bitstream arrives lsb first so shift right
206 shift3
|= (1u << 31);
212 // 128 bit right shift register
213 shift0
= (shift0
>> 1) | (shift1
<< 31);
214 shift1
= (shift1
>> 1) | (shift2
<< 31);
215 shift2
= (shift2
>> 1) | (shift3
<< 31);
218 // place a marker in the buffer between bits to visually aid location
219 add_temporary_marker(maxPos
, "");
222 RepaintGraphWindow();
224 PrintAndLogEx(DEBUG
, "TI tag : raw tag bits | %s", bits
);
226 TagType
= (shift3
>> 8) & 0xFF;
227 if (TagType
!= ((shift0
>> 16) & 0xFF)) {
228 PrintAndLogEx(DEBUG
, "TI tag : Error: start and stop bits do not match!");
230 } else if (TagType
== 0x7E) {
231 PrintAndLogEx(INFO
, "Readonly TI tag detected.");
232 retval
= PM3_SUCCESS
;
234 } else if (TagType
== 0xFE) {
235 PrintAndLogEx(INFO
, "Rewriteable TI tag detected.");
237 // put 64 bit data into shift1 and shift0
238 shift0
= (shift0
>> 24) | (shift1
<< 8);
239 shift1
= (shift1
>> 24) | (shift2
<< 8);
241 // align 16 bit crc into lower half of shift2
242 shift2
= ((shift2
>> 24) | (shift3
<< 8)) & 0x0FFFF;
244 // align 16 bit "end bits" or "ident" into lower half of shift3
247 // only 15 bits compare, last bit of ident is not valid
248 if ((shift3
^ shift0
) & 0x7FFF) {
249 PrintAndLogEx(WARNING
, "Warning: Ident mismatch!");
251 // WARNING the order of the bytes in which we calc crc below needs checking
252 // i'm 99% sure the crc algorithm is correct, but it may need to eat the
253 // bytes in reverse or something
256 (shift0
>> 0) & 0xFF,
257 (shift0
>> 8) & 0xFF,
258 (shift0
>> 16) & 0xFF,
259 (shift0
>> 24) & 0xFF,
260 (shift1
>> 0) & 0xFF,
261 (shift1
>> 8) & 0xFF,
262 (shift1
>> 16) & 0xFF,
263 (shift1
>> 24) & 0xFF
265 init_table(CRC_KERMIT
);
266 uint16_t calccrc
= crc16_kermit(raw
, sizeof(raw
));
267 const char *crc_str
= (calccrc
== (shift2
& 0xFFFF)) ? _GREEN_("ok") : _RED_("fail");
268 PrintAndLogEx(INFO
, "Tag data = %08X%08X [%04X] ( %s )", shift1
, shift0
, calccrc
, crc_str
);
270 if (calccrc
!= (shift2
& 0xFFFF))
271 PrintAndLogEx(WARNING
, "Warning: CRC mismatch, calculated %04X, got %04X", calccrc
, shift2
& 0xFFFF);
273 retval
= PM3_SUCCESS
;
276 PrintAndLogEx(WARNING
, "Unknown tag type.");
280 if (retval
!= PM3_SUCCESS
) {
281 restore_bufferS32(saveState
, g_GraphBuffer
);
282 g_GridOffset
= saveState
.offset
;
288 static int CmdTIDemod(const char *Cmd
) {
289 CLIParserContext
*ctx
;
290 CLIParserInit(&ctx
, "lf ti demod",
291 "Try to find TI preamble, if found decode / descramble data",
299 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
301 return demodTI(true);
304 // read a TI tag and return its ID
305 static int CmdTIReader(const char *Cmd
) {
306 CLIParserContext
*ctx
;
307 CLIParserInit(&ctx
, "lf ti reader",
309 "lf ti reader -@ -> continuous reader mode"
314 arg_lit0("@", NULL
, "optional - continuous reader mode"),
317 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
318 bool cm
= arg_get_lit(ctx
, 1);
322 PrintAndLogEx(INFO
, "Press " _GREEN_("<Enter>") " to exit");
326 clearCommandBuffer();
327 SendCommandNG(CMD_LF_TI_READ
, NULL
, 0);
328 } while (cm
&& !kbd_enter_pressed());
333 // write new data to a r/w TI tag
334 static int CmdTIWrite(const char *Cmd
) {
336 CLIParserContext
*ctx
;
337 CLIParserInit(&ctx
, "lf ti write",
338 "write to a r/w TI tag.",
339 "lf ti write --raw 1122334455667788\n"
340 "lf ti write --raw 1122334455667788 --crc 1122\n"
345 arg_str1("r", "raw", "<hex>", "raw hex data. 8 bytes max"),
346 arg_str0(NULL
, "crc", "<hex>", "optional - crc"),
349 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
352 uint8_t raw
[8] = {0};
353 CLIGetHexWithReturn(ctx
, 1, raw
, &raw_len
);
356 uint8_t crc
[2] = {0};
357 CLIGetHexWithReturn(ctx
, 2, crc
, &crc_len
);
366 payload
.high
= bytes_to_num(raw
, 4);
367 payload
.low
= bytes_to_num(raw
+ 4, 4);
368 payload
.crc
= bytes_to_num(crc
, crc_len
);
370 clearCommandBuffer();
371 SendCommandNG(CMD_LF_TI_WRITE
, (uint8_t *)&payload
, sizeof(payload
));
372 PrintAndLogEx(SUCCESS
, "Done");
373 PrintAndLogEx(HINT
, "Hint: try " _YELLOW_("`lf ti reader`") " to verify");
377 static command_t CommandTable
[] = {
378 {"help", CmdHelp
, AlwaysAvailable
, "This help"},
379 {"demod", CmdTIDemod
, AlwaysAvailable
, "Demodulate raw bits for TI LF tag from the GraphBuffer"},
380 {"reader", CmdTIReader
, IfPm3Lf
, "Read and decode a TI 134 kHz tag"},
381 {"write", CmdTIWrite
, IfPm3Lf
, "Write new data to a r/w TI 134 kHz tag"},
382 {NULL
, NULL
, NULL
, NULL
}
385 static int CmdHelp(const char *Cmd
) {
386 (void)Cmd
; // Cmd is not used so far
387 CmdsHelp(CommandTable
);
391 int CmdLFTI(const char *Cmd
) {
392 clearCommandBuffer();
393 return CmdsParse(CommandTable
, Cmd
);