fix one too small
[RRG-proxmark3.git] / client / src / cmdlfgallagher.c
blobf2bd45af3cc10ce385904e2ad59f167688ef5569
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 GALLAGHER tag commands
17 // ASK/MAN, RF/32, 96 bits long (unknown cs) (0x00088060)
18 // sample Q5 , ASK RF/32, STT, 96 bits (3blocks) ( 0x9000F006)
19 //-----------------------------------------------------------------------------
21 #include "cmdlfgallagher.h"
22 #include "mifare/gallaghercore.h"
23 #include <string.h> // memcpy
24 #include <ctype.h> // tolower
25 #include <stdio.h>
26 #include "commonutil.h" // ARRAYLEN
27 #include "common.h"
28 #include "cmdparser.h" // command_t
29 #include "comms.h"
30 #include "ui.h"
31 #include "cmddata.h"
32 #include "cmdlf.h"
33 #include "lfdemod.h" // preamble test
34 #include "protocols.h" // t55xx defines
35 #include "cmdlft55xx.h" // clone..
36 #include "crc.h" // CRC8/Cardx
37 #include "cmdlfem4x05.h" //
38 #include "cliparser.h"
40 static int CmdHelp(const char *Cmd);
42 //see ASK/MAN Demod for what args are accepted
43 int demodGallagher(bool verbose) {
44 (void) verbose; // unused so far
45 bool st = true;
46 if (ASKDemod_ext(32, 0, 100, 0, false, false, false, 1, &st) != PM3_SUCCESS) {
47 PrintAndLogEx(DEBUG, "DEBUG: Error - GALLAGHER: ASKDemod failed");
48 return PM3_ESOFT;
51 size_t size = g_DemodBufferLen;
52 int ans = detectGallagher(g_DemodBuffer, &size);
53 if (ans < 0) {
54 if (ans == -1)
55 PrintAndLogEx(DEBUG, "DEBUG: Error - GALLAGHER: too few bits found");
56 else if (ans == -2)
57 PrintAndLogEx(DEBUG, "DEBUG: Error - GALLAGHER: preamble not found");
58 else if (ans == -3)
59 PrintAndLogEx(DEBUG, "DEBUG: Error - GALLAGHER: Size not correct: %zu", size);
60 else
61 PrintAndLogEx(DEBUG, "DEBUG: Error - GALLAGHER: ans: %d", ans);
63 return PM3_ESOFT;
65 setDemodBuff(g_DemodBuffer, 96, ans);
66 setClockGrid(g_DemodClock, g_DemodStartIdx + (ans * g_DemodClock));
68 // got a good demod
69 uint32_t raw1 = bytebits_to_byte(g_DemodBuffer, 32);
70 uint32_t raw2 = bytebits_to_byte(g_DemodBuffer + 32, 32);
71 uint32_t raw3 = bytebits_to_byte(g_DemodBuffer + 64, 32);
73 // bytes
74 uint8_t arr[8] = {0};
75 for (int i = 0, pos = 0; i < ARRAYLEN(arr); i++) {
76 // first 16 bits are the 7FEA prefix, then every 9th bit is a checksum-bit for the preceding byte
77 pos = 16 + (9 * i);
78 arr[i] = bytebits_to_byte(g_DemodBuffer + pos, 8);
81 // crc
82 uint8_t crc = bytebits_to_byte(g_DemodBuffer + 16 + (9 * 8), 8);
83 uint8_t calc_crc = CRC8Cardx(arr, ARRAYLEN(arr));
85 GallagherCredentials_t creds = {0};
86 gallagher_decode_creds(arr, &creds);
88 PrintAndLogEx(SUCCESS, "GALLAGHER - Region: " _GREEN_("%u") " Facility: " _GREEN_("%u") " Card No.: " _GREEN_("%u") " Issue Level: " _GREEN_("%u"),
89 creds.region_code, creds.facility_code, creds.card_number, creds.issue_level);
90 PrintAndLogEx(SUCCESS, " Displayed: " _GREEN_("%C%u"), creds.region_code + 'A', creds.facility_code);
91 PrintAndLogEx(SUCCESS, " Raw: %08X%08X%08X", raw1, raw2, raw3);
92 PrintAndLogEx(SUCCESS, " CRC: %02X - %02X (%s)", crc, calc_crc, (crc == calc_crc) ? "ok" : "fail");
93 return PM3_SUCCESS;
96 static int CmdGallagherDemod(const char *Cmd) {
97 CLIParserContext *ctx;
98 CLIParserInit(&ctx, "lf gallagher demod",
99 "Try to find GALLAGHER preamble, if found decode / descramble data",
100 "lf gallagher demod"
103 void *argtable[] = {
104 arg_param_begin,
105 arg_param_end
107 CLIExecWithReturn(ctx, Cmd, argtable, true);
108 CLIParserFree(ctx);
109 return demodGallagher(true);
112 static int CmdGallagherReader(const char *Cmd) {
113 CLIParserContext *ctx;
114 CLIParserInit(&ctx, "lf gallagher reader",
115 "read a GALLAGHER tag",
116 "lf gallagher reader -@ -> continuous reader mode"
119 void *argtable[] = {
120 arg_param_begin,
121 arg_lit0("@", NULL, "optional - continuous reader mode"),
122 arg_param_end
124 CLIExecWithReturn(ctx, Cmd, argtable, true);
125 bool cm = arg_get_lit(ctx, 1);
126 CLIParserFree(ctx);
128 if (cm) {
129 PrintAndLogEx(INFO, "Press " _GREEN_("<Enter>") " to exit");
132 do {
133 lf_read(false, 4096 * 2 + 20);
134 demodGallagher(!cm);
135 } while (cm && !kbd_enter_pressed());
136 return PM3_SUCCESS;
139 static void setBitsInBlocks(uint32_t *blocks, uint8_t *pos, uint32_t data, uint8_t data_len) {
140 for (int i = data_len - 1; i >= 0; i--) {
141 uint8_t blk = *pos / 32;
142 uint8_t bitPos = 31 - *pos % 32; // fill from left
143 uint8_t bit = (data >> i) & 1;
144 blocks[blk] |= bit << bitPos;
145 (*pos)++;
149 static void createBlocks(uint32_t *blocks, GallagherCredentials_t *creds) {
150 // put data into the correct places (Gallagher obfuscation)
151 uint8_t arr[8] = {0};
152 gallagher_encode_creds(arr, creds);
154 blocks[0] = blocks[1] = blocks[2] = 0;
155 uint8_t pos = 0;
157 // magic prefix
158 setBitsInBlocks(blocks, &pos, 0x7fea, 16);
160 for (int i = 0; i < ARRAYLEN(arr); i++) {
161 // data byte
162 setBitsInBlocks(blocks, &pos, arr[i], 8);
164 // every byte is followed by a bit which is the inverse of the last bit
165 setBitsInBlocks(blocks, &pos, !(arr[i] & 0x1), 1);
168 // checksum
169 uint8_t crc = CRC8Cardx(arr, ARRAYLEN(arr));
170 setBitsInBlocks(blocks, &pos, crc, 8);
173 static int CmdGallagherClone(const char *Cmd) {
174 CLIParserContext *ctx;
175 CLIParserInit(&ctx, "lf gallagher clone",
176 "clone a GALLAGHER tag to a T55x7, Q5/T5555 or EM4305/4469 tag.",
177 "lf gallagher clone --raw 0FFD5461A9DA1346B2D1AC32 -> encode for T55x7 tag\n"
178 "lf gallagher clone --raw 0FFD5461A9DA1346B2D1AC32 --q5 -> encode for Q5/T5555 tag\n"
179 "lf gallagher clone --raw 0FFD5461A9DA1346B2D1AC32 --em -> encode for EM4305/4469\n"
180 "lf gallagher clone --rc 0 --fc 9876 --cn 1234 --il 1 -> encode for T55x7 tag from decoded data"
183 void *argtable[] = {
184 arg_param_begin,
185 arg_str0("r", "raw", "<hex>", "raw hex data. 12 bytes max"),
186 arg_lit0(NULL, "q5", "optional - specify writing to Q5/T5555 tag"),
187 arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"),
188 arg_u64_0(NULL, "rc", "<decimal>", "Region code. 4 bits max"),
189 arg_u64_0(NULL, "fc", "<decimal>", "Facility code. 2 bytes max"),
190 arg_u64_0(NULL, "cn", "<decimal>", "Card number. 3 bytes max"),
191 arg_u64_0(NULL, "il", "<decimal>", "Issue level. 4 bits max"),
192 arg_param_end
194 CLIExecWithReturn(ctx, Cmd, argtable, false);
196 int raw_len = 0;
197 // skip first block, 3*4 = 12 bytes left
198 uint8_t raw[12] = {0};
199 int res = CLIParamHexToBuf(arg_get_str(ctx, 1), raw, sizeof raw, &raw_len);
200 if (res) {
201 CLIParserFree(ctx);
202 return PM3_EINVARG;
205 bool q5 = arg_get_lit(ctx, 2);
206 bool em = arg_get_lit(ctx, 3);
207 uint64_t region_code = arg_get_u64_def(ctx, 4, -1); // uint4, input will be validated later
208 uint64_t facility_code = arg_get_u64_def(ctx, 5, -1); // uint16
209 uint64_t card_number = arg_get_u64_def(ctx, 6, -1); // uint24
210 uint64_t issue_level = arg_get_u64_def(ctx, 7, -1); // uint4
211 CLIParserFree(ctx);
213 if (q5 && em) {
214 PrintAndLogEx(FAILED, "Can't specify both Q5 and EM4305 at the same time");
215 return PM3_EINVARG;
218 bool use_raw = (raw_len > 0);
220 if (region_code == -1 && facility_code == -1 && card_number == -1 && issue_level == -1) {
221 if (use_raw == false) {
222 PrintAndLogEx(FAILED, "Must specify either raw data to clone, or rc/fc/cn/il");
223 return PM3_EINVARG;
225 } else {
226 // --raw and --rc/fc/cn/il are mutually exclusive
227 if (use_raw) {
228 PrintAndLogEx(FAILED, "Can't specify both raw and rc/fc/cn/il at the same time");
229 return PM3_EINVARG;
231 if (gallagher_is_valid_creds(region_code, facility_code, card_number, issue_level) == false) {
232 return PM3_EINVARG;
236 uint32_t blocks[4];
237 if (use_raw) {
238 for (uint8_t i = 1; i < ARRAYLEN(blocks); i++) {
239 blocks[i] = bytes_to_num(raw + ((i - 1) * 4), sizeof(uint32_t));
241 } else {
242 GallagherCredentials_t creds = {
243 .region_code = region_code,
244 .facility_code = facility_code,
245 .card_number = card_number,
246 .issue_level = issue_level,
248 // fill blocks 1 to 3 with Gallagher data
249 createBlocks(blocks + 1, &creds);
252 //Pac - compat mode, NRZ, data rate 40, 3 data blocks
253 blocks[0] = T55x7_MODULATION_MANCHESTER | T55x7_BITRATE_RF_32 | 3 << T55x7_MAXBLOCK_SHIFT;
254 char cardtype[16] = {"T55x7"};
256 // Q5
257 if (q5) {
258 blocks[0] = T5555_FIXED | T5555_MODULATION_MANCHESTER | T5555_SET_BITRATE(32) | 3 << T5555_MAXBLOCK_SHIFT;
259 snprintf(cardtype, sizeof(cardtype), "Q5/T5555");
262 // EM4305
263 if (em) {
264 blocks[0] = EM4305_GALLAGHER_CONFIG_BLOCK;
265 snprintf(cardtype, sizeof(cardtype), "EM4305/4469");
268 PrintAndLogEx(INFO, "Preparing to clone Gallagher to " _YELLOW_("%s") " from %s.",
269 cardtype,
270 use_raw ? "raw hex" : "specified data"
272 print_blocks(blocks, ARRAYLEN(blocks));
274 if (em) {
275 res = em4x05_clone_tag(blocks, ARRAYLEN(blocks), 0, false);
276 } else {
277 res = clone_t55xx_tag(blocks, ARRAYLEN(blocks));
279 PrintAndLogEx(SUCCESS, "Done");
280 PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf gallagher reader`") " to verify");
281 return res;
284 static int CmdGallagherSim(const char *Cmd) {
286 CLIParserContext *ctx;
287 CLIParserInit(&ctx, "lf gallagher sim",
288 "Enables simulation of GALLAGHER card with specified card number.\n"
289 "Simulation runs until the button is pressed or another USB command is issued.\n",
290 "lf gallagher sim --raw 0FFD5461A9DA1346B2D1AC32\n"
291 "lf gallagher sim --rc 0 --fc 9876 --cn 1234 --il 1"
294 void *argtable[] = {
295 arg_param_begin,
296 arg_str0("r", "raw", "<hex>", "raw hex data. 12 bytes max"),
297 arg_u64_0(NULL, "rc", "<decimal>", "Region code. 4 bits max"),
298 arg_u64_0(NULL, "fc", "<decimal>", "Facility code. 2 bytes max"),
299 arg_u64_0(NULL, "cn", "<decimal>", "Card number. 3 bytes max"),
300 arg_u64_0(NULL, "il", "<decimal>", "Issue level. 4 bits max"),
301 arg_param_end
303 CLIExecWithReturn(ctx, Cmd, argtable, false);
305 int raw_len = 0;
306 // skip first block, 3*4 = 12 bytes left
307 uint8_t raw[12] = {0};
308 CLIGetHexWithReturn(ctx, 1, raw, &raw_len);
309 int res = CLIParamHexToBuf(arg_get_str(ctx, 1), raw, sizeof raw, &raw_len);
310 if (res) {
311 CLIParserFree(ctx);
312 return PM3_EINVARG;
315 uint64_t region_code = arg_get_u64_def(ctx, 2, -1); // uint4, input will be validated later
316 uint64_t facility_code = arg_get_u64_def(ctx, 3, -1); // uint16
317 uint64_t card_number = arg_get_u64_def(ctx, 4, -1); // uint24
318 uint64_t issue_level = arg_get_u64_def(ctx, 5, -1); // uint4
319 CLIParserFree(ctx);
321 bool use_raw = raw_len > 0;
323 if (region_code == -1 && facility_code == -1 && card_number == -1 && issue_level == -1) {
324 if (use_raw == false) {
325 PrintAndLogEx(FAILED, "Must specify either raw data to clone, or rc/fc/cn/il");
326 return PM3_EINVARG;
328 } else {
329 // --raw and --rc/fc/cn/il are mutually exclusive
330 if (use_raw) {
331 PrintAndLogEx(FAILED, "Can't specify both raw and rc/fc/cn/il at the same time");
332 return PM3_EINVARG;
334 if (gallagher_is_valid_creds(region_code, facility_code, card_number, issue_level) == false) {
335 return PM3_EINVARG;
339 if (use_raw == false) {
340 // generate Gallagher data
341 GallagherCredentials_t creds = {
342 .region_code = region_code,
343 .facility_code = facility_code,
344 .card_number = card_number,
345 .issue_level = issue_level,
347 uint32_t blocks[3];
348 createBlocks(blocks, &creds);
350 // convert to the normal 'raw' format
351 for (int i = 0; i < ARRAYLEN(blocks); i++) {
352 raw[(4 * i) + 0] = (blocks[i] >> 24) & 0xff;
353 raw[(4 * i) + 1] = (blocks[i] >> 16) & 0xff;
354 raw[(4 * i) + 2] = (blocks[i] >> 8) & 0xff;
355 raw[(4 * i) + 3] = (blocks[i]) & 0xff;
359 // ASK/MAN sim.
360 PrintAndLogEx(SUCCESS, "Simulating Gallagher - raw " _YELLOW_("%s"), sprint_hex_inrow(raw, sizeof(raw)));
362 uint8_t bs[sizeof(raw) * 8];
363 bytes_to_bytebits(raw, sizeof(raw), bs);
365 lf_asksim_t *payload = calloc(1, sizeof(lf_asksim_t) + sizeof(bs));
366 payload->encoding = 1;
367 payload->invert = 0;
368 payload->separator = 0;
369 payload->clock = 32;
370 memcpy(payload->data, bs, sizeof(bs));
372 clearCommandBuffer();
373 SendCommandNG(CMD_LF_ASK_SIMULATE, (uint8_t *)payload, sizeof(lf_asksim_t) + sizeof(bs));
374 free(payload);
376 return lfsim_wait_check(CMD_LF_ASK_SIMULATE);
379 static command_t CommandTable[] = {
380 {"help", CmdHelp, AlwaysAvailable, "This help"},
381 {"demod", CmdGallagherDemod, AlwaysAvailable, "demodulate an GALLAGHER tag from the GraphBuffer"},
382 {"reader", CmdGallagherReader, IfPm3Lf, "attempt to read and extract tag data"},
383 {"clone", CmdGallagherClone, IfPm3Lf, "clone GALLAGHER tag to T55x7, Q5/T5555 or EM4305/4469"},
384 {"sim", CmdGallagherSim, IfPm3Lf, "simulate GALLAGHER tag"},
385 {NULL, NULL, NULL, NULL}
388 static int CmdHelp(const char *Cmd) {
389 (void)Cmd; // Cmd is not used so far
390 CmdsHelp(CommandTable);
391 return PM3_SUCCESS;
394 int CmdLFGallagher(const char *Cmd) {
395 clearCommandBuffer();
396 return CmdsParse(CommandTable, Cmd);
399 // find Gallagher preamble in already demoded data
400 int detectGallagher(uint8_t *dest, size_t *size) {
401 if (*size < 96) return -1; //make sure buffer has data
402 size_t startIdx = 0;
403 uint8_t preamble[] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0 };
404 if (!preambleSearch(dest, preamble, sizeof(preamble), size, &startIdx))
405 return -2; //preamble not found
407 if (*size != 96) return -3; //wrong demoded size
408 //return start position
409 return (int)startIdx;