1 //-----------------------------------------------------------------------------
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
7 //-----------------------------------------------------------------------------
8 // Low frequency Viking tag commands (AKA FDI Matalec Transit)
9 // ASK/Manchester, RF/32, 64 bits (complete)
10 //-----------------------------------------------------------------------------
11 #include "cmdlfviking.h"
16 #include "cmdparser.h" // command_t
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
32 if (ASKDemod_ext(0, 0, 100, 0, false, false, false, 1, &st
) != PM3_SUCCESS
) {
33 PrintAndLogEx(DEBUG
, "DEBUG: Error - Viking ASKDemod failed");
37 size_t size
= DemodBufferLen
;
38 int ans
= detectViking(DemodBuffer
, &size
);
40 PrintAndLogEx(DEBUG
, "DEBUG: Error - Viking Demod %d %s", ans
, (ans
== -5) ? _RED_("[chksum error]") : "");
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
));
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",
67 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
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"
83 arg_lit0("@", NULL
, "optional - continuous reader mode"),
86 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
87 bool cm
= arg_get_lit(ctx
, 1);
91 PrintAndLogEx(INFO
, "Press " _GREEN_("<Enter>") " to exit");
95 lf_read(false, 10000);
97 } while (cm
&& !kbd_enter_pressed());
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"
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"),
119 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
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);
128 uint32_t id
= bytes_to_num(raw
, raw_len
);
130 PrintAndLogEx(ERR
, "Cardnumber can't be zero");
135 PrintAndLogEx(FAILED
, "Can't specify both Q5 and EM4305 at the same time");
139 uint64_t rawID
= getVikingBits(id
);
149 num_to_bytes(rawID
, 8, &payload
.blocks
[0]);
151 char cardtype
[16] = {"T55x7"};
153 snprintf(cardtype
, sizeof(cardtype
), "Q5/T5555");
155 snprintf(cardtype
, sizeof(cardtype
), "EM4305/4469");
157 PrintAndLogEx(INFO
, "Preparing to clone Viking tag on " _YELLOW_("%s") " - ID " _YELLOW_("%08X")" raw " _YELLOW_("%s")
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.");
171 PrintAndLogEx(SUCCESS
, "Done");
172 PrintAndLogEx(HINT
, "Hint: try " _YELLOW_("`lf viking reader`") " to verify");
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"
188 arg_strx0(NULL
, "cn", "<hex>", "8 digit hex viking card number"),
191 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
194 uint8_t raw
[4] = {0};
195 CLIGetHexWithReturn(ctx
, 1, raw
, &raw_len
);
197 uint32_t id
= bytes_to_num(raw
, raw_len
);
199 PrintAndLogEx(ERR
, "Cardnumber can't be zero");
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));
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;
215 payload
->separator
= 0;
217 memcpy(payload
->data
, bs
, sizeof(bs
));
219 clearCommandBuffer();
220 SendCommandNG(CMD_LF_ASK_SIMULATE
, (uint8_t *)payload
, sizeof(lf_asksim_t
) + sizeof(bs
));
223 PacketResponseNG resp
;
224 WaitForResponse(CMD_LF_ASK_SIMULATE
, &resp
);
226 PrintAndLogEx(INFO
, "Done");
227 if (resp
.status
!= PM3_EOPABORTED
)
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
);
247 int CmdLFViking(const char *Cmd
) {
248 clearCommandBuffer();
249 return CmdsParse(CommandTable
, Cmd
);
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;
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;
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
;