]>
cvs.zerfleddert.de Git - proxmark3-svn/blob - client/reveng/reveng.c
3c6da126ab136b24f836dc983db1c07276bfd72e
   2  * Greg Cook, 9/Apr/2015 
   5 /* CRC RevEng, an arbitrary-precision CRC calculator and algorithm finder 
   6  * Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015  Gregory Cook 
   8  * This file is part of CRC RevEng. 
  10  * CRC RevEng is free software: you can redistribute it and/or modify 
  11  * it under the terms of the GNU General Public License as published by 
  12  * the Free Software Foundation, either version 3 of the License, or 
  13  * (at your option) any later version. 
  15  * CRC RevEng is distributed in the hope that it will be useful, 
  16  * but WITHOUT ANY WARRANTY; without even the implied warranty of 
  17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  18  * GNU General Public License for more details. 
  20  * You should have received a copy of the GNU General Public License 
  21  * along with CRC RevEng.  If not, see <http://www.gnu.org/licenses/>. 
  24 /* 2013-09-16: calini(), calout() work on shortest argument 
  25  * 2013-06-11: added sequence number to uprog() calls 
  26  * 2013-02-08: added polynomial range search 
  27  * 2013-01-18: refactored model checking to pshres(); renamed chkres() 
  28  * 2012-05-24: efficiently build Init contribution string 
  29  * 2012-05-24: removed broken search for crossed-endian algorithms 
  30  * 2012-05-23: rewrote engini() after Ewing; removed modini() 
  31  * 2011-01-17: fixed ANSI C warnings 
  32  * 2011-01-08: fixed calini(), modini() caters for crossed-endian algos 
  33  * 2011-01-04: renamed functions, added calini(), factored pshres(); 
  34  *             rewrote engini() and implemented quick Init search 
  35  * 2011-01-01: reveng() initialises terminating entry, addparms() 
  36  *             initialises all fields 
  37  * 2010-12-26: renamed CRC RevEng. right results, rejects polys faster 
  38  * 2010-12-24: completed, first tests (unsuccessful) 
  39  * 2010-12-21: completed modulate(), partial sketch of reveng() 
  40  * 2010-12-19: started reveng 
  43 /* reveng() can in theory be modified to search for polynomials shorter 
  44  * than the full width as well, but this imposes a heavy time burden on 
  45  * the full width search, which is the primary use case, as well as 
  46  * complicating the search range function introduced in version 1.1.0. 
  47  * It is more effective to search for each shorter width directly. 
  55 static poly_t 
*modpol(const poly_t init
, int rflags
, int args
, const poly_t 
*argpolys
); 
  56 static void engini(int *resc
, model_t 
**result
, const poly_t divisor
, int flags
, int args
, const poly_t 
*argpolys
); 
  57 static void calout(int *resc
, model_t 
**result
, const poly_t divisor
, const poly_t init
, int flags
, int args
, const poly_t 
*argpolys
); 
  58 static void calini(int *resc
, model_t 
**result
, const poly_t divisor
, int flags
, const poly_t xorout
, int args
, const poly_t 
*argpolys
); 
  59 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
); 
  61 static const poly_t pzero 
= PZERO
; 
  64 reveng(const model_t 
*guess
, const poly_t qpoly
, int rflags
, int args
, const poly_t 
*argpolys
) { 
  65         /* Complete the parameters of a model by calculation or brute search. */ 
  66         poly_t 
*pworks
, *wptr
, rem
, gpoly
; 
  67         model_t 
*result 
= NULL
, *rptr
; 
  69         unsigned long spin 
= 0, seq 
= 0; 
  71         if(~rflags 
& R_HAVEP
) { 
  72                 /* The poly is not known. 
  73                  * Produce a list of differences between the arguments. 
  75                 pworks 
= modpol(guess
->init
, rflags
, args
, argpolys
); 
  76                 if(!pworks 
|| !plen(*pworks
)) { 
  80                 /* Initialise the guessed poly to the starting value. */ 
  81                 gpoly 
= pclone(guess
->spoly
); 
  82                 /* Clear the least significant term, to be set in the 
  83                  * loop. qpoly does not need fixing as it is only 
  84                  * compared with odd polys. 
  87                         pshift(&gpoly
, gpoly
, 0UL, 0UL, plen(gpoly
) - 1UL, 1UL); 
  89                 while(piter(&gpoly
) && (~rflags 
& R_HAVEQ 
|| pcmp(&gpoly
, &qpoly
) < 0)) { 
  90                         /* For each possible poly of this size, try 
  91                          * dividing all the differences in the list. 
  93                         if(!(spin
++ & R_SPMASK
)) { 
  94                                 uprog(gpoly
, guess
->flags
, seq
++); 
  96                         for(wptr 
= pworks
; plen(*wptr
); ++wptr
) { 
  97                                 /* straight divide message by poly, don't multiply by x^n */ 
  98                                 rem 
= pcrc(*wptr
, gpoly
, pzero
, pzero
, 0); 
 105                         /* If gpoly divides all the differences, it is a 
 106                          * candidate.  Search for an Init value for this 
 107                          * poly or if Init is known, log the result. 
 110                                 /* gpoly is a candidate poly */ 
 111                                 if(rflags 
& R_HAVEI 
&& rflags 
& R_HAVEX
) 
 112                                         chkres(&resc
, &result
, gpoly
, guess
->init
, guess
->flags
, guess
->xorout
, args
, argpolys
); 
 113                                 else if(rflags 
& R_HAVEI
) 
 114                                         calout(&resc
, &result
, gpoly
, guess
->init
, guess
->flags
, args
, argpolys
); 
 115                                 else if(rflags 
& R_HAVEX
) 
 116                                         calini(&resc
, &result
, gpoly
, guess
->flags
, guess
->xorout
, args
, argpolys
); 
 118                                         engini(&resc
, &result
, gpoly
, guess
->flags
, args
, argpolys
); 
 123                 /* Finished with gpoly and the differences list, free them. 
 126                 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"); 
 146         rptr 
= result 
+ resc 
- 1; 
 150         rptr
->xorout 
= pzero
; 
 158 modpol(const poly_t init
, int rflags
, int args
, const poly_t 
*argpolys
) { 
 159         /* Produce, in ascending length order, a list of differences 
 160          * between the arguments in the list by summing pairs of arguments. 
 161          * If R_HAVEI is not set in rflags, only pairs of equal length are 
 163          * Otherwise, sums of right-aligned pairs are also returned, with 
 164          * the supplied init poly added to the leftmost terms of each 
 167         poly_t work
, swap
, *result
, *rptr
, *iptr
; 
 168         const poly_t 
*aptr
, *bptr
, *eptr 
= argpolys 
+ args
; 
 169         unsigned long alen
, blen
; 
 171         if(args 
< 2) return(NULL
); 
 173         if(!(result 
= malloc(((((args 
- 1) * args
) >> 1) + 1) * sizeof(poly_t
)))) 
 174                 uerror("cannot allocate memory for codeword table"); 
 178         for(aptr 
= argpolys
; aptr 
< eptr
; ++aptr
) { 
 180                 for(bptr 
= aptr 
+ 1; bptr 
< eptr
; ++bptr
) { 
 183                                 work 
= pclone(*aptr
); 
 184                                 psum(&work
, *bptr
, 0UL); 
 185                         } else if(rflags 
& R_HAVEI 
&& alen 
< blen
) { 
 186                                 work 
= pclone(*bptr
); 
 187                                 psum(&work
, *aptr
, blen 
- alen
); 
 188                                 psum(&work
, init
, 0UL); 
 189                                 psum(&work
, init
, blen 
- alen
); 
 190                         } else if(rflags 
& R_HAVEI 
/* && alen > blen */) { 
 191                                 work 
= pclone(*aptr
); 
 192                                 psum(&work
, *bptr
, alen 
- blen
); 
 193                                 psum(&work
, init
, 0UL); 
 194                                 psum(&work
, init
, alen 
- blen
); 
 200                         if((blen 
= plen(work
))) { 
 201                                 /* insert work into result[] in ascending order of length */ 
 202                                 for(iptr 
= result
; iptr 
< rptr
; ++iptr
) { 
 203                                         if(plen(work
) < plen(*iptr
)) { 
 208                                         else if(plen(*iptr
) == blen 
&& !pcmp(&work
, iptr
)) { 
 223 engini(int *resc
, model_t 
**result
, const poly_t divisor
, int flags
, int args
, const poly_t 
*argpolys
) { 
 224         /* Search for init values implied by the arguments. 
 225          * Method from: Ewing, Gregory C. (March 2010). 
 226          * "Reverse-Engineering a CRC Algorithm". Christchurch: 
 227          * University of Canterbury. 
 228          * <http://www.cosc.canterbury.ac.nz/greg.ewing/essays/ 
 229          * CRC-Reverse-Engineering.html> 
 231         poly_t apoly 
= PZERO
, bpoly
, pone 
= PZERO
, *mat
, *jptr
; 
 232         const poly_t 
*aptr
, *bptr
, *iptr
; 
 233         unsigned long alen
, blen
, dlen
, ilen
, i
, j
; 
 236         dlen 
= plen(divisor
); 
 238         /* Allocate the CRC matrix */ 
 239         if(!(mat 
= (poly_t 
*) malloc((dlen 
<< 1) * sizeof(poly_t
)))) 
 240                 uerror("cannot allocate memory for CRC matrix"); 
 242         /* Find arguments of the two shortest lengths */ 
 243         alen 
= blen 
= plen(*(aptr 
= bptr 
= iptr 
= argpolys
)); 
 244         for(++iptr
; iptr 
< argpolys 
+ args
; ++iptr
) { 
 247                         bptr 
= aptr
; blen 
= alen
; 
 248                         aptr 
= iptr
; alen 
= ilen
; 
 249                 } else if(ilen 
> alen 
&& (aptr 
== bptr 
|| ilen 
< blen
)) { 
 250                         bptr 
= iptr
; blen 
= ilen
; 
 254                 /* if no arguments are suitable, calculate Init with an 
 255                  * assumed XorOut of 0.  Create a padded XorOut 
 257                 palloc(&apoly
, dlen
); 
 258                 calini(resc
, result
, divisor
, flags
, apoly
, args
, argpolys
); 
 263         /* Find the potential contribution of the bottom bit of Init */ 
 266         if(blen 
< (dlen 
<< 1)) { 
 267                 palloc(&apoly
, dlen
); /* >= 1 */ 
 268                 psum(&apoly
, pone
, (dlen 
<< 1) - 1UL - blen
); /* >= 0 */ 
 269                 psum(&apoly
, pone
, (dlen 
<< 1) - 1UL - alen
); /* >= 1 */ 
 271                 palloc(&apoly
, blen 
- dlen 
+ 1UL); /* > dlen */ 
 272                 psum(&apoly
, pone
, 0UL); 
 273                 psum(&apoly
, pone
, blen 
- alen
); /* >= 1 */ 
 275         if(plen(apoly
) > dlen
) { 
 276                 mat
[dlen
] = pcrc(apoly
, divisor
, pzero
, pzero
, 0); 
 282         /* Find the actual contribution of Init */ 
 283         apoly 
= pcrc(*aptr
, divisor
, pzero
, pzero
, 0); 
 284         bpoly 
= pcrc(*bptr
, divisor
, pzero
, apoly
, 0); 
 286         /* Populate the matrix */ 
 288         for(jptr
=mat
; jptr
<mat
+dlen
; ++jptr
) 
 290         for(iptr 
= jptr
++; jptr 
< mat 
+ (dlen 
<< 1); iptr 
= jptr
++) 
 291                 *jptr 
= pcrc(apoly
, divisor
, *iptr
, pzero
, P_MULXN
); 
 294         /* Transpose the matrix, augment with the Init contribution 
 295          * and convert to row echelon form 
 297         for(i
=0UL; i
<dlen
; ++i
) { 
 299                 iptr 
= mat 
+ (dlen 
<< 1); 
 300                 for(j
=0UL; j
<dlen
; ++j
) 
 301                         ppaste(&apoly
, *--iptr
, i
, j
, j 
+ 1UL, dlen 
+ 1UL); 
 303                         ppaste(&apoly
, bpoly
, i
, dlen
, dlen 
+ 1UL, dlen 
+ 1UL); 
 305                 while(j 
< dlen 
&& !pident(mat
[j
], pzero
)) { 
 306                         psum(&apoly
, mat
[j
], 0UL); /* pfirst(apoly) > j */ 
 310                         mat
[j
] = apoly
; /* pident(mat[j], pzero) || pfirst(mat[j]) == j */ 
 314         palloc(&bpoly
, dlen 
+ 1UL); 
 315         psum(&bpoly
, pone
, dlen
); 
 317         /* Iterate through all solutions */ 
 319                 /* Solve the matrix by Gaussian elimination. 
 320                  * The parity of the result, masked by each row, should be even. 
 323                 apoly 
= pclone(bpoly
); 
 325                 for(i
=0UL; i
<dlen
; ++i
) { 
 326                         /* Compute next bit of Init */ 
 327                         if(pmpar(apoly
, *--jptr
)) 
 328                                 psum(&apoly
, pone
, dlen 
- 1UL - i
); 
 329                         /* Toggle each zero row with carry, for next iteration */ 
 331                                if(pident(*jptr
, pzero
)) { 
 332                                        /* 0 to 1, no carry */ 
 335                                } else if(pident(*jptr
, bpoly
)) { 
 336                                        /* 1 to 0, carry forward */ 
 342                 /* Trim the augment mask bit */ 
 343                 praloc(&apoly
, dlen
); 
 345                 /* Test the Init value and add to results if correct */ 
 346                 calout(resc
, result
, divisor
, apoly
, flags
, args
, argpolys
); 
 352         /* Free the matrix. */ 
 353         for(jptr
=mat
; jptr 
< mat 
+ (dlen 
<< 1); ++jptr
) 
 359 calout(int *resc
, model_t 
**result
, const poly_t divisor
, const poly_t init
, int flags
, int args
, const poly_t 
*argpolys
) { 
 360         /* Calculate Xorout, check it against all the arguments and 
 361          * add to results if consistent. 
 364         const poly_t 
*aptr
, *iptr
; 
 365         unsigned long alen
, ilen
; 
 369         /* find argument of the shortest length */ 
 370         alen 
= plen(*(aptr 
= iptr 
= argpolys
)); 
 371         for(++iptr
; iptr 
< argpolys 
+ args
; ++iptr
) { 
 374                         aptr 
= iptr
; alen 
= ilen
; 
 378         xorout 
= pcrc(*aptr
, divisor
, init
, pzero
, 0); 
 379         /* On little-endian algorithms, the calculations yield 
 380          * the reverse of the actual xorout: in the Williams 
 381          * model, the refout stage intervenes between init and 
 387         /* Submit the model to the results table. 
 388          * Could skip the shortest argument but we wish to check our 
 391         chkres(resc
, result
, divisor
, init
, flags
, xorout
, args
, argpolys
); 
 396 calini(int *resc
, model_t 
**result
, const poly_t divisor
, int flags
, const poly_t xorout
, int args
, const poly_t 
*argpolys
) { 
 397         /* Calculate Init, check it against all the arguments and add to 
 398          * results if consistent. 
 400         poly_t rcpdiv
, rxor
, arg
, init
; 
 401         const poly_t 
*aptr
, *iptr
; 
 402         unsigned long alen
, ilen
; 
 406         /* find argument of the shortest length */ 
 407         alen 
= plen(*(aptr 
= iptr 
= argpolys
)); 
 408         for(++iptr
; iptr 
< argpolys 
+ args
; ++iptr
) { 
 411                         aptr 
= iptr
; alen 
= ilen
; 
 415         rcpdiv 
= pclone(divisor
); 
 417         /* If the algorithm is reflected, an ordinary CRC requires the 
 418          * model's XorOut to be reversed, as XorOut follows the RefOut 
 419          * stage.  To reverse the CRC calculation we need rxor to be the 
 420          * mirror image of the forward XorOut. 
 422         rxor 
= pclone(xorout
); 
 423         if(~flags 
& P_REFOUT
) 
 428         init 
= pcrc(arg
, rcpdiv
, rxor
, pzero
, 0); 
 434         /* Submit the model to the results table. 
 435          * Could skip the shortest argument but we wish to check our 
 438         chkres(resc
, result
, divisor
, init
, flags
, xorout
, args
, argpolys
); 
 443 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
) { 
 444         /* Checks a model against the argument list, and adds to the 
 445          * external results table if consistent. 
 446          * Extends the result array and update the external pointer if 
 451         const poly_t 
*aptr 
= argpolys
, *const eptr 
= argpolys 
+ args
; 
 453         /* If the algorithm is reflected, an ordinary CRC requires the 
 454          * model's XorOut to be reversed, as XorOut follows the RefOut 
 457         xor = pclone(xorout
); 
 461         for(; aptr 
< eptr
; ++aptr
) { 
 462                 crc 
= pcrc(*aptr
, divisor
, init
, xor, 0); 
 471         if(aptr 
!= eptr
) return; 
 473         if(!(*result 
= realloc(*result
, ++*resc 
* sizeof(model_t
)))) 
 474                 uerror("cannot reallocate result array"); 
 476         rptr 
= *result 
+ *resc 
- 1; 
 477         rptr
->spoly  
= pclone(divisor
); 
 478         rptr
->init   
= pclone(init
); 
 480         rptr
->xorout 
= pclone(xorout
); 
 483         /* compute check value for this model */ 
 486         /* callback to notify new model */