1 //-----------------------------------------------------------------------------
3 // This code is licensed to you under the terms of the GNU GPL, version 2 or,
4 // at your option, any later version. See the LICENSE.txt file for the text of
6 //-----------------------------------------------------------------------------
7 // Low frequency Securakey tag commands
8 // ASK/Manchester, RF/40, 96 bits long (unknown cs)
9 //-----------------------------------------------------------------------------
10 #include "cmdlfsecurakey.h"
11 #include <string.h> // memcpy
12 #include <ctype.h> // tolower
13 #include "commonutil.h" // ARRAYLEN
14 #include "cmdparser.h" // command_t
19 #include "lfdemod.h" // preamble test
20 #include "parity.h" // for wiegand parity test
21 #include "protocols.h" // t55xx defines
22 #include "cmdlft55xx.h" // clone..
23 #include "cliparser.h"
24 #include "cmdlfem4x05.h" // EM defines
26 static int CmdHelp(const char *Cmd
);
28 //see ASKDemod for what args are accepted
29 int demodSecurakey(bool verbose
) {
30 (void) verbose
; // unused so far
34 if (ASKDemod_ext(40, 0, 0, 0, false, false, false, 1, &st
) != PM3_SUCCESS
) {
35 PrintAndLogEx(DEBUG
, "DEBUG: Error - Securakey: ASK/Manchester Demod failed");
41 size_t size
= DemodBufferLen
;
42 int ans
= detectSecurakey(DemodBuffer
, &size
);
45 PrintAndLogEx(DEBUG
, "DEBUG: Error - Securakey: too few bits found");
47 PrintAndLogEx(DEBUG
, "DEBUG: Error - Securakey: preamble not found");
49 PrintAndLogEx(DEBUG
, "DEBUG: Error - Securakey: Size not correct: %zu", size
);
51 PrintAndLogEx(DEBUG
, "DEBUG: Error - Securakey: ans: %d", ans
);
54 setDemodBuff(DemodBuffer
, 96, ans
);
55 setClockGrid(g_DemodClock
, g_DemodStartIdx
+ (ans
* g_DemodClock
));
58 uint32_t raw1
= bytebits_to_byte(DemodBuffer
, 32);
59 uint32_t raw2
= bytebits_to_byte(DemodBuffer
+ 32, 32);
60 uint32_t raw3
= bytebits_to_byte(DemodBuffer
+ 64, 32);
63 // preamble ??bitlen reserved EPx xxxxxxxy yyyyyyyy yyyyyyyOP CS? CS2?
64 // 0111111111 0 01011010 0 00000000 0 00000010 0 00110110 0 00111110 0 01100010 0 00001111 0 01100000 0 00000000 0 0000
67 // preamble ??bitlen reserved EPxxxxxxx xxxxxxxy yyyyyyyy yyyyyyyOP CS? CS2?
68 // 0111111111 0 01100000 0 00000000 0 10000100 0 11001010 0 01011011 0 01010110 0 00010110 0 11100000 0 00000000 0 0000
72 // standard wiegand parities.
73 // unknown checksum 11 bits? at the end
74 uint8_t bits_no_spacer
[85];
75 memcpy(bits_no_spacer
, DemodBuffer
+ 11, 85);
77 // remove marker bits (0's every 9th digit after preamble) (pType = 3 (always 0s))
78 size
= removeParity(bits_no_spacer
, 0, 9, 3, 85);
80 PrintAndLogEx(DEBUG
, "DEBUG: Error removeParity: %zu", size
);
84 uint8_t bitLen
= (uint8_t)bytebits_to_byte(bits_no_spacer
+ 2, 6);
85 uint32_t fc
= 0, lWiegand
= 0, rWiegand
= 0;
86 if (bitLen
> 40) { //securakey's max bitlen is 40 bits...
87 PrintAndLogEx(DEBUG
, "DEBUG: Error bitLen too long: %u", bitLen
);
90 // get left 1/2 wiegand & right 1/2 wiegand (for parity test and wiegand print)
91 lWiegand
= bytebits_to_byte(bits_no_spacer
+ 48 - bitLen
, bitLen
/ 2);
92 rWiegand
= bytebits_to_byte(bits_no_spacer
+ 48 - bitLen
+ bitLen
/ 2, bitLen
/ 2);
94 fc
= bytebits_to_byte(bits_no_spacer
+ 49 - bitLen
, bitLen
- 2 - 16);
97 if (bitLen
!= 26 && bitLen
!= 32)
98 PrintAndLogEx(NORMAL
, "***unknown securakey bitLen - share with forum***");
100 uint32_t cardid
= bytebits_to_byte(bits_no_spacer
+ 8 + 23, 16);
101 // test parities - evenparity32 looks to add an even parity returns 0 if already even...
102 bool parity
= !evenparity32(lWiegand
) && !oddparity32(rWiegand
);
104 PrintAndLogEx(SUCCESS
, "Securakey - len: " _GREEN_("%u") " FC: " _GREEN_("0x%X")" Card: " _GREEN_("%u") ", Raw: %08X%08X%08X", bitLen
, fc
, cardid
, raw1
, raw2
, raw3
);
106 PrintAndLogEx(SUCCESS
, "Wiegand: " _GREEN_("%08X") " parity (%s)", (lWiegand
<< (bitLen
/ 2)) | rWiegand
, parity
? _GREEN_("ok") : _RED_("fail"));
109 PrintAndLogEx(INFO
, "\nHow the FC translates to printed FC is unknown");
110 PrintAndLogEx(INFO
, "How the checksum is calculated is unknown");
111 PrintAndLogEx(INFO
, "Help the community identify this format further\nby sharing your tag on the pm3 forum or discord");
116 static int CmdSecurakeyDemod(const char *Cmd
) {
117 CLIParserContext
*ctx
;
118 CLIParserInit(&ctx
, "lf securakey demod",
119 "Try to find Securakey preamble, if found decode / descramble data",
127 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
129 return demodSecurakey(true);
132 static int CmdSecurakeyReader(const char *Cmd
) {
133 CLIParserContext
*ctx
;
134 CLIParserInit(&ctx
, "lf securakey reader",
135 "read a Securakey tag",
136 "lf securakey reader -@ -> continuous reader mode"
141 arg_lit0("@", NULL
, "optional - continuous reader mode"),
144 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
145 bool cm
= arg_get_lit(ctx
, 1);
149 PrintAndLogEx(INFO
, "Press " _GREEN_("<Enter>") " to exit");
153 lf_read(false, 8000);
155 } while (cm
&& !kbd_enter_pressed());
160 static int CmdSecurakeyClone(const char *Cmd
) {
162 CLIParserContext
*ctx
;
163 CLIParserInit(&ctx
, "lf securakey clone",
164 "clone a Securakey tag to a T55x7, Q5/T5555 or EM4305/4469 tag.",
165 "lf securakey clone --raw 7FCB400001ADEA5344300000\n"
166 "lf securakey clone --q5 --raw 7FCB400001ADEA5344300000 -> encode for Q5/T5555 tag\n"
167 "lf securakey clone --em --raw 7FCB400001ADEA5344300000 -> encode for EM4305/4469"
172 arg_str1("r", "raw", "<hex>", "raw hex data. 12 bytes"),
173 arg_lit0(NULL
, "q5", "optional - specify writing to Q5/T5555 tag"),
174 arg_lit0(NULL
, "em", "optional - specify writing to EM4305/4469 tag"),
177 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
180 // skip first block, 3*4 = 12 bytes left
181 uint8_t raw
[12] = {0};
182 CLIGetHexWithReturn(ctx
, 1, raw
, &raw_len
);
183 bool q5
= arg_get_lit(ctx
, 2);
184 bool em
= arg_get_lit(ctx
, 3);
188 PrintAndLogEx(FAILED
, "Can't specify both Q5 and EM4305 at the same time");
193 PrintAndLogEx(ERR
, "Data must be 12 bytes (24 HEX characters) %d", raw_len
);
198 for (uint8_t i
= 1; i
< ARRAYLEN(blocks
); i
++) {
199 blocks
[i
] = bytes_to_num(raw
+ ((i
- 1) * 4), sizeof(uint32_t));
202 //Securakey - compat mode, ASK/Man, data rate 40, 3 data blocks
203 blocks
[0] = T55x7_MODULATION_MANCHESTER
| T55x7_BITRATE_RF_40
| 3 << T55x7_MAXBLOCK_SHIFT
;
204 char cardtype
[16] = {"T55x7"};
207 blocks
[0] = T5555_FIXED
| T5555_MODULATION_MANCHESTER
| T5555_SET_BITRATE(40) | T5555_ST_TERMINATOR
| 3 << T5555_MAXBLOCK_SHIFT
;
208 snprintf(cardtype
, sizeof(cardtype
), "Q5/T5555");
213 blocks
[0] = EM4305_SECURAKEY_CONFIG_BLOCK
;
214 snprintf(cardtype
, sizeof(cardtype
), "EM4305/4469");
217 PrintAndLogEx(INFO
, "Preparing to clone Securakey to " _YELLOW_("%s") " with raw hex", cardtype
);
218 print_blocks(blocks
, ARRAYLEN(blocks
));
222 res
= em4x05_clone_tag(blocks
, ARRAYLEN(blocks
), 0, false);
224 res
= clone_t55xx_tag(blocks
, ARRAYLEN(blocks
));
226 PrintAndLogEx(SUCCESS
, "Done");
227 PrintAndLogEx(HINT
, "Hint: try " _YELLOW_("`lf securakey reader`") " to verify");
231 static int CmdSecurakeySim(const char *Cmd
) {
233 CLIParserContext
*ctx
;
234 CLIParserInit(&ctx
, "lf securakey sim",
235 "Enables simulation of secura card with specified card number.\n"
236 "Simulation runs until the button is pressed or another USB command is issued.",
237 "lf securakey sim --raw 7FCB400001ADEA5344300000"
242 arg_str0("r", "raw", "<hex>", " raw hex data. 12 bytes"),
245 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
248 // skip first block, 3*4 = 12 bytes left
249 uint8_t raw
[12] = {0};
250 CLIGetHexWithReturn(ctx
, 1, raw
, &raw_len
);
254 PrintAndLogEx(ERR
, "Data must be 12 bytes (24 HEX characters) %d", raw_len
);
258 PrintAndLogEx(SUCCESS
, "Simulating SecuraKey - raw " _YELLOW_("%s"), sprint_hex_inrow(raw
, sizeof(raw
)));
260 uint8_t bs
[sizeof(raw
) * 8];
261 bytes_to_bytebits(raw
, sizeof(raw
), bs
);
263 lf_asksim_t
*payload
= calloc(1, sizeof(lf_asksim_t
) + sizeof(bs
));
264 payload
->encoding
= 1;
266 payload
->separator
= 0;
268 memcpy(payload
->data
, bs
, sizeof(bs
));
270 clearCommandBuffer();
271 SendCommandNG(CMD_LF_ASK_SIMULATE
, (uint8_t *)payload
, sizeof(lf_asksim_t
) + sizeof(bs
));
274 PacketResponseNG resp
;
275 WaitForResponse(CMD_LF_ASK_SIMULATE
, &resp
);
277 PrintAndLogEx(INFO
, "Done");
278 if (resp
.status
!= PM3_EOPABORTED
)
284 static command_t CommandTable
[] = {
285 {"help", CmdHelp
, AlwaysAvailable
, "This help"},
286 {"demod", CmdSecurakeyDemod
, AlwaysAvailable
, "demodulate an Securakey tag from the GraphBuffer"},
287 {"reader", CmdSecurakeyReader
, IfPm3Lf
, "attempt to read and extract tag data"},
288 {"clone", CmdSecurakeyClone
, IfPm3Lf
, "clone Securakey tag to T55x7"},
289 {"sim", CmdSecurakeySim
, IfPm3Lf
, "simulate Securakey tag"},
290 {NULL
, NULL
, NULL
, NULL
}
293 static int CmdHelp(const char *Cmd
) {
294 (void)Cmd
; // Cmd is not used so far
295 CmdsHelp(CommandTable
);
299 int CmdLFSecurakey(const char *Cmd
) {
300 clearCommandBuffer();
301 return CmdsParse(CommandTable
, Cmd
);
305 // find Securakey preamble in already demoded data
306 int detectSecurakey(uint8_t *dest
, size_t *size
) {
307 if (*size
< 96) return -1; //make sure buffer has data
309 uint8_t preamble
[] = {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1};
310 if (!preambleSearch(dest
, preamble
, sizeof(preamble
), size
, &startIdx
))
311 return -2; //preamble not found
312 if (*size
!= 96) return -3; //wrong demoded size
313 //return start position
314 return (int)startIdx
;