textual
[RRG-proxmark3.git] / client / src / cmdlfti.c
blob3e813833218ea159befbfb723514cf561ff59d28
1 //-----------------------------------------------------------------------------
2 // Copyright (C) 2010 iZsh <izsh at fail0verflow.com>
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 // Low frequency TI commands
9 //-----------------------------------------------------------------------------
11 #include "cmdlfti.h"
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <inttypes.h>
15 #include "cmdparser.h" // command_t
16 #include "commonutil.h"
17 #include "comms.h"
18 #include "crc16.h"
19 #include "ui.h"
20 #include "proxgui.h"
21 #include "graph.h"
22 #include "cliparser.h"
24 static int CmdHelp(const char *Cmd);
26 int demodTI(bool verbose) {
27 (void) verbose; // unused so far
28 /* MATLAB as follows:
29 f_s = 2000000; % sampling frequency
30 f_l = 123200; % low FSK tone
31 f_h = 134200; % high FSK tone
33 T_l = 119e-6; % low bit duration
34 T_h = 130e-6; % high bit duration
36 l = 2*pi*ones(1, floor(f_s*T_l))*(f_l/f_s);
37 h = 2*pi*ones(1, floor(f_s*T_h))*(f_h/f_s);
39 l = sign(sin(cumsum(l)));
40 h = sign(sin(cumsum(h)));
43 // 2M*16/134.2k = 238
44 static const int LowTone[] = {
45 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
46 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
47 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
48 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
49 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
50 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
51 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
52 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
53 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
54 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, -1,
58 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
61 // 2M*16/123.2k = 260
62 static const int HighTone[] = {
63 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,
65 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,
67 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, -1, -1, -1, -1,
69 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1,
70 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1,
71 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
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,
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, -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,
80 1, 1, 1, 1, 1, 1, 1, 1
83 save_restoreGB(GRAPH_SAVE);
85 int lowLen = ARRAYLEN(LowTone);
86 int highLen = ARRAYLEN(HighTone);
87 int convLen = (highLen > lowLen) ? highLen : lowLen;
88 int i, j, TagType;
89 int lowSum = 0, highSum = 0;
90 int lowTot = 0, highTot = 0;
91 int retval = PM3_ESOFT;
93 for (i = 0; i < GraphTraceLen - convLen; i++) {
94 lowSum = 0;
95 highSum = 0;
97 for (j = 0; j < lowLen; j++) {
98 lowSum += LowTone[j] * GraphBuffer[i + j];
100 for (j = 0; j < highLen; j++) {
101 highSum += HighTone[j] * GraphBuffer[i + j];
103 lowSum = abs((100 * lowSum) / lowLen);
104 highSum = abs((100 * highSum) / highLen);
105 lowSum = (lowSum < 0) ? -lowSum : lowSum;
106 highSum = (highSum < 0) ? -highSum : highSum;
108 GraphBuffer[i] = (highSum << 16) | lowSum;
111 for (i = 0; i < GraphTraceLen - convLen - 16; i++) {
112 lowTot = 0;
113 highTot = 0;
114 // 16 and 15 are f_s divided by f_l and f_h, rounded
115 for (j = 0; j < 16; j++) {
116 lowTot += (GraphBuffer[i + j] & 0xffff);
118 for (j = 0; j < 15; j++) {
119 highTot += (GraphBuffer[i + j] >> 16);
121 GraphBuffer[i] = lowTot - highTot;
124 GraphTraceLen -= (convLen + 16);
126 RepaintGraphWindow();
128 // TI tag data format is 16 prebits, 8 start bits, 64 data bits,
129 // 16 crc CCITT bits, 8 stop bits, 15 end bits
131 // the 16 prebits are always low
132 // the 8 start and stop bits of a tag must match
133 // the start/stop prebits of a ro tag are 01111110
134 // the start/stop prebits of a rw tag are 11111110
135 // the 15 end bits of a ro tag are all low
136 // the 15 end bits of a rw tag match bits 15-1 of the data bits
138 // Okay, so now we have unsliced soft decisions;
139 // find bit-sync, and then get some bits.
140 // look for 17 low bits followed by 6 highs (common pattern for ro and rw tags)
141 int max = 0, maxPos = 0;
142 for (i = 0; i < 6000; i++) {
143 int dec = 0;
144 // searching 17 consecutive lows
145 for (j = 0; j < 17 * lowLen; j++) {
146 dec -= GraphBuffer[i + j];
148 // searching 7 consecutive highs
149 for (; j < 17 * lowLen + 6 * highLen; j++) {
150 dec += GraphBuffer[i + j];
152 if (dec > max) {
153 max = dec;
154 maxPos = i;
158 // place a marker in the buffer to visually aid location
159 // of the start of sync
160 GraphBuffer[maxPos] = 800;
161 GraphBuffer[maxPos + 1] = -800;
163 // advance pointer to start of actual data stream (after 16 pre and 8 start bits)
164 maxPos += 17 * lowLen;
165 maxPos += 6 * highLen;
167 // place a marker in the buffer to visually aid location
168 // of the end of sync
169 GraphBuffer[maxPos] = 800;
170 GraphBuffer[maxPos + 1] = -800;
172 PrintAndLogEx(DEBUG, "actual data bits start at sample %d", maxPos);
173 PrintAndLogEx(DEBUG, "length %d/%d", highLen, lowLen);
175 uint8_t bits[1 + 64 + 16 + 8 + 16];
176 bits[sizeof(bits) - 1] = '\0';
178 uint32_t shift3 = 0x7e000000, shift2 = 0, shift1 = 0, shift0 = 0;
180 for (i = 0; i < ARRAYLEN(bits) - 1; i++) {
181 int high = 0, low = 0;
182 for (j = 0; j < lowLen; j++) {
183 low -= GraphBuffer[maxPos + j];
185 for (j = 0; j < highLen; j++) {
186 high += GraphBuffer[maxPos + j];
189 if (high > low) {
190 bits[i] = '1';
191 maxPos += highLen;
192 // bitstream arrives lsb first so shift right
193 shift3 |= (1u << 31);
194 } else {
195 bits[i] = '.';
196 maxPos += lowLen;
199 // 128 bit right shift register
200 shift0 = (shift0 >> 1) | (shift1 << 31);
201 shift1 = (shift1 >> 1) | (shift2 << 31);
202 shift2 = (shift2 >> 1) | (shift3 << 31);
203 shift3 >>= 1;
205 // place a marker in the buffer between bits to visually aid location
206 GraphBuffer[maxPos] = 800;
207 GraphBuffer[maxPos + 1] = -800;
210 RepaintGraphWindow();
212 PrintAndLogEx(DEBUG, "TI tag : raw tag bits | %s", bits);
214 TagType = (shift3 >> 8) & 0xFF;
215 if (TagType != ((shift0 >> 16) & 0xFF)) {
216 PrintAndLogEx(DEBUG, "TI tag : Error: start and stop bits do not match!");
217 goto out;
218 } else if (TagType == 0x7E) {
219 PrintAndLogEx(INFO, "Readonly TI tag detected.");
220 retval = PM3_SUCCESS;
221 goto out;
222 } else if (TagType == 0xFE) {
223 PrintAndLogEx(INFO, "Rewriteable TI tag detected.");
225 // put 64 bit data into shift1 and shift0
226 shift0 = (shift0 >> 24) | (shift1 << 8);
227 shift1 = (shift1 >> 24) | (shift2 << 8);
229 // align 16 bit crc into lower half of shift2
230 shift2 = ((shift2 >> 24) | (shift3 << 8)) & 0x0FFFF;
232 // align 16 bit "end bits" or "ident" into lower half of shift3
233 shift3 >>= 16;
235 // only 15 bits compare, last bit of ident is not valid
236 if ((shift3 ^ shift0) & 0x7FFF) {
237 PrintAndLogEx(WARNING, "Warning: Ident mismatch!");
239 // WARNING the order of the bytes in which we calc crc below needs checking
240 // i'm 99% sure the crc algorithm is correct, but it may need to eat the
241 // bytes in reverse or something
242 // calculate CRC
243 uint8_t raw[8] = {
244 (shift0 >> 0) & 0xFF,
245 (shift0 >> 8) & 0xFF,
246 (shift0 >> 16) & 0xFF,
247 (shift0 >> 24) & 0xFF,
248 (shift1 >> 0) & 0xFF,
249 (shift1 >> 8) & 0xFF,
250 (shift1 >> 16) & 0xFF,
251 (shift1 >> 24) & 0xFF
253 init_table(CRC_KERMIT);
254 uint16_t calccrc = crc16_kermit(raw, sizeof(raw));
255 const char *crc_str = (calccrc == (shift2 & 0xFFFF)) ? _GREEN_("ok") : _RED_("fail");
256 PrintAndLogEx(INFO, "Tag data = %08X%08X [%04X] (%s)", shift1, shift0, calccrc, crc_str);
258 if (calccrc != (shift2 & 0xFFFF))
259 PrintAndLogEx(WARNING, "Warning: CRC mismatch, calculated %04X, got %04X", calccrc, shift2 & 0xFFFF);
261 retval = PM3_SUCCESS;
262 goto out;
263 } else {
264 PrintAndLogEx(WARNING, "Unknown tag type.");
267 out:
268 if (retval != PM3_SUCCESS)
269 save_restoreGB(GRAPH_RESTORE);
271 return retval;
274 static int CmdTIDemod(const char *Cmd) {
275 CLIParserContext *ctx;
276 CLIParserInit(&ctx, "lf ti demod",
277 "Try to find TI preamble, if found decode / descramble data",
278 "lf ti demod"
281 void *argtable[] = {
282 arg_param_begin,
283 arg_param_end
285 CLIExecWithReturn(ctx, Cmd, argtable, true);
286 CLIParserFree(ctx);
287 return demodTI(true);
290 // read a TI tag and return its ID
291 static int CmdTIReader(const char *Cmd) {
292 CLIParserContext *ctx;
293 CLIParserInit(&ctx, "lf ti reader",
294 "read a TI tag",
295 "lf ti reader -@ -> continuous reader mode"
298 void *argtable[] = {
299 arg_param_begin,
300 arg_lit0("@", NULL, "optional - continuous reader mode"),
301 arg_param_end
303 CLIExecWithReturn(ctx, Cmd, argtable, true);
304 bool cm = arg_get_lit(ctx, 1);
305 CLIParserFree(ctx);
307 if (cm) {
308 PrintAndLogEx(INFO, "Press " _GREEN_("<Enter>") " to exit");
311 do {
312 clearCommandBuffer();
313 SendCommandNG(CMD_LF_TI_READ, NULL, 0);
314 } while (cm && !kbd_enter_pressed());
316 return PM3_SUCCESS;
319 // write new data to a r/w TI tag
320 static int CmdTIWrite(const char *Cmd) {
322 CLIParserContext *ctx;
323 CLIParserInit(&ctx, "lf ti write",
324 "write to a r/w TI tag.",
325 "lf ti write --raw 1122334455667788\n"
326 "lf ti write --raw 1122334455667788 --crc 1122\n"
329 void *argtable[] = {
330 arg_param_begin,
331 arg_str1("r", "raw", "<hex>", "raw hex data. 8 bytes max"),
332 arg_str0(NULL, "crc", "<hex>", "optional - crc"),
333 arg_param_end
335 CLIExecWithReturn(ctx, Cmd, argtable, false);
337 int raw_len = 0;
338 uint8_t raw[8] = {0};
339 CLIGetHexWithReturn(ctx, 1, raw, &raw_len);
341 int crc_len = 0;
342 uint8_t crc[2] = {0};
343 CLIGetHexWithReturn(ctx, 2, crc, &crc_len);
344 CLIParserFree(ctx);
346 struct {
347 uint32_t high;
348 uint32_t low;
349 uint16_t crc;
350 } PACKED payload;
352 payload.high = bytes_to_num(raw, 4);
353 payload.low = bytes_to_num(raw + 4, 4);
354 payload.crc = bytes_to_num(crc, crc_len);
356 clearCommandBuffer();
357 SendCommandNG(CMD_LF_TI_WRITE, (uint8_t *)&payload, sizeof(payload));
358 PrintAndLogEx(SUCCESS, "Done");
359 PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf ti reader`") " to verify");
360 return PM3_SUCCESS;
363 static command_t CommandTable[] = {
364 {"help", CmdHelp, AlwaysAvailable, "This help"},
365 {"demod", CmdTIDemod, AlwaysAvailable, "Demodulate raw bits for TI LF tag from the GraphBuffer"},
366 {"reader", CmdTIReader, IfPm3Lf, "Read and decode a TI 134 kHz tag"},
367 {"write", CmdTIWrite, IfPm3Lf, "Write new data to a r/w TI 134 kHz tag"},
368 {NULL, NULL, NULL, NULL}
371 static int CmdHelp(const char *Cmd) {
372 (void)Cmd; // Cmd is not used so far
373 CmdsHelp(CommandTable);
374 return PM3_SUCCESS;
377 int CmdLFTI(const char *Cmd) {
378 clearCommandBuffer();
379 return CmdsParse(CommandTable, Cmd);