fix one too small
[RRG-proxmark3.git] / client / src / cmdlfpresco.c
blob2fd78c724dde85cc18262ff0806d96f17beb2fcc
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 Presco tag commands
17 //-----------------------------------------------------------------------------
19 #include "cmdlfpresco.h"
20 #include <string.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <ctype.h>
24 #include "commonutil.h" // ARRAYLEN
25 #include "cmdparser.h" // command_t
26 #include "comms.h"
27 #include "ui.h"
28 #include "cmddata.h"
29 #include "cmdlf.h"
30 #include "protocols.h" // for T55xx config register definitions
31 #include "lfdemod.h" // parityTest
32 #include "cmdlft55xx.h" // verifywrite
33 #include "cmdlfem4x05.h" //
34 #include "cliparser.h"
36 static int CmdHelp(const char *Cmd);
38 // find presco preamble 0x10D in already demoded data
39 static int detectPresco(uint8_t *dest, size_t *size) {
40 if (*size < 128 * 2) return -1; //make sure buffer has data
41 size_t startIdx = 0;
42 uint8_t preamble[] = {0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
43 if (!preambleSearch(dest, preamble, sizeof(preamble), size, &startIdx))
44 return -2; //preamble not found
45 if (*size != 128) return -3; //wrong demoded size
46 //return start position
47 return (int)startIdx;
50 // convert base 12 ID to sitecode & usercode & 8 bit other unknown code
51 static int getWiegandFromPrintedPresco(void *arr, uint32_t *fullcode) {
52 char *s = (char *)arr;
53 uint8_t val = 0;
54 for (int i = 0; i < strlen(s); ++i) {
55 // Get value from number string.
56 if (s[i] == '*')
57 val = 10;
58 if (s[i] == '#')
59 val = 11;
60 if (s[i] >= 0x30 && s[i] <= 0x39)
61 val = s[i] - 0x30;
63 *fullcode += val;
65 // last digit is only added, not multiplied.
66 if (i < strlen(s) - 1)
67 *fullcode *= 12;
69 return PM3_SUCCESS;
72 // calc not certain - intended to get bitstream for programming / sim
73 static int getPrescoBits(uint32_t fullcode, uint8_t *prescoBits) {
74 num_to_bytebits(0x10D00000, 32, prescoBits);
75 num_to_bytebits(0x00000000, 32, prescoBits + 32);
76 num_to_bytebits(0x00000000, 32, prescoBits + 64);
77 num_to_bytebits(fullcode, 32, prescoBits + 96);
78 return PM3_SUCCESS;
81 //see ASKDemod for what args are accepted
82 int demodPresco(bool verbose) {
83 (void) verbose; // unused so far
84 bool st = true;
85 if (ASKDemod_ext(32, 0, 0, 0, false, false, false, 1, &st) != PM3_SUCCESS) {
86 PrintAndLogEx(DEBUG, "DEBUG: Error Presco ASKDemod failed");
87 return PM3_ESOFT;
89 size_t size = g_DemodBufferLen;
90 int ans = detectPresco(g_DemodBuffer, &size);
91 if (ans < 0) {
92 if (ans == -1)
93 PrintAndLogEx(DEBUG, "DEBUG: Error - Presco: too few bits found");
94 else if (ans == -2)
95 PrintAndLogEx(DEBUG, "DEBUG: Error - Presco: preamble not found");
96 else if (ans == -3)
97 PrintAndLogEx(DEBUG, "DEBUG: Error - Presco: Size not correct: %zu", size);
98 else
99 PrintAndLogEx(DEBUG, "DEBUG: Error - Presco: ans: %d", ans);
100 return PM3_ESOFT;
102 setDemodBuff(g_DemodBuffer, 128, ans);
103 setClockGrid(g_DemodClock, g_DemodStartIdx + (ans * g_DemodClock));
105 //got a good demod
106 uint32_t raw1 = bytebits_to_byte(g_DemodBuffer, 32);
107 uint32_t raw2 = bytebits_to_byte(g_DemodBuffer + 32, 32);
108 uint32_t raw3 = bytebits_to_byte(g_DemodBuffer + 64, 32);
109 uint32_t raw4 = bytebits_to_byte(g_DemodBuffer + 96, 32);
110 uint32_t fullcode = raw4;
111 uint32_t usercode = fullcode & 0x0000FFFF;
112 uint32_t sitecode = (fullcode >> 24) & 0x000000FF;
114 PrintAndLogEx(SUCCESS, "Presco Site code: " _GREEN_("%u") " User code: " _GREEN_("%u") " Full code: " _GREEN_("%08X") " Raw: " _YELLOW_("%08X%08X%08X%08X")
115 , sitecode
116 , usercode
117 , fullcode
118 , raw1, raw2, raw3, raw4
120 return PM3_SUCCESS;
123 static int CmdPrescoDemod(const char *Cmd) {
124 CLIParserContext *ctx;
125 CLIParserInit(&ctx, "lf presco demod",
126 "Try to find presco preamble, if found decode / descramble data",
127 "lf presco demod"
130 void *argtable[] = {
131 arg_param_begin,
132 arg_param_end
134 CLIExecWithReturn(ctx, Cmd, argtable, true);
135 CLIParserFree(ctx);
136 return demodPresco(true);
139 //see ASKDemod for what args are accepted
140 static int CmdPrescoReader(const char *Cmd) {
141 CLIParserContext *ctx;
142 CLIParserInit(&ctx, "lf presco reader",
143 "read a presco tag",
144 "lf presco reader -@ -> continuous reader mode"
147 void *argtable[] = {
148 arg_param_begin,
149 arg_lit0("@", NULL, "optional - continuous reader mode"),
150 arg_param_end
152 CLIExecWithReturn(ctx, Cmd, argtable, true);
153 bool cm = arg_get_lit(ctx, 1);
154 CLIParserFree(ctx);
156 if (cm) {
157 PrintAndLogEx(INFO, "Press " _GREEN_("<Enter>") " to exit");
160 do {
161 lf_read(false, 12000);
162 demodPresco(!cm);
163 } while (cm && !kbd_enter_pressed());
164 return PM3_SUCCESS;
167 // takes base 12 ID converts to hex
168 // Or takes 8 digit hex ID
169 static int CmdPrescoClone(const char *Cmd) {
170 CLIParserContext *ctx;
171 CLIParserInit(&ctx, "lf presco clone",
172 "clone a presco tag to a T55x7, Q5/T5555 or EM4305/4469 tag.",
173 "lf presco clone -d 018363467 -> encode for T55x7 tag\n"
174 "lf presco clone -d 018363467 --q5 -> encode for Q5/T5555 tag\n"
175 "lf presco clone -d 018363467 --em -> encode for EM4305/4469"
178 void *argtable[] = {
179 arg_param_begin,
180 arg_str0("c", NULL, "<hex>", "8 digit hex card number"),
181 arg_str0("d", NULL, "<digits>", "9 digit presco card ID"),
182 arg_lit0(NULL, "q5", "optional - specify writing to Q5/T5555 tag"),
183 arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"),
184 arg_param_end
186 CLIExecWithReturn(ctx, Cmd, argtable, false);
188 int hex_len = 0;
189 uint8_t hex[4] = {0, 0, 0, 0};
190 CLIGetHexWithReturn(ctx, 1, hex, &hex_len);
192 uint8_t idstr[10];
193 int slen = sizeof(idstr) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated
194 memset(idstr, 0x00, sizeof(idstr));
195 CLIGetStrWithReturn(ctx, 2, idstr, &slen);
197 bool q5 = arg_get_lit(ctx, 3);
198 bool em = arg_get_lit(ctx, 4);
199 CLIParserFree(ctx);
201 if (q5 && em) {
202 PrintAndLogEx(FAILED, "Can't specify both Q5 and EM4305 at the same time");
203 return PM3_EINVARG;
206 uint32_t fullcode = 0;
208 if (hex_len) {
209 fullcode = bytes_to_num(hex, hex_len);
210 } else {
211 //param get string int param_getstr(const char *line, int paramnum, char * str)
212 if (slen < 2) {
213 PrintAndLogEx(ERR, "Must contain atleast 2 digits");
214 return PM3_EINVARG;
217 getWiegandFromPrintedPresco(idstr, &fullcode);
220 uint32_t usercode = fullcode & 0x0000FFFF; //% 65566
221 uint32_t sitecode = (fullcode >> 24) & 0x000000FF; // /= 16777216;
223 uint32_t blocks[5] = {T55x7_MODULATION_MANCHESTER | T55x7_BITRATE_RF_32 | 4 << T55x7_MAXBLOCK_SHIFT | T55x7_ST_TERMINATOR, 0, 0, 0, 0};
225 char cardtype[16] = {"T55x7"};
226 // Q5
227 if (q5) {
228 blocks[0] = T5555_FIXED | T5555_MODULATION_MANCHESTER | T5555_SET_BITRATE(32) | 4 << T5555_MAXBLOCK_SHIFT | T5555_ST_TERMINATOR;
229 snprintf(cardtype, sizeof(cardtype), "Q5/T5555");
232 // EM4305
233 if (em) {
234 blocks[0] = EM4305_PRESCO_CONFIG_BLOCK;
235 snprintf(cardtype, sizeof(cardtype), "EM4305/4469");
238 if ((sitecode & 0xFF) != sitecode) {
239 sitecode &= 0xFF;
240 PrintAndLogEx(INFO, "Site code truncated to 8-bits (Presco): %u", sitecode);
243 if ((usercode & 0xFFFF) != usercode) {
244 usercode &= 0xFFFF;
245 PrintAndLogEx(INFO, "User code truncated to 16-bits (Presco): %u", usercode);
248 blocks[1] = 0x10D00000; //preamble
249 blocks[2] = 0x00000000;
250 blocks[3] = 0x00000000;
251 blocks[4] = fullcode;
253 PrintAndLogEx(INFO, "Preparing to clone Presco to " _GREEN_("%s") " with Site code: " _GREEN_("%u") " User code: " _GREEN_("%u") " Full code: " _GREEN_("%08x")
254 , cardtype
255 , sitecode
256 , usercode
257 , fullcode
259 print_blocks(blocks, ARRAYLEN(blocks));
261 int res;
262 if (em) {
263 res = em4x05_clone_tag(blocks, ARRAYLEN(blocks), 0, false);
264 } else {
265 res = clone_t55xx_tag(blocks, ARRAYLEN(blocks));
267 PrintAndLogEx(SUCCESS, "Done");
268 PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf presco reader`") " to verify");
269 return res;
272 // takes base 12 ID converts to hex
273 // Or takes 8 digit hex ID
274 static int CmdPrescoSim(const char *Cmd) {
275 CLIParserContext *ctx;
276 CLIParserInit(&ctx, "lf presco sim",
277 "Enables simulation of presco card with specified card number.\n"
278 "Simulation runs until the button is pressed or another USB command is issued.\n"
279 "Per presco format, the card number is 9 digit number and can contain *# chars. Larger values are truncated.",
280 "lf presco sim -d 018363467"
283 void *argtable[] = {
284 arg_param_begin,
285 arg_str0("c", NULL, "<hex>", "8 digit hex card number"),
286 arg_str0("d", NULL, "<digits>", "9 digit presco card ID"),
287 arg_param_end
289 CLIExecWithReturn(ctx, Cmd, argtable, false);
291 int hex_len = 0;
292 uint8_t hex[4] = {0, 0, 0, 0};
293 CLIGetHexWithReturn(ctx, 1, hex, &hex_len);
295 uint8_t idstr[10] = {0};
296 int slen = sizeof(idstr) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated
297 memset(idstr, 0x00, sizeof(idstr));
298 CLIGetStrWithReturn(ctx, 2, idstr, &slen);
299 CLIParserFree(ctx);
301 uint32_t fullcode = 0;
303 if (hex_len) {
304 fullcode = bytes_to_num(hex, hex_len);
305 } else {
306 if (slen < 2) {
307 PrintAndLogEx(ERR, "Must contain atleast 2 digits");
308 return PM3_EINVARG;
310 getWiegandFromPrintedPresco(idstr, &fullcode);
313 uint32_t usercode = fullcode & 0x0000FFFF;
314 uint32_t sitecode = (fullcode >> 24) & 0x000000FF;
316 if ((sitecode & 0xFF) != sitecode) {
317 sitecode &= 0xFF;
318 PrintAndLogEx(INFO, "Site code truncated to 8-bits (Presco): %u", sitecode);
321 if ((usercode & 0xFFFF) != usercode) {
322 usercode &= 0xFFFF;
323 PrintAndLogEx(INFO, "User code truncated to 16-bits (Presco): %u", usercode);
326 PrintAndLogEx(SUCCESS, "Simulating Presco - Site Code: " _GREEN_("%u") " User Code: " _GREEN_("%u") " Full Code: " _GREEN_("%08X")
327 , sitecode
328 , usercode
329 , fullcode)
332 uint8_t bs[128];
333 getPrescoBits(fullcode, bs);
335 lf_asksim_t *payload = calloc(1, sizeof(lf_asksim_t) + sizeof(bs));
336 payload->encoding = 1;
337 payload->invert = 0;
338 payload->separator = 1;
339 payload->clock = 32;
340 memcpy(payload->data, bs, sizeof(bs));
342 clearCommandBuffer();
343 SendCommandNG(CMD_LF_ASK_SIMULATE, (uint8_t *)payload, sizeof(lf_asksim_t) + sizeof(bs));
344 free(payload);
346 PacketResponseNG resp;
347 WaitForResponse(CMD_LF_ASK_SIMULATE, &resp);
349 PrintAndLogEx(INFO, "Done!");
350 if (resp.status != PM3_EOPABORTED) {
351 return resp.status;
353 return PM3_SUCCESS;
356 static command_t CommandTable[] = {
357 {"help", CmdHelp, AlwaysAvailable, "This help"},
358 {"demod", CmdPrescoDemod, AlwaysAvailable, "demodulate Presco tag from the GraphBuffer"},
359 {"reader", CmdPrescoReader, IfPm3Lf, "attempt to read and extract tag data"},
360 {"clone", CmdPrescoClone, IfPm3Lf, "clone presco tag to T55x7, Q5/T5555 or EM4305/4469"},
361 {"sim", CmdPrescoSim, IfPm3Lf, "simulate presco tag"},
362 {NULL, NULL, NULL, NULL}
365 static int CmdHelp(const char *Cmd) {
366 (void)Cmd; // Cmd is not used so far
367 CmdsHelp(CommandTable);
368 return PM3_SUCCESS;
371 int CmdLFPresco(const char *Cmd) {
372 clearCommandBuffer();
373 return CmdsParse(CommandTable, Cmd);