style
[RRG-proxmark3.git] / client / src / cmdlfkeri.c
blob16f7d07c906891bede4b828f7fab612225b3196a
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 KERI tag commands
17 // PSK1, RF/128, RF/2, 64 bits long
18 //-----------------------------------------------------------------------------
19 #include "cmdlfkeri.h"
20 #include <string.h>
21 #include <inttypes.h>
22 #include <ctype.h>
23 #include <stdlib.h>
24 #include "commonutil.h" // ARRAYLEN
25 #include "cmdparser.h" // command_t
26 #include "cliparser.h"
27 #include "comms.h"
28 #include "ui.h"
29 #include "cmddata.h"
30 #include "cmdlf.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) {
52 *FC = 0;
53 *ID = 0;
54 for (card_idx = 0; card_idx < 32; card_idx++) {
55 // Get Bit State
56 bool BitState = (*CardID >> card_idx) & 1;
57 // Card ID
58 if (CardToID[card_idx] < 32) {
59 *ID = *ID | (BitState << CardToID[card_idx]);
61 // Card FC
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++) {
72 // Card ID
73 if (CardToID[card_idx] < 32) {
74 if ((*ID & (1U << CardToID[card_idx])) > 0)
75 *CardID |= (1U << card_idx);
77 // Card FC
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
91 *CardID |= (1 << 3);
93 // Check/Parity Bits
94 int parity = 1;
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);
105 return PM3_SUCCESS;
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");
113 return PM3_ESOFT;
116 bool invert = false;
117 size_t size = g_DemodBufferLen;
118 int idx = detectKeri(g_DemodBuffer, &size, &invert);
119 if (idx < 0) {
120 if (idx == -1)
121 PrintAndLogEx(DEBUG, "DEBUG: Error - KERI: too few bits found");
122 else if (idx == -2)
123 PrintAndLogEx(DEBUG, "DEBUG: Error - KERI: preamble not found");
124 else if (idx == -3)
125 PrintAndLogEx(DEBUG, "DEBUG: Error - KERI: Size not correct: 64 != %zu", size);
126 else
127 PrintAndLogEx(DEBUG, "DEBUG: Error - KERI: ans: %d", idx);
129 return PM3_ESOFT;
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
150 Descramble Data.
152 uint32_t fc = 0;
153 uint32_t cardid = 0;
154 //got a good demod
155 uint32_t raw1 = bytebits_to_byte(g_DemodBuffer, 32);
156 uint32_t raw2 = bytebits_to_byte(g_DemodBuffer + 32, 32);
158 if (invert) {
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");
169 //get internal id
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.
173 uint32_t ID = raw2;
174 ID &= 0x7FFFFFFF;
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);
182 return PM3_SUCCESS;
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",
189 "lf keri demod"
192 void *argtable[] = {
193 arg_param_begin,
194 arg_param_end
196 CLIExecWithReturn(ctx, Cmd, argtable, true);
197 CLIParserFree(ctx);
198 return demodKeri(true);
201 static int CmdKeriReader(const char *Cmd) {
202 CLIParserContext *ctx;
203 CLIParserInit(&ctx, "lf keri reader",
204 "read a keri tag",
205 "lf keri reader -@ -> continuous reader mode"
208 void *argtable[] = {
209 arg_param_begin,
210 arg_lit0("@", NULL, "optional - continuous reader mode"),
211 arg_param_end
213 CLIExecWithReturn(ctx, Cmd, argtable, true);
214 bool cm = arg_get_lit(ctx, 1);
215 CLIParserFree(ctx);
217 if (cm) {
218 PrintAndLogEx(INFO, "Press " _GREEN_("<Enter>") " to exit");
221 do {
222 lf_read(false, 10000);
223 demodKeri(!cm);
224 } while (cm && !kbd_enter_pressed());
226 return PM3_SUCCESS;
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");
236 void *argtable[] = {
237 arg_param_begin,
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"),
243 arg_param_end
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);
255 CLIParserFree(ctx);
257 if (q5 && em) {
258 PrintAndLogEx(FAILED, "Can't specify both Q5 and EM4305 at the same time");
259 return PM3_EINVARG;
262 // Setup card data/build internal id
263 uint32_t internalid = 0;
264 switch (keritype[0]) {
265 case 'i' : // Internal ID
266 // MSB is ONE
267 internalid = cid | 0x80000000;
268 break;
269 case 'm' : // MS
270 CmdKeriMSScramble(Scramble, &fc, &cid, &internalid);
271 break;
272 default :
273 PrintAndLogEx(ERR, "Invalid type");
274 return PM3_EINVARG;
277 uint32_t blocks[3];
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"};
284 if (q5) {
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");
289 if (em) {
290 blocks[0] = EM4305_KERI_CONFIG_BLOCK;
291 snprintf(cardtype, sizeof(cardtype), "EM4305/4469");
295 // Prepare and write to card
296 // 3 LSB is ONE
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));
305 int res;
306 if (em) {
307 res = em4x05_clone_tag(blocks, ARRAYLEN(blocks), 0, false);
308 } else {
309 res = clone_t55xx_tag(blocks, ARRAYLEN(blocks));
312 PrintAndLogEx(SUCCESS, "Done!");
313 PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf keri read`") " to verify");
314 return res;
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"
326 void *argtable[] = {
327 arg_param_begin,
328 arg_u64_1(NULL, "id", "<dec>", "KERI card ID"),
329 arg_param_end
331 CLIExecWithReturn(ctx, Cmd, argtable, false);
332 uint64_t internalid = arg_get_u64_def(ctx, 1, 0);
333 CLIParserFree(ctx);
335 internalid |= 0x80000000;
336 internalid <<= 3;
337 internalid += 7;
339 uint8_t bs[64] = {0x00};
340 // loop to bits
341 uint8_t j = 0;
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;
350 payload->invert = 0;
351 payload->clock = 32;
352 memcpy(payload->data, bs, sizeof(bs));
354 clearCommandBuffer();
355 SendCommandNG(CMD_LF_PSK_SIMULATE, (uint8_t *)payload, sizeof(lf_psksim_t) + sizeof(bs));
356 free(payload);
358 PacketResponseNG resp;
359 WaitForResponse(CMD_LF_PSK_SIMULATE, &resp);
361 PrintAndLogEx(INFO, "Done!");
362 if (resp.status != PM3_EOPABORTED) {
363 return resp.status;
365 return PM3_SUCCESS;
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);
380 return PM3_SUCCESS;
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};
393 // sanity check.
394 if (*size < sizeof(preamble)) return -1;
396 size_t startIdx = 0;
397 size_t found_size = *size;
399 if (!preambleSearch(dest, preamble, sizeof(preamble), &found_size, &startIdx)) {
401 found_size = *size;
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))
405 return -2;
407 *invert ^= 1;
410 if (found_size < 64) return -3; //wrong demoded size
412 *size = found_size;
414 return (int)startIdx;