textual
[RRG-proxmark3.git] / client / src / cmdlfviking.c
blob2bb3897ece76d361b4f2b4322d113dd160ee55e7
1 //-----------------------------------------------------------------------------
2 // Marshmellow,
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 Viking tag commands (AKA FDI Matalec Transit)
9 // ASK/Manchester, RF/32, 64 bits (complete)
10 //-----------------------------------------------------------------------------
11 #include "cmdlfviking.h"
12 #include <string.h>
13 #include <stdlib.h>
14 #include <ctype.h>
15 #include "common.h"
16 #include "cmdparser.h" // command_t
17 #include "comms.h"
18 #include "ui.h"
19 #include "cmddata.h"
20 #include "cmdlf.h"
21 #include "lfdemod.h"
22 #include "commonutil.h" // num_to_bytes
23 #include "cliparser.h"
25 static int CmdHelp(const char *Cmd);
27 //see ASKDemod for what args are accepted
28 int demodViking(bool verbose) {
29 (void) verbose; // unused so far
31 bool st = false;
32 if (ASKDemod_ext(0, 0, 100, 0, false, false, false, 1, &st) != PM3_SUCCESS) {
33 PrintAndLogEx(DEBUG, "DEBUG: Error - Viking ASKDemod failed");
34 return PM3_ESOFT;
37 size_t size = DemodBufferLen;
38 int ans = detectViking(DemodBuffer, &size);
39 if (ans < 0) {
40 PrintAndLogEx(DEBUG, "DEBUG: Error - Viking Demod %d %s", ans, (ans == -5) ? _RED_("[chksum error]") : "");
41 return PM3_ESOFT;
44 //got a good demod
45 uint32_t raw1 = bytebits_to_byte(DemodBuffer + ans, 32);
46 uint32_t raw2 = bytebits_to_byte(DemodBuffer + ans + 32, 32);
47 uint32_t cardid = bytebits_to_byte(DemodBuffer + ans + 24, 32);
48 uint8_t checksum = bytebits_to_byte(DemodBuffer + ans + 32 + 24, 8);
49 PrintAndLogEx(SUCCESS, "Viking - Card " _GREEN_("%08X") ", Raw: %08X%08X", cardid, raw1, raw2);
50 PrintAndLogEx(DEBUG, "Checksum: %02X", checksum);
51 setDemodBuff(DemodBuffer, 64, ans);
52 setClockGrid(g_DemodClock, g_DemodStartIdx + (ans * g_DemodClock));
53 return PM3_SUCCESS;
56 static int CmdVikingDemod(const char *Cmd) {
57 CLIParserContext *ctx;
58 CLIParserInit(&ctx, "lf viking demod",
59 "Try to find Viking AM preamble, if found decode / descramble data",
60 "lf viking demod"
63 void *argtable[] = {
64 arg_param_begin,
65 arg_param_end
67 CLIExecWithReturn(ctx, Cmd, argtable, true);
68 CLIParserFree(ctx);
69 return demodViking(true);
72 //see ASKDemod for what args are accepted
73 static int CmdVikingReader(const char *Cmd) {
75 CLIParserContext *ctx;
76 CLIParserInit(&ctx, "lf viking reader",
77 "read a Viking AM tag",
78 "lf viking reader -@ -> continuous reader mode"
81 void *argtable[] = {
82 arg_param_begin,
83 arg_lit0("@", NULL, "optional - continuous reader mode"),
84 arg_param_end
86 CLIExecWithReturn(ctx, Cmd, argtable, true);
87 bool cm = arg_get_lit(ctx, 1);
88 CLIParserFree(ctx);
90 if (cm) {
91 PrintAndLogEx(INFO, "Press " _GREEN_("<Enter>") " to exit");
94 do {
95 lf_read(false, 10000);
96 demodViking(true);
97 } while (cm && !kbd_enter_pressed());
99 return PM3_SUCCESS;
102 static int CmdVikingClone(const char *Cmd) {
104 CLIParserContext *ctx;
105 CLIParserInit(&ctx, "lf viking clone",
106 "clone a Viking AM tag to a T55x7, Q5/T5555 or EM4305/4469 tag.",
107 "lf viking clone --cn 01A337\n"
108 "lf viking clone --cn 01A337 --q5 -> encode for Q5/T5555 tag\n"
109 "lf viking clone --cn 112233 --em -> encode for EM4305/4469"
112 void *argtable[] = {
113 arg_param_begin,
114 arg_strx0(NULL, "cn", "<hex>", "8 digit hex viking card number"),
115 arg_lit0(NULL, "q5", "optional - specify writing to Q5/T5555 tag"),
116 arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"),
117 arg_param_end
119 CLIExecWithReturn(ctx, Cmd, argtable, false);
121 int raw_len = 0;
122 uint8_t raw[4] = {0};
123 CLIGetHexWithReturn(ctx, 1, raw, &raw_len);
124 bool q5 = arg_get_lit(ctx, 2);
125 bool em = arg_get_lit(ctx, 3);
126 CLIParserFree(ctx);
128 uint32_t id = bytes_to_num(raw, raw_len);
129 if (id == 0) {
130 PrintAndLogEx(ERR, "Cardnumber can't be zero");
131 return PM3_EINVARG;
134 if (q5 && em) {
135 PrintAndLogEx(FAILED, "Can't specify both Q5 and EM4305 at the same time");
136 return PM3_EINVARG;
139 uint64_t rawID = getVikingBits(id);
141 struct p {
142 bool Q5;
143 bool EM;
144 uint8_t blocks[8];
145 } PACKED payload;
146 payload.Q5 = q5;
147 payload.EM = em;
149 num_to_bytes(rawID, 8, &payload.blocks[0]);
151 char cardtype[16] = {"T55x7"};
152 if (q5)
153 snprintf(cardtype, sizeof(cardtype), "Q5/T5555");
154 else if (em)
155 snprintf(cardtype, sizeof(cardtype), "EM4305/4469");
157 PrintAndLogEx(INFO, "Preparing to clone Viking tag on " _YELLOW_("%s") " - ID " _YELLOW_("%08X")" raw " _YELLOW_("%s")
158 , cardtype
159 , id
160 , sprint_hex(payload.blocks, sizeof(payload.blocks))
163 clearCommandBuffer();
165 SendCommandNG(CMD_LF_VIKING_CLONE, (uint8_t *)&payload, sizeof(payload));
166 PacketResponseNG resp;
167 if (!WaitForResponseTimeout(CMD_LF_VIKING_CLONE, &resp, T55XX_WRITE_TIMEOUT)) {
168 PrintAndLogEx(ERR, "Error occurred, device did not respond during write operation.");
169 return PM3_ETIMEOUT;
171 PrintAndLogEx(SUCCESS, "Done");
172 PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf viking reader`") " to verify");
173 return resp.status;
176 static int CmdVikingSim(const char *Cmd) {
178 CLIParserContext *ctx;
179 CLIParserInit(&ctx, "lf viking sim",
180 "Enables simulation of viking card with specified card number.\n"
181 "Simulation runs until the button is pressed or another USB command is issued.\n"
182 "Per viking format, the card number is 8 digit hex number. Larger values are truncated.",
183 "lf viking sim --cn 01A337"
186 void *argtable[] = {
187 arg_param_begin,
188 arg_strx0(NULL, "cn", "<hex>", "8 digit hex viking card number"),
189 arg_param_end
191 CLIExecWithReturn(ctx, Cmd, argtable, false);
193 int raw_len = 0;
194 uint8_t raw[4] = {0};
195 CLIGetHexWithReturn(ctx, 1, raw, &raw_len);
197 uint32_t id = bytes_to_num(raw, raw_len);
198 if (id == 0) {
199 PrintAndLogEx(ERR, "Cardnumber can't be zero");
200 CLIParserFree(ctx);
201 return PM3_EINVARG;
203 CLIParserFree(ctx);
205 uint64_t rawID = getVikingBits(id);
207 PrintAndLogEx(SUCCESS, "Simulating Viking - ID " _YELLOW_("%08X") " raw " _YELLOW_("%08X%08X"), id, (uint32_t)(rawID >> 32), (uint32_t)(rawID & 0xFFFFFFFF));
209 uint8_t bs[64];
210 num_to_bytebits(rawID, sizeof(bs), bs);
212 lf_asksim_t *payload = calloc(1, sizeof(lf_asksim_t) + sizeof(bs));
213 payload->encoding = 1;
214 payload->invert = 0;
215 payload->separator = 0;
216 payload->clock = 32;
217 memcpy(payload->data, bs, sizeof(bs));
219 clearCommandBuffer();
220 SendCommandNG(CMD_LF_ASK_SIMULATE, (uint8_t *)payload, sizeof(lf_asksim_t) + sizeof(bs));
221 free(payload);
223 PacketResponseNG resp;
224 WaitForResponse(CMD_LF_ASK_SIMULATE, &resp);
226 PrintAndLogEx(INFO, "Done");
227 if (resp.status != PM3_EOPABORTED)
228 return resp.status;
229 return PM3_SUCCESS;
232 static command_t CommandTable[] = {
233 {"help", CmdHelp, AlwaysAvailable, "This help"},
234 {"demod", CmdVikingDemod, AlwaysAvailable, "demodulate a Viking tag from the GraphBuffer"},
235 {"reader", CmdVikingReader, IfPm3Lf, "attempt to read and extract tag data"},
236 {"clone", CmdVikingClone, IfPm3Lf, "clone Viking tag to T55x7 or Q5/T5555"},
237 {"sim", CmdVikingSim, IfPm3Lf, "simulate Viking tag"},
238 {NULL, NULL, NULL, NULL}
241 static int CmdHelp(const char *Cmd) {
242 (void)Cmd; // Cmd is not used so far
243 CmdsHelp(CommandTable);
244 return PM3_SUCCESS;
247 int CmdLFViking(const char *Cmd) {
248 clearCommandBuffer();
249 return CmdsParse(CommandTable, Cmd);
252 // calc checksum
253 uint64_t getVikingBits(uint32_t id) {
254 uint8_t checksum = ((id >> 24) & 0xFF) ^ ((id >> 16) & 0xFF) ^ ((id >> 8) & 0xFF) ^ (id & 0xFF) ^ 0xF2 ^ 0xA8;
255 uint64_t ret = (uint64_t)0xF2 << 56;
256 ret |= (uint64_t)id << 8;
257 ret |= checksum;
258 return ret;
261 // find viking preamble 0xF200 in already demoded data
262 int detectViking(uint8_t *src, size_t *size) {
263 //make sure buffer has data
264 if (*size < 64) return -2;
265 size_t startIdx = 0;
266 uint8_t preamble[] = {1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
267 if (!preambleSearch(src, preamble, sizeof(preamble), size, &startIdx))
268 return -4; //preamble not found
270 uint32_t checkCalc = bytebits_to_byte(src + startIdx, 8) ^
271 bytebits_to_byte(src + startIdx + 8, 8) ^
272 bytebits_to_byte(src + startIdx + 16, 8) ^
273 bytebits_to_byte(src + startIdx + 24, 8) ^
274 bytebits_to_byte(src + startIdx + 32, 8) ^
275 bytebits_to_byte(src + startIdx + 40, 8) ^
276 bytebits_to_byte(src + startIdx + 48, 8) ^
277 bytebits_to_byte(src + startIdx + 56, 8);
279 if (checkCalc != 0xA8) return -5;
280 if (*size != 64) return -6;
281 //return start position
282 return (int)startIdx;