fix one too small
[RRG-proxmark3.git] / client / src / cmdlfti.c
blob4a5dcc18ae81fd7750b5030a444d371defc974b6
1 //-----------------------------------------------------------------------------
2 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
3 //
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.
8 //
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 //-----------------------------------------------------------------------------
19 #include "cmdlfti.h"
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h> // strncpy
23 #include <inttypes.h>
24 #include "cmdparser.h" // command_t
25 #include "commonutil.h"
26 #include "comms.h"
27 #include "crc16.h"
28 #include "ui.h"
29 #include "proxgui.h"
30 #include "graph.h"
31 #include "cliparser.h"
33 static int CmdHelp(const char *Cmd);
35 int demodTI(bool verbose) {
36 (void) verbose; // unused so far
37 /* MATLAB as follows:
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)));
52 // 2M*16/134.2k = 238
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
70 // 2M*16/123.2k = 260
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;
98 int i, j, TagType;
99 int lowSum = 0, highSum = 0;
100 int lowTot = 0, highTot = 0;
101 int retval = PM3_ESOFT;
103 if (g_GraphTraceLen < convLen) {
104 return retval;
106 for (i = 0; i < g_GraphTraceLen - convLen; i++) {
107 lowSum = 0;
108 highSum = 0;
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++) {
125 lowTot = 0;
126 highTot = 0;
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++) {
156 int dec = 0;
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];
165 if (dec > max) {
166 max = dec;
167 maxPos = i;
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];
202 if (high > low) {
203 bits[i] = '1';
204 maxPos += highLen;
205 // bitstream arrives lsb first so shift right
206 shift3 |= (1u << 31);
207 } else {
208 bits[i] = '.';
209 maxPos += lowLen;
212 // 128 bit right shift register
213 shift0 = (shift0 >> 1) | (shift1 << 31);
214 shift1 = (shift1 >> 1) | (shift2 << 31);
215 shift2 = (shift2 >> 1) | (shift3 << 31);
216 shift3 >>= 1;
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!");
229 goto out;
230 } else if (TagType == 0x7E) {
231 PrintAndLogEx(INFO, "Readonly TI tag detected.");
232 retval = PM3_SUCCESS;
233 goto out;
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
245 shift3 >>= 16;
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
254 // calculate CRC
255 uint8_t raw[8] = {
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;
274 goto out;
275 } else {
276 PrintAndLogEx(WARNING, "Unknown tag type.");
279 out:
280 if (retval != PM3_SUCCESS) {
281 restore_bufferS32(saveState, g_GraphBuffer);
282 g_GridOffset = saveState.offset;
285 return retval;
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",
292 "lf ti demod"
295 void *argtable[] = {
296 arg_param_begin,
297 arg_param_end
299 CLIExecWithReturn(ctx, Cmd, argtable, true);
300 CLIParserFree(ctx);
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",
308 "read a TI tag",
309 "lf ti reader -@ -> continuous reader mode"
312 void *argtable[] = {
313 arg_param_begin,
314 arg_lit0("@", NULL, "optional - continuous reader mode"),
315 arg_param_end
317 CLIExecWithReturn(ctx, Cmd, argtable, true);
318 bool cm = arg_get_lit(ctx, 1);
319 CLIParserFree(ctx);
321 if (cm) {
322 PrintAndLogEx(INFO, "Press " _GREEN_("<Enter>") " to exit");
325 do {
326 clearCommandBuffer();
327 SendCommandNG(CMD_LF_TI_READ, NULL, 0);
328 } while (cm && !kbd_enter_pressed());
330 return PM3_SUCCESS;
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"
343 void *argtable[] = {
344 arg_param_begin,
345 arg_str1("r", "raw", "<hex>", "raw hex data. 8 bytes max"),
346 arg_str0(NULL, "crc", "<hex>", "optional - crc"),
347 arg_param_end
349 CLIExecWithReturn(ctx, Cmd, argtable, false);
351 int raw_len = 0;
352 uint8_t raw[8] = {0};
353 CLIGetHexWithReturn(ctx, 1, raw, &raw_len);
355 int crc_len = 0;
356 uint8_t crc[2] = {0};
357 CLIGetHexWithReturn(ctx, 2, crc, &crc_len);
358 CLIParserFree(ctx);
360 struct {
361 uint32_t high;
362 uint32_t low;
363 uint16_t crc;
364 } PACKED payload;
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");
374 return PM3_SUCCESS;
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);
388 return PM3_SUCCESS;
391 int CmdLFTI(const char *Cmd) {
392 clearCommandBuffer();
393 return CmdsParse(CommandTable, Cmd);