Merge pull request #2672 from kitsunehunter/laundry-keys
[RRG-proxmark3.git] / client / deps / reveng / reveng.c
blobf36ccc2f44aa63307c87e0a4a6a1f05302269dd8
1 /* reveng.c
2 * Greg Cook, 23/Feb/2019
3 */
5 /* CRC RevEng: arbitrary-precision CRC calculator and algorithm finder
6 * Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
7 * 2019 Gregory Cook
9 * This file is part of CRC RevEng.
11 * CRC RevEng is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation, either version 3 of the License, or
14 * (at your option) any later version.
16 * CRC RevEng is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with CRC RevEng. If not, see <https://www.gnu.org/licenses/>.
25 /* 2013-09-16: calini(), calout() work on shortest argument
26 * 2013-06-11: added sequence number to uprog() calls
27 * 2013-02-08: added polynomial range search
28 * 2013-01-18: refactored model checking to pshres(); renamed chkres()
29 * 2012-05-24: efficiently build Init contribution string
30 * 2012-05-24: removed broken search for crossed-endian algorithms
31 * 2012-05-23: rewrote engini() after Ewing; removed modini()
32 * 2011-01-17: fixed ANSI C warnings
33 * 2011-01-08: fixed calini(), modini() caters for crossed-endian algos
34 * 2011-01-04: renamed functions, added calini(), factored pshres();
35 * rewrote engini() and implemented quick Init search
36 * 2011-01-01: reveng() initialises terminating entry, addparms()
37 * initialises all fields
38 * 2010-12-26: renamed CRC RevEng. right results, rejects polys faster
39 * 2010-12-24: completed, first tests (unsuccessful)
40 * 2010-12-21: completed modulate(), partial sketch of reveng()
41 * 2010-12-19: started reveng
44 /* reveng() can in theory be modified to search for polynomials shorter
45 * than the full width as well, but this imposes a heavy time burden on
46 * the full width search, which is the primary use case, as well as
47 * complicating the search range function introduced in version 1.1.0.
48 * It is more effective to search for each shorter width directly.
51 #include <stdlib.h>
53 #define FILE void
54 #include "reveng.h"
56 static poly_t *modpol(const poly_t init, int rflags, int args, const poly_t *argpolys);
57 static void engini(int *resc, model_t **result, const poly_t divisor, int flags, int args, const poly_t *argpolys);
58 static void calout(int *resc, model_t **result, const poly_t divisor, const poly_t init, int flags, int args, const poly_t *argpolys);
59 static void calini(int *resc, model_t **result, const poly_t divisor, int flags, const poly_t xorout, int args, const poly_t *argpolys);
60 static void chkres(int *resc, model_t **result, const poly_t divisor, const poly_t init, int flags, const poly_t xorout, int args, const poly_t *argpolys);
62 static const poly_t pzero = PZERO;
64 model_t *
65 reveng(const model_t *guess, const poly_t qpoly, int rflags, int args, const poly_t *argpolys) {
66 /* Complete the parameters of a model by calculation or brute search. */
67 poly_t *pworks, *wptr, rem, gpoly;
68 model_t *result = NULL, *rptr;
69 int resc = 0;
70 unsigned long spin = 0, seq = 0;
72 if (~rflags & R_HAVEP) {
73 /* The poly is not known.
74 * Produce a list of differences between the arguments.
76 pworks = modpol(guess->init, rflags, args, argpolys);
77 if (!pworks || !plen(*pworks)) {
78 free(pworks);
79 goto requit;
81 /* Initialise the guessed poly to the starting value. */
82 gpoly = pclone(guess->spoly);
83 /* Clear the least significant term, to be set in the
84 * loop. qpoly does not need fixing as it is only
85 * compared with odd polys.
87 if (plen(gpoly))
88 pshift(&gpoly, gpoly, 0UL, 0UL, plen(gpoly) - 1UL, 1UL);
90 while (piter(&gpoly) && (~rflags & R_HAVEQ || pcmp(&gpoly, &qpoly) < 0)) {
91 /* For each possible poly of this size, try
92 * dividing all the differences in the list.
94 if (!(spin++ & R_SPMASK)) {
95 uprog(gpoly, guess->flags, seq++);
97 for (wptr = pworks; plen(*wptr); ++wptr) {
98 /* straight divide message by poly, don't multiply by x^n */
99 rem = pcrc(*wptr, gpoly, pzero, pzero, 0);
100 if (ptst(rem)) {
101 pfree(&rem);
102 break;
103 } else
104 pfree(&rem);
106 /* If gpoly divides all the differences, it is a
107 * candidate. Search for an Init value for this
108 * poly or if Init is known, log the result.
110 if (!plen(*wptr)) {
111 /* gpoly is a candidate poly */
112 if (rflags & R_HAVEI && rflags & R_HAVEX)
113 chkres(&resc, &result, gpoly, guess->init, guess->flags, guess->xorout, args, argpolys);
114 else if (rflags & R_HAVEI)
115 calout(&resc, &result, gpoly, guess->init, guess->flags, args, argpolys);
116 else if (rflags & R_HAVEX)
117 calini(&resc, &result, gpoly, guess->flags, guess->xorout, args, argpolys);
118 else
119 engini(&resc, &result, gpoly, guess->flags, args, argpolys);
121 if (!piter(&gpoly))
122 break;
124 /* Finished with gpoly and the differences list, free them.
126 pfree(&gpoly);
127 for (wptr = pworks; plen(*wptr); ++wptr)
128 pfree(wptr);
129 free(pworks);
130 } else if (rflags & R_HAVEI && rflags & R_HAVEX)
131 /* All parameters are known! Submit the result if we get here */
132 chkres(&resc, &result, guess->spoly, guess->init, guess->flags, guess->xorout, args, argpolys);
133 else if (rflags & R_HAVEI)
134 /* Poly and Init are known, calculate XorOut */
135 calout(&resc, &result, guess->spoly, guess->init, guess->flags, args, argpolys);
136 else if (rflags & R_HAVEX)
137 /* Poly and XorOut are known, calculate Init */
138 calini(&resc, &result, guess->spoly, guess->flags, guess->xorout, args, argpolys);
139 else
140 /* Poly is known but not Init; search for Init. */
141 engini(&resc, &result, guess->spoly, guess->flags, args, argpolys);
143 requit:
144 if (!(result = realloc(result, ++resc * sizeof(model_t)))) {
145 uerror("cannot reallocate result array");
146 return NULL;
148 rptr = result + resc - 1;
149 rptr->spoly = pzero;
150 rptr->init = pzero;
151 rptr->flags = 0;
152 rptr->xorout = pzero;
153 rptr->check = pzero;
154 rptr->magic = pzero;
155 rptr->name = NULL;
157 return (result);
160 static poly_t *
161 modpol(const poly_t init, int rflags, int args, const poly_t *argpolys) {
162 /* Produce, in ascending length order, a list of differences
163 * between the arguments in the list by summing pairs of arguments.
164 * If R_HAVEI is not set in rflags, only pairs of equal length are
165 * summed.
166 * Otherwise, sums of right-aligned pairs are also returned, with
167 * the supplied init poly added to the leftmost terms of each
168 * poly of the pair.
170 poly_t work, swap, *result, *rptr, *iptr;
171 const poly_t *aptr, *bptr, *eptr = argpolys + args;
172 unsigned long alen, blen;
174 if (args < 2) return (NULL);
176 result = calloc(((((args - 1) * args) >> 1) + 1) * sizeof(poly_t), sizeof(char));
177 if (!result)
178 uerror("cannot allocate memory for codeword table");
180 rptr = result;
182 for (aptr = argpolys; aptr < eptr; ++aptr) {
183 alen = plen(*aptr);
184 for (bptr = aptr + 1; bptr < eptr; ++bptr) {
185 blen = plen(*bptr);
186 if (alen == blen) {
187 work = pclone(*aptr);
188 psum(&work, *bptr, 0UL);
189 } else if (rflags & R_HAVEI && alen < blen) {
190 work = pclone(*bptr);
191 psum(&work, *aptr, blen - alen);
192 psum(&work, init, 0UL);
193 psum(&work, init, blen - alen);
194 } else if (rflags & R_HAVEI /* && alen > blen */) {
195 work = pclone(*aptr);
196 psum(&work, *bptr, alen - blen);
197 psum(&work, init, 0UL);
198 psum(&work, init, alen - blen);
199 } else
200 work = pzero;
202 if (plen(work))
203 pnorm(&work);
204 if ((blen = plen(work))) {
205 /* insert work into result[] in ascending order of length */
206 for (iptr = result; iptr < rptr; ++iptr) {
207 if (plen(work) < plen(*iptr)) {
208 swap = *iptr;
209 *iptr = work;
210 work = swap;
211 } else if (plen(*iptr) == blen && !pcmp(&work, iptr)) {
212 pfree(&work);
213 work = *--rptr;
214 break;
217 *rptr++ = work;
221 *rptr = pzero;
222 return (result);
225 static void
226 engini(int *resc, model_t **result, const poly_t divisor, int flags, int args, const poly_t *argpolys) {
227 /* Search for init values implied by the arguments.
228 * Method from: Ewing, Gregory C. (March 2010).
229 * "Reverse-Engineering a CRC Algorithm". Christchurch:
230 * University of Canterbury.
231 * <http://www.cosc.canterbury.ac.nz/greg.ewing/essays/
232 * CRC-Reverse-Engineering.html>
234 poly_t apoly = PZERO, bpoly, pone = PZERO, *mat, *jptr;
235 const poly_t *aptr, *bptr, *iptr;
236 unsigned long alen, blen, dlen, ilen, i, j;
237 int cy;
239 dlen = plen(divisor);
241 /* Allocate the CRC matrix */
242 mat = (poly_t *) calloc((dlen << 1) * sizeof(poly_t), sizeof(char));
243 if (!mat)
244 uerror("cannot allocate memory for CRC matrix");
246 /* Find arguments of the two shortest lengths */
247 alen = blen = plen(*(aptr = bptr = iptr = argpolys));
248 for (++iptr; iptr < argpolys + args; ++iptr) {
249 ilen = plen(*iptr);
250 if (ilen < alen) {
251 bptr = aptr;
252 blen = alen;
253 aptr = iptr;
254 alen = ilen;
255 } else if (ilen > alen && (aptr == bptr || ilen < blen)) {
256 bptr = iptr;
257 blen = ilen;
260 if (aptr == bptr) {
261 /* if no arguments are suitable, calculate Init with an
262 * assumed XorOut of 0. Create a padded XorOut
264 palloc(&apoly, dlen);
265 calini(resc, result, divisor, flags, apoly, args, argpolys);
266 pfree(&apoly);
267 free(mat);
268 return;
271 /* Find the potential contribution of the bottom bit of Init */
272 palloc(&pone, 1UL);
273 piter(&pone);
274 if (blen < (dlen << 1)) {
275 palloc(&apoly, dlen); /* >= 1 */
276 psum(&apoly, pone, (dlen << 1) - 1UL - blen); /* >= 0 */
277 psum(&apoly, pone, (dlen << 1) - 1UL - alen); /* >= 1 */
278 } else {
279 palloc(&apoly, blen - dlen + 1UL); /* > dlen */
280 psum(&apoly, pone, 0UL);
281 psum(&apoly, pone, blen - alen); /* >= 1 */
283 if (plen(apoly) > dlen) {
284 mat[dlen] = pcrc(apoly, divisor, pzero, pzero, 0);
285 pfree(&apoly);
286 } else {
287 mat[dlen] = apoly;
290 /* Find the actual contribution of Init */
291 apoly = pcrc(*aptr, divisor, pzero, pzero, 0);
292 bpoly = pcrc(*bptr, divisor, pzero, apoly, 0);
294 /* Populate the matrix */
295 palloc(&apoly, 1UL);
296 for (jptr = mat; jptr < mat + dlen; ++jptr)
297 *jptr = pzero;
298 for (iptr = jptr++; jptr < mat + (dlen << 1); iptr = jptr++)
299 * jptr = pcrc(apoly, divisor, *iptr, pzero, P_MULXN);
300 pfree(&apoly);
302 /* Transpose the matrix, augment with the Init contribution
303 * and convert to row echelon form
305 for (i = 0UL; i < dlen; ++i) {
306 apoly = pzero;
307 iptr = mat + (dlen << 1);
308 for (j = 0UL; j < dlen; ++j)
309 ppaste(&apoly, *--iptr, i, j, j + 1UL, dlen + 1UL);
310 if (ptst(apoly))
311 ppaste(&apoly, bpoly, i, dlen, dlen + 1UL, dlen + 1UL);
312 j = pfirst(apoly);
313 while (j < dlen && !pident(mat[j], pzero)) {
314 psum(&apoly, mat[j], 0UL); /* pfirst(apoly) > j */
315 j = pfirst(apoly);
317 if (j < dlen)
318 mat[j] = apoly; /* pident(mat[j], pzero) || pfirst(mat[j]) == j */
319 else
320 pfree(&apoly);
322 palloc(&bpoly, dlen + 1UL);
323 psum(&bpoly, pone, dlen);
325 /* Iterate through all solutions */
326 do {
327 /* Solve the matrix by Gaussian elimination.
328 * The parity of the result, masked by each row, should be even.
330 cy = 1;
331 apoly = pclone(bpoly);
332 jptr = mat + dlen;
333 for (i = 0UL; i < dlen; ++i) {
334 /* Compute next bit of Init */
335 if (pmpar(apoly, *--jptr))
336 psum(&apoly, pone, dlen - 1UL - i);
337 /* Toggle each zero row with carry, for next iteration */
338 if (cy) {
339 if (pident(*jptr, pzero)) {
340 /* 0 to 1, no carry */
341 *jptr = bpoly;
342 cy = 0;
343 } else if (pident(*jptr, bpoly)) {
344 /* 1 to 0, carry forward */
345 *jptr = pzero;
350 /* Trim the augment mask bit */
351 praloc(&apoly, dlen);
353 /* Test the Init value and add to results if correct */
354 calout(resc, result, divisor, apoly, flags, args, argpolys);
355 pfree(&apoly);
356 } while (!cy);
357 pfree(&pone);
358 pfree(&bpoly);
360 /* Free the matrix. */
361 for (jptr = mat; jptr < mat + (dlen << 1); ++jptr)
362 pfree(jptr);
363 free(mat);
366 static void
367 calout(int *resc, model_t **result, const poly_t divisor, const poly_t init, int flags, int args, const poly_t *argpolys) {
368 /* Calculate Xorout, check it against all the arguments and
369 * add to results if consistent.
371 poly_t xorout;
372 const poly_t *aptr, *iptr;
373 unsigned long alen, ilen;
375 if (args < 1) return;
377 /* find argument of the shortest length */
378 alen = plen(*(aptr = iptr = argpolys));
379 for (++iptr; iptr < argpolys + args; ++iptr) {
380 ilen = plen(*iptr);
381 if (ilen < alen) {
382 aptr = iptr;
383 alen = ilen;
387 xorout = pcrc(*aptr, divisor, init, pzero, 0);
388 /* On little-endian algorithms, the calculations yield
389 * the reverse of the actual xorout: in the Williams
390 * model, the refout stage intervenes between init and
391 * xorout.
393 if (flags & P_REFOUT)
394 prev(&xorout);
396 /* Submit the model to the results table.
397 * Could skip the shortest argument but we wish to check our
398 * calculation.
400 chkres(resc, result, divisor, init, flags, xorout, args, argpolys);
401 pfree(&xorout);
404 static void
405 calini(int *resc, model_t **result, const poly_t divisor, int flags, const poly_t xorout, int args, const poly_t *argpolys) {
406 /* Calculate Init, check it against all the arguments and add to
407 * results if consistent.
409 poly_t rcpdiv, rxor, arg, init;
410 const poly_t *aptr, *iptr;
411 unsigned long alen, ilen;
413 if (args < 1) return;
415 /* find argument of the shortest length */
416 alen = plen(*(aptr = iptr = argpolys));
417 for (++iptr; iptr < argpolys + args; ++iptr) {
418 ilen = plen(*iptr);
419 if (ilen < alen) {
420 aptr = iptr;
421 alen = ilen;
425 rcpdiv = pclone(divisor);
426 prcp(&rcpdiv);
427 /* If the algorithm is reflected, an ordinary CRC requires the
428 * model's XorOut to be reversed, as XorOut follows the RefOut
429 * stage. To reverse the CRC calculation we need rxor to be the
430 * mirror image of the forward XorOut.
432 rxor = pclone(xorout);
433 if (~flags & P_REFOUT)
434 prev(&rxor);
435 arg = pclone(*aptr);
436 prev(&arg);
438 init = pcrc(arg, rcpdiv, rxor, pzero, 0);
439 pfree(&arg);
440 pfree(&rxor);
441 pfree(&rcpdiv);
442 prev(&init);
444 /* Submit the model to the results table.
445 * Could skip the shortest argument but we wish to check our
446 * calculation.
448 chkres(resc, result, divisor, init, flags, xorout, args, argpolys);
449 pfree(&init);
452 static void
453 chkres(int *resc, model_t **result, const poly_t divisor, const poly_t init, int flags, const poly_t xorout, int args, const poly_t *argpolys) {
454 /* Checks a model against the argument list, and adds to the
455 * external results table if consistent.
456 * Extends the result array and updates the external pointer if
457 * necessary.
459 model_t *rptr;
460 poly_t xor, crc;
461 const poly_t *aptr = argpolys, *const eptr = argpolys + args;
463 /* If the algorithm is reflected, an ordinary CRC requires the
464 * model's XorOut to be reversed, as XorOut follows the RefOut
465 * stage.
467 xor = pclone(xorout);
468 if (flags & P_REFOUT)
469 prev(&xor);
471 for (; aptr < eptr; ++aptr) {
472 crc = pcrc(*aptr, divisor, init, xor, 0);
473 if (ptst(crc)) {
474 pfree(&crc);
475 break;
476 } else {
477 pfree(&crc);
480 pfree(&xor);
481 if (aptr != eptr) return;
483 *result = realloc(*result, ++*resc * sizeof(model_t));
484 if (!*result) {
485 uerror("cannot reallocate result array");
486 return;
489 rptr = *result + *resc - 1;
490 rptr->spoly = pclone(divisor);
491 rptr->init = pclone(init);
492 rptr->flags = flags;
493 rptr->xorout = pclone(xorout);
494 rptr->check = pzero;
495 rptr->magic = pzero;
496 rptr->name = NULL;
498 /* compute check value for this model */
499 mcheck(rptr);
501 /* callback to notify new model */
502 ufound(rptr);