Merge pull request #2654 from Antiklesys/master
[RRG-proxmark3.git] / client / src / cmdcrc.c
blob7cbee8d2f551152e555a2dad06bc624dfe91f6c0
1 //-----------------------------------------------------------------------------
2 // Borrowed initially from https://reveng.sourceforge.io/
3 // Copyright (C) Greg Cook 2019
4 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
5 //
6 // This program is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation, either version 3 of the License, or
9 // (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // See LICENSE.txt for the text of the license.
17 //-----------------------------------------------------------------------------
18 // CRC Calculations
19 //-----------------------------------------------------------------------------
20 #include "cmdcrc.h"
22 #include <stdio.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <ctype.h>
27 #ifdef _WIN32
28 # include <io.h>
29 # include <fcntl.h>
30 # ifndef STDIN_FILENO
31 # define STDIN_FILENO 0
32 # endif /* STDIN_FILENO */
33 #endif /* _WIN32 */
35 #include "reveng.h"
36 #include "ui.h"
37 #include "util.h"
38 #include "pm3_cmd.h"
40 #define MAX_ARGS 20
42 static int split(char *str, char *arr[MAX_ARGS]) {
43 int beginIndex = 0;
44 int wordCnt = 0;
46 while (1) {
47 while (isspace(str[beginIndex])) {
48 ++beginIndex;
50 if (str[beginIndex] == '\0') {
51 break;
53 int endIndex = beginIndex;
54 while (str[endIndex] && !isspace(str[endIndex])) {
55 ++endIndex;
57 int len = endIndex - beginIndex;
58 char *tmp = calloc(len + 1, sizeof(char));
59 memcpy(tmp, &str[beginIndex], len);
60 arr[wordCnt++] = tmp;
61 beginIndex = endIndex;
62 if (wordCnt == MAX_ARGS)
63 break;
65 return wordCnt;
68 //returns array of model names and the count of models returning
69 // as well as a width array for the width of each model
70 int GetModels(char *Models[], int *count, uint8_t *width) {
71 /* default values */
72 static model_t model = MZERO;
74 poly_t apoly, crc, qpoly = PZERO, *apolys = NULL, *pptr = NULL, *qptr = NULL;
75 model_t pset = model;
77 /* stdin must be binary */
78 #ifdef _WIN32
79 _setmode(STDIN_FILENO, _O_BINARY);
80 #endif /* _WIN32 */
82 SETBMP();
84 if (width[0] == 0) { //reveng -D
85 *count = mcount();
86 if (!*count) {
87 PrintAndLogEx(WARNING, "no preset models available");
88 return 0;
90 for (int mode = 0; mode < *count; ++mode) {
91 mbynum(&model, mode);
92 mcanon(&model);
93 size_t size = (model.name && *model.name) ? strlen(model.name) : 7;
94 char *tmp = calloc(size + 1, sizeof(char));
95 if (tmp == NULL) {
96 PrintAndLogEx(WARNING, "out of memory?");
97 return 0;
99 if (model.name != NULL) {
100 memcpy(tmp, model.name, size);
101 Models[mode] = tmp;
102 width[mode] = plen(model.spoly);
103 } else {
104 free(tmp);
107 mfree(&model);
108 } else { //reveng -s
110 int ibperhx = 8;//, obperhx = 8;
111 int rflags = 0, uflags = 0; /* search and UI flags */
112 if (~model.flags & P_MULXN) {
113 PrintAndLogEx(WARNING, "cannot search for non-Williams compliant models");
114 return 0;
116 praloc(&model.spoly, (unsigned long)width[0]);
117 praloc(&model.init, (unsigned long)width[0]);
118 praloc(&model.xorout, (unsigned long)width[0]);
120 if (!plen(model.spoly))
121 palloc(&model.spoly, (unsigned long)width[0]);
122 else
123 width[0] = (uint8_t)plen(model.spoly);
125 /* special case if qpoly is zero, search to end of range */
126 if (!ptst(qpoly))
127 rflags &= ~R_HAVEQ;
129 int pass;
131 /* if endianness not specified, try
132 * little-endian then big-endian.
133 * NB: crossed-endian algorithms will not be
134 * searched.
136 /* scan against preset models */
137 if (~uflags & C_NOPCK) {
138 pass = 0;
139 int Cnt = 0;
140 do {
141 int psets = mcount();
143 while (psets) {
144 mbynum(&pset, --psets);
146 /* skip if different width, or refin or refout don't match */
147 if (plen(pset.spoly) != width[0] || (model.flags ^ pset.flags) & (P_REFIN | P_REFOUT))
148 continue;
149 /* skip if the preset doesn't match specified parameters */
150 if (rflags & R_HAVEP && pcmp(&model.spoly, &pset.spoly))
151 continue;
152 if (rflags & R_HAVEI && psncmp(&model.init, &pset.init))
153 continue;
154 if (rflags & R_HAVEX && psncmp(&model.xorout, &pset.xorout))
155 continue;
157 //for additional args (not used yet, maybe future?)
158 apoly = pclone(pset.xorout);
160 if (pset.flags & P_REFOUT)
161 prev(&apoly);
164 for (qptr = apolys; qptr < pptr; ++qptr) {
165 crc = pcrc(*qptr, pset.spoly, pset.init, apoly, 0);
166 if (ptst(crc)) {
167 pfree(&crc);
168 break;
170 pfree(&crc);
172 pfree(&apoly);
174 if (qptr == pptr) {
176 /* the selected model solved all arguments */
177 mcanon(&pset);
179 size_t size = (pset.name && *pset.name) ? strlen(pset.name) : 7;
180 //PrintAndLogEx(NORMAL, "Size: %d, %s, count: %d",size,pset.name, Cnt);
181 char *tmp = calloc(size + 1, sizeof(char));
182 if (tmp == NULL) {
183 PrintAndLogEx(WARNING, "out of memory?");
184 return 0;
186 width[Cnt] = width[0];
187 memcpy(tmp, pset.name, size);
188 Models[Cnt++] = tmp;
189 *count = Cnt;
190 uflags |= C_RESULT;
193 mfree(&pset);
195 /* toggle refIn/refOut and reflect arguments */
196 if (~rflags & R_HAVERI) {
197 model.flags ^= P_REFIN | P_REFOUT;
198 for (qptr = apolys; qptr < pptr; ++qptr) {
199 prevch(qptr, ibperhx);
202 } while (~rflags & R_HAVERI && ++pass < 2);
204 //got everything now free the memory...
206 if (uflags & C_RESULT) {
207 for (qptr = apolys; qptr < pptr; ++qptr) {
208 pfree(qptr);
211 if (uflags & C_NOBFS && ~rflags & R_HAVEP) {
212 PrintAndLogEx(WARNING, "no models found");
213 return 0;
216 if (!(model.flags & P_REFIN) != !(model.flags & P_REFOUT)) {
217 PrintAndLogEx(WARNING, "cannot search for crossed-endian models");
218 return 0;
220 pass = 0;
221 int args = 0;
222 do {
223 model_t *candmods = reveng(&model, qpoly, rflags, args, apolys);
224 model_t *mptr = candmods;
225 if (mptr && plen(mptr->spoly)) {
226 uflags |= C_RESULT;
228 while (mptr && plen(mptr->spoly)) {
229 mfree(mptr++);
231 free(candmods);
232 if (~rflags & R_HAVERI) {
233 model.flags ^= P_REFIN | P_REFOUT;
234 for (qptr = apolys; qptr < pptr; ++qptr) {
235 prevch(qptr, ibperhx);
238 } while (~rflags & R_HAVERI && ++pass < 2);
240 for (qptr = apolys; qptr < pptr; ++qptr) {
241 pfree(qptr);
243 free(apolys);
244 mfree(&model);
246 if (~uflags & C_RESULT) {
247 PrintAndLogEx(WARNING, "no models found");
248 return 0;
251 return 1;
254 //-c || -v
255 //inModel = valid model name string - CRC-8
256 //inHexStr = input hex string to calculate crc on
257 //reverse = reverse calc option if true
258 //endian = {0 = calc default endian input and output, b = big endian input and output, B = big endian output, r = right justified
259 // l = little endian input and output, L = little endian output only, t = left justified}
260 //result = calculated crc hex string
261 int RunModel(char *inModel, char *inHexStr, bool reverse, char endian, char *result) {
262 /* default values */
263 static model_t model = MZERO;
265 int ibperhx = 8, obperhx = 8;
266 // int rflags = 0; // search flags
267 poly_t apoly, crc;
269 char *string;
271 // stdin must be binary
272 #ifdef _WIN32
273 _setmode(STDIN_FILENO, _O_BINARY);
274 #endif /* _WIN32 */
276 SETBMP();
277 //set model
278 int c = mbynam(&model, inModel);
279 if (!c) {
280 PrintAndLogEx(ERR, "error: preset model '%s' not found. Use reveng -D to list presets. [%d]", inModel, c);
281 return 0;
283 if (c < 0) {
284 PrintAndLogEx(WARNING, "no preset models available");
285 return 0;
287 // rflags |= R_HAVEP | R_HAVEI | R_HAVERI | R_HAVERO | R_HAVEX;
289 //set flags
290 switch (endian) {
291 case 'b': /* b big-endian (RefIn = false, RefOut = false ) */
292 model.flags &= ~P_REFIN;
293 //rflags |= R_HAVERI;
294 /* fall through: */
295 case 'B': /* B big-endian output (RefOut = false) */
296 model.flags &= ~P_REFOUT;
297 //rflags |= R_HAVERO;
298 mnovel(&model);
299 /* fall through: */
300 case 'r': /* r right-justified */
301 model.flags |= P_RTJUST;
302 break;
303 case 'l': /* l little-endian input and output */
304 model.flags |= P_REFIN;
305 //rflags |= R_HAVERI;
306 /* fall through: */
307 case 'L': /* L little-endian output */
308 model.flags |= P_REFOUT;
309 //rflags |= R_HAVERO;
310 mnovel(&model);
311 /* fall through: */
312 case 't': /* t left-justified */
313 model.flags &= ~P_RTJUST;
314 break;
316 /* canonicalise the model, so the one we dump is the one we
317 * calculate with (not with -s, spoly may be blank which will
318 * normalise to zero and clear init and xorout.)
320 mcanon(&model);
323 if (reverse) {
324 // v calculate reversed CRC
325 /* Distinct from the -V switch as this causes
326 * the arguments and output to be reversed as well.
328 // reciprocate Poly
329 prcp(&model.spoly);
331 /* mrev() does:
332 * if(refout) prev(init); else prev(xorout);
333 * but here the entire argument polynomial is
334 * reflected, not just the characters, so RefIn
335 * and RefOut are not inverted as with -V.
336 * Consequently Init is the mirror image of the
337 * one resulting from -V, and so we have:
339 if (~model.flags & P_REFOUT) {
340 prev(&model.init);
341 prev(&model.xorout);
344 // swap init and xorout
345 apoly = model.init;
346 model.init = model.xorout;
347 model.xorout = apoly;
349 // c calculate CRC
351 /* in the Williams model, xorout is applied after the refout stage.
352 * as refout is part of ptostr(), we reverse xorout here.
354 if (model.flags & P_REFOUT)
355 prev(&model.xorout);
357 apoly = strtop(inHexStr, model.flags, ibperhx);
359 if (reverse)
360 prev(&apoly);
362 crc = pcrc(apoly, model.spoly, model.init, model.xorout, model.flags);
364 if (reverse)
365 prev(&crc);
367 string = ptostr(crc, model.flags, obperhx);
368 for (int i = 0; i < 50; i++) {
369 result[i] = string[i];
370 if (result[i] == 0) break;
372 free(string);
373 pfree(&crc);
374 pfree(&apoly);
375 return 1;
378 //test call to RunModel
379 static int CmdrevengTestC(const char *Cmd) {
380 int cmdp = 0;
381 char inModel[30] = {0x00};
382 char inHexStr[30] = {0x00};
383 char result[30];
384 int dataLen;
385 char endian = 0;
386 dataLen = param_getstr(Cmd, cmdp++, inModel, sizeof(inModel));
387 if (dataLen < 4) return 0;
388 dataLen = param_getstr(Cmd, cmdp++, inHexStr, sizeof(inHexStr));
389 if (dataLen < 4) return 0;
390 bool reverse = (param_get8(Cmd, cmdp++)) ? true : false;
391 endian = param_getchar(Cmd, cmdp++);
393 //PrintAndLogEx(NORMAL, "mod: %s, hex: %s, rev %d", inModel, inHexStr, reverse);
394 int ans = RunModel(inModel, inHexStr, reverse, endian, result);
395 if (!ans) return 0;
397 PrintAndLogEx(SUCCESS, "result: %s", result);
398 return 1;
401 //returns a calloced string (needs to be freed)
402 static char *SwapEndianStr(const char *inStr, const size_t len, const uint8_t blockSize) {
403 char *tmp = calloc(len + 1, sizeof(char));
404 for (uint8_t block = 0; block < (uint8_t)(len / blockSize); block++) {
405 for (size_t i = 0; i < blockSize; i += 2) {
406 tmp[i + (blockSize * block)] = inStr[(blockSize - 1 - i - 1) + (blockSize * block)];
407 tmp[i + (blockSize * block) + 1] = inStr[(blockSize - 1 - i) + (blockSize * block)];
410 return tmp;
413 // takes hex string in and searches for a matching result (hex string must include checksum)
414 static int CmdrevengSearch(const char *Cmd) {
416 #define NMODELS 106
418 char inHexStr[256] = {0x00};
419 int dataLen = param_getstr(Cmd, 0, inHexStr, sizeof(inHexStr));
420 if (dataLen < 4) return 0;
422 // these two arrays, must match preset size.
423 char *Models[NMODELS];
424 uint8_t width[NMODELS] = {0};
425 int count = 0;
427 char result[50 + 1] = {0};
428 char revResult[50 + 1] = {0};
429 int ans = GetModels(Models, &count, width);
430 bool found = false;
431 if (!ans) {
432 for (int i = 0; i < count; i++) {
433 free(Models[i]);
435 return 0;
438 str_lower(inHexStr);
440 // try each model and get result
441 for (int i = 0; i < count; i++) {
442 /*if (found) {
443 free(Models[i]);
444 continue;
446 // round up to # of characters in this model's crc
447 uint8_t crcChars = ((width[i] + 7) / 8) * 2;
448 // can't test a model that has more crc digits than our data
449 if (crcChars >= dataLen) {
450 free(Models[i]);
451 continue;
454 PrintAndLogEx(DEBUG
455 , "DEBUG: dataLen %d, crcChars %u, width[i] %u"
456 , dataLen
457 , crcChars
458 , width[i]
461 if (crcChars == 0) {
462 free(Models[i]);
463 continue;
466 memset(result, 0, sizeof(result));
467 char *inCRC = calloc(crcChars + 1, sizeof(char));
468 if (inCRC == NULL) {
469 return 0;
472 memcpy(inCRC, inHexStr + (dataLen - crcChars), crcChars);
474 char *outHex = calloc(dataLen - crcChars + 1, sizeof(char));
475 if (outHex == NULL) {
476 free(inCRC);
477 return 0;
480 memcpy(outHex, inHexStr, dataLen - crcChars);
482 ans = RunModel(Models[i], outHex, false, 0, result);
483 if (ans) {
485 str_lower(result);
487 // test for match
488 if (memcmp(result, inCRC, crcChars) == 0) {
489 PrintAndLogEx(SUCCESS, "model... " _YELLOW_("%s"), Models[i]);
490 PrintAndLogEx(SUCCESS, "value... %s\n", result);
491 //optional - stop searching if found...
492 found = true;
493 } else {
494 if (crcChars > 2) {
495 char *swapEndian = SwapEndianStr(result, crcChars, crcChars);
496 if (memcmp(swapEndian, inCRC, crcChars) == 0) {
497 PrintAndLogEx(SUCCESS, "model... " _YELLOW_("%s"), Models[i]);
498 PrintAndLogEx(SUCCESS, "value endian swapped... %s\n", swapEndian);
499 // optional - stop searching if found...
500 found = true;
502 free(swapEndian);
506 ans = RunModel(Models[i], outHex, true, 0, revResult);
507 if (ans) {
508 str_lower(revResult);
510 // test for match
511 if (memcmp(revResult, inCRC, crcChars) == 0) {
512 PrintAndLogEx(SUCCESS, "model reversed... " _YELLOW_("%s"), Models[i]);
513 PrintAndLogEx(SUCCESS, "value... %s\n", revResult);
514 // optional - stop searching if found...
515 found = true;
516 } else {
517 if (crcChars > 2) {
518 char *swapEndian = SwapEndianStr(revResult, crcChars, crcChars);
519 if (memcmp(swapEndian, inCRC, crcChars) == 0) {
520 PrintAndLogEx(SUCCESS, "model reversed... " _YELLOW_("%s"), Models[i]);
521 PrintAndLogEx(SUCCESS, "value endian swapped... %s\n", swapEndian);
522 // optional - stop searching if found...
523 found = true;
525 free(swapEndian);
529 free(inCRC);
530 free(outHex);
531 free(Models[i]);
534 if (found == false) {
535 PrintAndLogEx(FAILED, "\nno matches found\n");
537 return PM3_SUCCESS;
540 int CmdCrc(const char *Cmd) {
541 char c[100 + 7];
542 snprintf(c, sizeof(c), "reveng ");
543 snprintf(c + strlen(c), sizeof(c) - strlen(c), Cmd, strlen(Cmd));
545 char *argv[MAX_ARGS];
546 int argc = split(c, argv);
548 if (argc == 3 && memcmp(argv[1], "-g", 2) == 0) {
549 CmdrevengSearch(argv[2]);
550 } else {
551 reveng_main(argc, argv);
554 for (int i = 0; i < argc; ++i) {
555 free(argv[i]);
557 return PM3_SUCCESS;