1 //-----------------------------------------------------------------------------
2 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
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.
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 KERI tag commands
17 // PSK1, RF/128, RF/2, 64 bits long
18 //-----------------------------------------------------------------------------
19 #include "cmdlfkeri.h"
24 #include "commonutil.h" // ARRAYLEN
25 #include "cmdparser.h" // command_t
26 #include "cliparser.h"
31 #include "protocols.h" // for T55xx config register definitions
32 #include "lfdemod.h" // preamble test
33 #include "cmdlft55xx.h" // verifywrite
34 #include "cmdlfem4x05.h" //
36 static int CmdHelp(const char *Cmd
);
37 typedef enum {Scramble
= 0, Descramble
= 1} KeriMSScramble_t
;
39 static int CmdKeriMSScramble(KeriMSScramble_t Action
, uint32_t *FC
, uint32_t *ID
, uint32_t *CardID
) {
40 // 255 = Not used/Unknown other values are the bit offset in the ID/FC values
41 const uint8_t CardToID
[] = { 255, 255, 255, 255, 13, 12, 20, 5, 16, 6, 21, 17, 8, 255, 0, 7,
42 10, 15, 255, 11, 4, 1, 255, 18, 255, 19, 2, 14, 3, 9, 255, 255
45 const uint8_t CardToFC
[] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255,
46 255, 255, 2, 255, 255, 255, 3, 255, 4, 255, 255, 255, 255, 255, 1, 255
49 uint8_t card_idx
; // 0 - 31
51 if (Action
== Descramble
) {
54 for (card_idx
= 0; card_idx
< 32; card_idx
++) {
56 bool BitState
= (*CardID
>> card_idx
) & 1;
58 if (CardToID
[card_idx
] < 32) {
59 *ID
= *ID
| (BitState
<< CardToID
[card_idx
]);
62 if (CardToFC
[card_idx
] < 32) {
63 *FC
= *FC
| (BitState
<< CardToFC
[card_idx
]);
68 if (Action
== Scramble
) {
69 *CardID
= 0; // set to 0
71 for (card_idx
= 0; card_idx
< 32; card_idx
++) {
73 if (CardToID
[card_idx
] < 32) {
74 if ((*ID
& (1U << CardToID
[card_idx
])) > 0)
75 *CardID
|= (1U << card_idx
);
78 if (CardToFC
[card_idx
] < 32) {
79 if ((*FC
& (1U << CardToFC
[card_idx
])) > 0)
80 *CardID
|= (1U << card_idx
);
84 // Fixed bits and parity/check bits
86 Add Parity and Fixed bits
87 Bit 3 - Note Used/Fixed 1 - TBC
88 Bit 31 - 1 Fixed Not in check/parity
89 Bit 0,1 - 2 Bit Parity
95 for (card_idx
= 4; card_idx
<= 31; card_idx
+= 2) {
96 parity
^= ((*CardID
>> card_idx
) & 11);
98 *CardID
= *CardID
| parity
;
100 // Bit 31 was fixed but not in check/parity bits
101 *CardID
|= 1UL << 31;
103 PrintAndLogEx(SUCCESS
, "Scrambled MS - FC: " _GREEN_("%d") " Card: " _GREEN_("%d") ", Raw: E0000000%08X", *FC
, *ID
, *CardID
);
108 int demodKeri(bool verbose
) {
109 (void) verbose
; // unused so far
111 if (PSKDemod(0, 0, 100, false) != PM3_SUCCESS
) {
112 PrintAndLogEx(DEBUG
, "DEBUG: Error - KERI: PSK1 Demod failed");
117 size_t size
= g_DemodBufferLen
;
118 int idx
= detectKeri(g_DemodBuffer
, &size
, &invert
);
121 PrintAndLogEx(DEBUG
, "DEBUG: Error - KERI: too few bits found");
123 PrintAndLogEx(DEBUG
, "DEBUG: Error - KERI: preamble not found");
125 PrintAndLogEx(DEBUG
, "DEBUG: Error - KERI: Size not correct: 64 != %zu", size
);
127 PrintAndLogEx(DEBUG
, "DEBUG: Error - KERI: ans: %d", idx
);
131 setDemodBuff(g_DemodBuffer
, size
, idx
);
132 setClockGrid(g_DemodClock
, g_DemodStartIdx
+ (idx
* g_DemodClock
));
135 000000000000000000000000000001XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX111
136 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^1###############################^^^
137 Preamble block 29 bits of ZEROS
138 32 bit Internal ID (First bit always 1)
139 3 bit of 1s in the end
141 How this is decoded to Facility ID, Card number is unknown
142 Facility ID = 0-31 (indicates 5 bits)
143 Card number = up to 10 digits
145 Might be a hash of FC & CN to generate Internal ID
155 uint32_t raw1
= bytebits_to_byte(g_DemodBuffer
, 32);
156 uint32_t raw2
= bytebits_to_byte(g_DemodBuffer
+ 32, 32);
159 PrintAndLogEx(INFO
, "Had to Invert - probably KERI");
160 for (size_t i
= 0; i
< size
; i
++)
161 g_DemodBuffer
[i
] ^= 1;
163 raw1
= bytebits_to_byte(g_DemodBuffer
, 32);
164 raw2
= bytebits_to_byte(g_DemodBuffer
+ 32, 32);
166 CmdPrintDemodBuff("-x");
170 // uint32_t ID = bytebits_to_byte(g_DemodBuffer + 29, 32);
171 // Due to the 3 sync bits being at the start of the capture
172 // We can take the last 32bits as the internal ID.
176 PrintAndLogEx(SUCCESS
, "KERI - Internal ID: " _GREEN_("%u") ", Raw: %08X%08X", ID
, raw1
, raw2
);
178 // Just need to the low 32 bits without the 111 trailer
179 CmdKeriMSScramble(Descramble
, &fc
, &cardid
, &raw2
);
181 PrintAndLogEx(SUCCESS
, "Descrambled MS - FC: " _GREEN_("%d") " Card: " _GREEN_("%d"), fc
, cardid
);
185 static int CmdKeriDemod(const char *Cmd
) {
186 CLIParserContext
*ctx
;
187 CLIParserInit(&ctx
, "lf keri demod",
188 "Try to find KERI preamble, if found decode / descramble data",
196 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
198 return demodKeri(true);
201 static int CmdKeriReader(const char *Cmd
) {
202 CLIParserContext
*ctx
;
203 CLIParserInit(&ctx
, "lf keri reader",
205 "lf keri reader -@ -> continuous reader mode"
210 arg_lit0("@", NULL
, "optional - continuous reader mode"),
213 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
214 bool cm
= arg_get_lit(ctx
, 1);
218 PrintAndLogEx(INFO
, "Press " _GREEN_("<Enter>") " to exit");
222 lf_read(false, 10000);
224 } while (cm
&& !kbd_enter_pressed());
229 static int CmdKeriClone(const char *Cmd
) {
230 CLIParserContext
*ctx
;
231 CLIParserInit(&ctx
, "lf keri clone",
232 "clone a KERI tag to a T55x7, Q5/T5555 or EM4305/4469 tag",
233 "lf keri clone -t i --cn 12345 -> Internal ID\n"
234 "lf keri clone -t m --fc 6 --cn 12345 -> MS ID\n");
238 arg_str0("t", "type", "<m|i>", "Type m - MS, i - Internal ID"),
239 arg_int0(NULL
, "fc", "<dec>", "Facility Code"),
240 arg_int1(NULL
, "cn", "<dec>", "KERI card ID"),
241 arg_lit0(NULL
, "q5", "specify writing to Q5/T5555 tag"),
242 arg_lit0(NULL
, "em", "specify writing to EM4305/4469 tag"),
245 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
247 uint8_t keritype
[2] = {'i', 0}; // default to internalid
248 int typeLen
= sizeof(keritype
) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated
249 CLIGetStrWithReturn(ctx
, 1, keritype
, &typeLen
);
251 uint32_t fc
= arg_get_int_def(ctx
, 2, 0);
252 uint32_t cid
= arg_get_int_def(ctx
, 3, 0);
253 bool q5
= arg_get_lit(ctx
, 4);
254 bool em
= arg_get_lit(ctx
, 5);
258 PrintAndLogEx(FAILED
, "Can't specify both Q5 and EM4305 at the same time");
262 // Setup card data/build internal id
263 uint32_t internalid
= 0;
264 switch (keritype
[0]) {
265 case 'i' : // Internal ID
267 internalid
= cid
| 0x80000000;
270 CmdKeriMSScramble(Scramble
, &fc
, &cid
, &internalid
);
273 PrintAndLogEx(ERR
, "Invalid type");
278 blocks
[0] = T55x7_TESTMODE_DISABLED
| T55x7_X_MODE
| T55x7_MODULATION_PSK1
| T55x7_PSKCF_RF_2
| 2 << T55x7_MAXBLOCK_SHIFT
;
279 // dynamic bitrate used
280 blocks
[0] |= 0xF << 18;
282 char cardtype
[16] = {"T55x7"};
285 blocks
[0] = T5555_FIXED
| T5555_MODULATION_PSK1
| T5555_SET_BITRATE(32) | T5555_PSK_RF_2
| 2 << T5555_MAXBLOCK_SHIFT
;
286 snprintf(cardtype
, sizeof(cardtype
), "Q5/T5555");
290 blocks
[0] = EM4305_KERI_CONFIG_BLOCK
;
291 snprintf(cardtype
, sizeof(cardtype
), "EM4305/4469");
295 // Prepare and write to card
297 uint64_t data
= ((uint64_t)internalid
<< 3) + 7;
298 PrintAndLogEx(INFO
, "Preparing to clone KERI to " _YELLOW_("%s") " with Internal Id " _YELLOW_("%" PRIx32
), cardtype
, internalid
);
300 blocks
[1] = data
>> 32;
301 blocks
[2] = data
& 0xFFFFFFFF;
303 print_blocks(blocks
, ARRAYLEN(blocks
));
307 res
= em4x05_clone_tag(blocks
, ARRAYLEN(blocks
), 0, false);
309 res
= clone_t55xx_tag(blocks
, ARRAYLEN(blocks
));
312 PrintAndLogEx(SUCCESS
, "Done!");
313 PrintAndLogEx(HINT
, "Hint: try " _YELLOW_("`lf keri read`") " to verify");
317 static int CmdKeriSim(const char *Cmd
) {
319 CLIParserContext
*ctx
;
320 CLIParserInit(&ctx
, "lf keri sim",
321 "Enables simulation of KERI card with internal ID.\n"
322 "You supply a KERI card id and it will converted to a KERI internal ID.",
323 "lf keri sim --cn 112233"
328 arg_u64_1(NULL
, "id", "<dec>", "KERI card ID"),
331 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
332 uint64_t internalid
= arg_get_u64_def(ctx
, 1, 0);
335 internalid
|= 0x80000000;
339 uint8_t bs
[64] = {0x00};
342 for (int8_t i
= 63; i
>= 0; --i
) {
343 bs
[j
++] = ((internalid
>> i
) & 1);
346 PrintAndLogEx(SUCCESS
, "Simulating KERI - Internal Id " _YELLOW_("%" PRIu64
), internalid
);
348 lf_psksim_t
*payload
= calloc(1, sizeof(lf_psksim_t
) + sizeof(bs
));
349 payload
->carrier
= 2;
352 memcpy(payload
->data
, bs
, sizeof(bs
));
354 clearCommandBuffer();
355 SendCommandNG(CMD_LF_PSK_SIMULATE
, (uint8_t *)payload
, sizeof(lf_psksim_t
) + sizeof(bs
));
358 PacketResponseNG resp
;
359 WaitForResponse(CMD_LF_PSK_SIMULATE
, &resp
);
361 PrintAndLogEx(INFO
, "Done!");
362 if (resp
.status
!= PM3_EOPABORTED
) {
368 static command_t CommandTable
[] = {
369 {"help", CmdHelp
, AlwaysAvailable
, "This help"},
370 {"demod", CmdKeriDemod
, AlwaysAvailable
, "demodulate an KERI tag from the GraphBuffer"},
371 {"reader", CmdKeriReader
, IfPm3Lf
, "attempt to read and extract tag data"},
372 {"clone", CmdKeriClone
, IfPm3Lf
, "clone KERI tag to T55x7, Q5/T5555 or EM4305/4469"},
373 {"sim", CmdKeriSim
, IfPm3Lf
, "simulate KERI tag"},
374 {NULL
, NULL
, NULL
, NULL
}
377 static int CmdHelp(const char *Cmd
) {
378 (void)Cmd
; // Cmd is not used so far
379 CmdsHelp(CommandTable
);
383 int CmdLFKeri(const char *Cmd
) {
384 clearCommandBuffer();
385 return CmdsParse(CommandTable
, Cmd
);
388 // find KERI preamble in already demoded data
389 int detectKeri(uint8_t *dest
, size_t *size
, bool *invert
) {
391 uint8_t preamble
[] = {1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
394 if (*size
< sizeof(preamble
)) return -1;
397 size_t found_size
= *size
;
399 if (!preambleSearch(dest
, preamble
, sizeof(preamble
), &found_size
, &startIdx
)) {
402 // if didn't find preamble try again inverting
403 uint8_t preamble_i
[] = {0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0};
404 if (!preambleSearch(g_DemodBuffer
, preamble_i
, sizeof(preamble_i
), &found_size
, &startIdx
))
410 if (found_size
< 64) return -3; //wrong demoded size
414 return (int)startIdx
;