2 * Greg Cook, 23/Feb/2019
5 /* CRC RevEng: arbitrary-precision CRC calculator and algorithm finder
6 * Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
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.
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
;
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
;
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
)) {
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.
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);
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.
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
);
119 engini(&resc
, &result
, gpoly
, guess
->flags
, args
, argpolys
);
124 /* Finished with gpoly and the differences list, free them.
127 for (wptr
= pworks
; plen(*wptr
); ++wptr
)
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
);
140 /* Poly is known but not Init; search for Init. */
141 engini(&resc
, &result
, guess
->spoly
, guess
->flags
, args
, argpolys
);
144 if (!(result
= realloc(result
, ++resc
* sizeof(model_t
)))) {
145 uerror("cannot reallocate result array");
148 rptr
= result
+ resc
- 1;
152 rptr
->xorout
= pzero
;
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
166 * Otherwise, sums of right-aligned pairs are also returned, with
167 * the supplied init poly added to the leftmost terms of each
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));
178 uerror("cannot allocate memory for codeword table");
182 for (aptr
= argpolys
; aptr
< eptr
; ++aptr
) {
184 for (bptr
= aptr
+ 1; bptr
< eptr
; ++bptr
) {
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
);
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
)) {
211 } else if (plen(*iptr
) == blen
&& !pcmp(&work
, iptr
)) {
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
;
239 dlen
= plen(divisor
);
241 /* Allocate the CRC matrix */
242 mat
= (poly_t
*) calloc((dlen
<< 1) * sizeof(poly_t
), sizeof(char));
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
) {
255 } else if (ilen
> alen
&& (aptr
== bptr
|| ilen
< blen
)) {
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
);
271 /* Find the potential contribution of the bottom bit of Init */
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 */
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);
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 */
296 for (jptr
= mat
; jptr
< mat
+ dlen
; ++jptr
)
298 for (iptr
= jptr
++; jptr
< mat
+ (dlen
<< 1); iptr
= jptr
++)
299 * jptr
= pcrc(apoly
, divisor
, *iptr
, pzero
, P_MULXN
);
302 /* Transpose the matrix, augment with the Init contribution
303 * and convert to row echelon form
305 for (i
= 0UL; i
< dlen
; ++i
) {
307 iptr
= mat
+ (dlen
<< 1);
308 for (j
= 0UL; j
< dlen
; ++j
)
309 ppaste(&apoly
, *--iptr
, i
, j
, j
+ 1UL, dlen
+ 1UL);
311 ppaste(&apoly
, bpoly
, i
, dlen
, dlen
+ 1UL, dlen
+ 1UL);
313 while (j
< dlen
&& !pident(mat
[j
], pzero
)) {
314 psum(&apoly
, mat
[j
], 0UL); /* pfirst(apoly) > j */
318 mat
[j
] = apoly
; /* pident(mat[j], pzero) || pfirst(mat[j]) == j */
322 palloc(&bpoly
, dlen
+ 1UL);
323 psum(&bpoly
, pone
, dlen
);
325 /* Iterate through all solutions */
327 /* Solve the matrix by Gaussian elimination.
328 * The parity of the result, masked by each row, should be even.
331 apoly
= pclone(bpoly
);
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 */
339 if (pident(*jptr
, pzero
)) {
340 /* 0 to 1, no carry */
343 } else if (pident(*jptr
, bpoly
)) {
344 /* 1 to 0, carry forward */
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
);
360 /* Free the matrix. */
361 for (jptr
= mat
; jptr
< mat
+ (dlen
<< 1); ++jptr
)
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.
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
) {
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
393 if (flags
& P_REFOUT
)
396 /* Submit the model to the results table.
397 * Could skip the shortest argument but we wish to check our
400 chkres(resc
, result
, divisor
, init
, flags
, xorout
, args
, argpolys
);
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
) {
425 rcpdiv
= pclone(divisor
);
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
)
438 init
= pcrc(arg
, rcpdiv
, rxor
, pzero
, 0);
444 /* Submit the model to the results table.
445 * Could skip the shortest argument but we wish to check our
448 chkres(resc
, result
, divisor
, init
, flags
, xorout
, args
, argpolys
);
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
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
467 xor = pclone(xorout
);
468 if (flags
& P_REFOUT
)
471 for (; aptr
< eptr
; ++aptr
) {
472 crc
= pcrc(*aptr
, divisor
, init
, xor, 0);
481 if (aptr
!= eptr
) return;
483 *result
= realloc(*result
, ++*resc
* sizeof(model_t
));
485 uerror("cannot reallocate result array");
489 rptr
= *result
+ *resc
- 1;
490 rptr
->spoly
= pclone(divisor
);
491 rptr
->init
= pclone(init
);
493 rptr
->xorout
= pclone(xorout
);
498 /* compute check value for this model */
501 /* callback to notify new model */