Come viene calcolato un checksum CRC32?


102

Forse semplicemente non lo vedo, ma CRC32 sembra inutilmente complicato o non sufficientemente spiegato da nessuna parte che ho trovato sul web.

Capisco che è il resto di una divisione aritmetica non basata sul carry del valore del messaggio, diviso per il polinomio (generatore), ma l'effettiva implementazione di esso mi sfugge.

Ho letto A Painless Guide To CRC Error Detection Algorithms , e devo dire che non è stato indolore. Va oltre la teoria piuttosto bene, ma l'autore non arriva mai a un semplice "è questo". Dice quali sono i parametri per l'algoritmo CRC32 standard, ma trascura di esporre chiaramente come ci si arriva.

La parte che mi colpisce è quando dice "questo è tutto" e poi aggiunge "oh a proposito, può essere invertito o avviato con condizioni iniziali diverse" e non dà una risposta chiara su quale sia il modo finale di calcolare un checksum CRC32 dato tutte le modifiche che ha appena aggiunto.

  • C'è una spiegazione più semplice di come viene calcolato CRC32?

Ho tentato di codificare in C come è formata la tabella:

for (i = 0; i < 256; i++)
{
    temp = i;

    for (j = 0; j < 8; j++)
    {
        if (temp & 1)
        {
            temp >>= 1;
            temp ^= 0xEDB88320;
        }
        else {temp >>= 1;}
    }
    testcrc[i] = temp;
}

ma questo sembra generare valori incoerenti con i valori che ho trovato altrove su Internet. Ho potuto utilizzare i valori che ho trovato on-line, ma voglio capire come sono stati creati.

Qualsiasi aiuto per chiarire questi numeri incredibilmente confusi sarebbe molto apprezzato.


9
Il codice per la generazione della tabella CRC32 sembra essere corretto. Il tuo polinomio CRC32 lsbit-first ( invertito ) 0xEDB88320può anche essere scritto msbit-first ( normale ) come 0x04C11DB7. I valori della tabella che hai trovato altrove sono stati generati utilizzando lo stesso polinomio CRC?
jschmier

1
@jschmier ciao, mi sento come se fossi un passo dietro questo ragazzo che fa le domande? stackoverflow.com/questions/62168128/...
bluejayke

Se qualcun altro è curioso di leggere "A Painless Guide To CRC Error Detection Algorithms" collegato sopra, l'URL originale è stato nascosto ma Google ha trovato facilmente diverse copie, inclusa questa: zlib.net/crc_v3.txt
Stéphane

Risposte:


114

Il polinomio per CRC32 è:

x 32 + x 26 + x 23 + x 22 + x 16 + x 12 + x 11 + x 10 + x 8 + x 7 + x 5 + x 4 + x 2 + x + 1

O in esadecimale e binario:

0x 01 04 C1 1D B7
1 0000 0100 1100 0001 0001 1101 1011 0111

Il termine più alto (x 32 ) di solito non è scritto in modo esplicito, quindi può essere rappresentato in esadecimale proprio come

0x 04 C1 1D B7

Sentiti libero di contare gli 1 e gli 0, ma scoprirai che corrispondono al polinomio, dove 1 è il bit 0 (o il primo bit) ed xè il bit 1 (o il secondo bit).

Perché questo polinomio? Perché deve esserci uno standard dato polinomio e lo standard è stato impostato da IEEE 802.3. Inoltre è estremamente difficile trovare un polinomio che rilevi efficacemente diversi errori di bit.

Puoi pensare al CRC-32 come a una serie di "Aritmetica binaria senza riporti", o fondamentalmente "Operazioni XOR e spostamento". Questo è tecnicamente chiamato aritmetica polinomiale.

Per capirlo meglio, pensa a questa moltiplicazione:

(x^3 + x^2 + x^0)(x^3 + x^1 + x^0)
= (x^6 + x^4 + x^3
 + x^5 + x^3 + x^2
 + x^3 + x^1 + x^0)
= x^6 + x^5 + x^4 + 3*x^3 + x^2 + x^1 + x^0

Se assumiamo che x sia in base 2, otteniamo:

x^7 + x^3 + x^2 + x^1 + x^0

Perché? Poiché 3x ^ 3 è 11x ^ 11 (ma abbiamo bisogno solo di 1 o 0 cifre preliminari) quindi trasferiamo:

=1x^110 + 1x^101 + 1x^100          + 11x^11 + 1x^10 + 1x^1 + x^0
=1x^110 + 1x^101 + 1x^100 + 1x^100 + 1x^11 + 1x^10 + 1x^1 + x^0
=1x^110 + 1x^101 + 1x^101          + 1x^11 + 1x^10 + 1x^1 + x^0
=1x^110 + 1x^110                   + 1x^11 + 1x^10 + 1x^1 + x^0
=1x^111                            + 1x^11 + 1x^10 + 1x^1 + x^0

Ma i matematici hanno cambiato le regole in modo che sia mod 2. Quindi, fondamentalmente, qualsiasi polinomio binario mod 2 è solo un'aggiunta senza carry o XOR. Quindi la nostra equazione originale è simile a:

=( 1x^110 + 1x^101 + 1x^100 + 11x^11 + 1x^10 + 1x^1 + x^0 ) MOD 2
=( 1x^110 + 1x^101 + 1x^100 +  1x^11 + 1x^10 + 1x^1 + x^0 )
= x^6 + x^5 + x^4 + 3*x^3 + x^2 + x^1 + x^0 (or that original number we had)

So che questo è un atto di fede, ma va oltre le mie capacità di programmatore di linea. Se sei uno studente di informatica o un ingegnere hard-core, sfido a scomporlo. Tutti trarranno vantaggio da questa analisi.

Quindi, per elaborare un esempio completo:

   Original message                : 1101011011
   Polynomial of (W)idth 4         :      10011
   Message after appending W zeros : 11010110110000

Ora dividiamo il messaggio aumentato per il Poly usando l'aritmetica CRC. Questa è la stessa divisione di prima:

            1100001010 = Quotient (nobody cares about the quotient)
       _______________
10011 ) 11010110110000 = Augmented message (1101011011 + 0000)
=Poly   10011,,.,,....
        -----,,.,,....
         10011,.,,....
         10011,.,,....
         -----,.,,....
          00001.,,....
          00000.,,....
          -----.,,....
           00010,,....
           00000,,....
           -----,,....
            00101,....
            00000,....
            -----,....
             01011....
             00000....
             -----....
              10110...
              10011...
              -----...
               01010..
               00000..
               -----..
                10100.
                10011.
                -----.
                 01110
                 00000
                 -----
                  1110 = Remainder = THE CHECKSUM!!!!

La divisione restituisce un quoziente, che viene eliminato, e un resto, che è il checksum calcolato. Questo termina il calcolo. Di solito, il checksum viene quindi aggiunto al messaggio e il risultato trasmesso. In questo caso la trasmissione sarebbe: 11010110111110.

Usa solo un numero a 32 bit come divisore e usa l'intero flusso come dividendo. Getta via il quoziente e conserva il resto. Metti il ​​resto alla fine del tuo messaggio e avrai un CRC32.

Recensione ragazzo medio:

         QUOTIENT
        ----------
DIVISOR ) DIVIDEND
                 = REMAINDER
  1. Prendi i primi 32 bit.
  2. Spostare i bit
  3. Se 32 bit sono inferiori a DIVISOR, andare al passaggio 2.
  4. XOR 32 bit di DIVISOR. Vai al passaggio 2.

(Si noti che il flusso deve essere divisibile per 32 bit o dovrebbe essere riempito. Ad esempio, un flusso ANSI a 8 bit dovrebbe essere riempito. Inoltre, alla fine del flusso, la divisione viene interrotta.)


13
+1 per la "Average Guy Review" alla fine - forse considera di spostare questo diritto all'inizio - una sorta di TL; DR: P
Aaronsnoswell

4
@abstractnature Ricorda che stiamo dividendo polinomi, non solo numeri binari. Non possiamo eseguire sottrazioni "normali" perché non possiamo "prendere in prestito" $ x ^ n $ da $ x ^ {n + 1} $; sono diversi tipi di cose. Inoltre, poiché i bit sono solo 0 o 1, quale sarebbe -1? In realtà, stiamo lavorando nell'anello dei polinomi con coefficienti nel campo $ Z / 2Z $, che ha solo due elementi, 0 e 1, e dove $ 1 + 1 = 0 $. Mettendo i cofficienti in un campo, i polinomi formano quello che viene chiamato un dominio euclideo, che fondamentalmente consente solo che ciò che stiamo cercando di fare sia ben definito in primo luogo.
calavicci

6
Giusto per chiarire che il polinomio effettivo è 100000100110000010001110110110111 = 0x104C11DB7. Il MSB è implicito, ma dovrebbe comunque essere preso in considerazione in un'implementazione. Poiché sarà sempre impostato perché il polinomio deve essere lungo 33 bit (quindi il resto può essere lungo 32 bit) alcune persone omettono il MSB.
Felipe T.

2
x^6 + x^5 + x^4 + 3*x^3 + x^2 + x^1 + x^0 ... If we assume x is base 2 then we get: x^7 + x^3 + x^2 + x^1 + x^0. Non è così che funziona la matematica. I coefficienti del polinomio sono mod (2) o GF (2), le x sono lasciate sole, risultando in x ^ 6 + x ^ 5 + x ^ 4 + x ^ 3 + x ^ 2 + x ^ 1 + x ^ 0 (dal 3 mod (2) = 1). Tack the remainder on the end of your message- tecnicamente il resto viene sottratto dai bit 0 aggiunti al messaggio, ma poiché si tratta di matematica mod (2), sia l'addizione che la sottrazione sono uguali a XOR e gli zero bit XOR con il resto sono gli stessi come il resto.
rcgldr

2
@ MarcusJ - Why did you append four 0s though?- gli algoritmi del software per calcolare il crc aggiungono efficacemente gli 0, anche se non è evidente. Se si mostra il calcolo CRC utilizzando la divisione della mano lunga, è necessario aggiungere degli 0 affinché l'esempio di divisione appaia correttamente.
rcgldr

11

Per IEEE802.3, CRC-32. Pensa all'intero messaggio come a un flusso di bit seriale, aggiungi 32 zeri alla fine del messaggio. Successivamente, DEVI invertire i bit di OGNI byte del messaggio e completare con 1 i primi 32 bit. Ora dividi per il polinomio CRC-32, 0x104C11DB7. Infine, è necessario che gli 1 completino il resto a 32 bit di questa divisione in bit per invertire ciascuno dei 4 byte del resto. Diventa il CRC a 32 bit che viene aggiunto alla fine del messaggio.

La ragione di questa strana procedura è che le prime implementazioni Ethernet serializzano il messaggio un byte alla volta e trasmettono prima il bit meno significativo di ogni byte. Il flusso di bit seriale è stato quindi sottoposto a un calcolo del registro a scorrimento CRC-32 seriale, che è stato semplicemente completato e inviato in rete dopo che il messaggio è stato completato. Il motivo per completare i primi 32 bit del messaggio è che non si ottiene un CRC tutto zero anche se il messaggio era tutto zero.


2
Questa è la migliore risposta qui finora, anche se sostituirei 'bit-reverse ciascuno dei 4 byte', con 'bit-inverte i 4 byte, trattandoli come un'unica entità' ad esempio 'abcdefgh ijklmnop qrstuvwx yzABCDEF' con 'FEDCBAzy xwvutsrq ponmlkji hgfedcba '. Vedi anche: Tutorial sull'hash CRC-32 - Comunità AutoHotkey .
vafylec

1
ciao, quale "messaggio" esatto, invertite? stackoverflow.com/questions/62168128/...
bluejayke

10

Un CRC è piuttosto semplice; prendi un polinomio rappresentato come bit e dati e dividi il polinomio nei dati (o rappresenti i dati come polinomio e fai la stessa cosa). Il resto, che è compreso tra 0 e il polinomio, è il CRC. Il tuo codice è un po 'difficile da capire, in parte perché è incompleto: temp e testcrc non sono dichiarati, quindi non è chiaro cosa viene indicizzato e quanti dati stanno attraversando l'algoritmo.

Il modo per capire i CRC è provare a calcolarne alcuni usando un breve pezzo di dati (16 bit circa) con un breve polinomio - 4 bit, forse. Se ti eserciti in questo modo, capirai davvero come potresti procedere per codificarlo.

Se lo fai spesso, un CRC è piuttosto lento da calcolare nel software. Il calcolo hardware è molto più efficiente e richiede solo poche porte.


1
Per CRC32 o CRC32b, otteniamo un significato di collisione hash per due stringhe diverse, otteniamo lo stesso CRC
indianwebdevil

1
ciao, sono un po 'confuso cosa intendi per "divifde i polinomi nei dati"? stackoverflow.com/questions/62168128/... ciò che è X nel polinomiale represnted da? Uso gli altri byte dal blocco?
bluejayke

7

Oltre al controllo di ridondanza ciclica di Wikipedia e al calcolo degli articoli CRC , ho trovato un documento intitolato Reversing CRC - Theory and Practice * come un buon riferimento.

Esistono essenzialmente tre approcci per il calcolo di un CRC: un approccio algebrico, un approccio orientato ai bit e un approccio basato sulla tabella. In Reversing CRC - Teoria e pratica * , ciascuno di questi tre algoritmi / approcci è spiegato in teoria accompagnato nell'APPENDICE da un'implementazione per CRC32 nel linguaggio di programmazione C.

* PDF Link
Reversing CRC - Theory and Practice.
HU Berlin Public Report
SAR-PR-2006-05
maggio 2006
Autori:
Martin Stigge, Henryk Plötz, Wolf Müller, Jens-Peter Redlich


ciao, puoi approfondire un po '?
bluejayke

7

Ho passato un po 'di tempo a cercare di scoprire la risposta a questa domanda e oggi ho finalmente pubblicato un tutorial su CRC-32: tutorial sull'hash CRC-32 - AutoHotkey Community

In questo esempio da esso, mostro come calcolare l'hash CRC-32 per la stringa ASCII 'abc':

calculate the CRC-32 hash for the ASCII string 'abc':

inputs:
dividend: binary for 'abc': 0b011000010110001001100011 = 0x616263
polynomial: 0b100000100110000010001110110110111 = 0x104C11DB7

011000010110001001100011
reverse bits in each byte:
100001100100011011000110
append 32 0 bits:
10000110010001101100011000000000000000000000000000000000
XOR the first 4 bytes with 0xFFFFFFFF:
01111001101110010011100111111111000000000000000000000000

'CRC division':
01111001101110010011100111111111000000000000000000000000
 100000100110000010001110110110111
 ---------------------------------
  111000100010010111111010010010110
  100000100110000010001110110110111
  ---------------------------------
   110000001000101011101001001000010
   100000100110000010001110110110111
   ---------------------------------
    100001011101010011001111111101010
    100000100110000010001110110110111
    ---------------------------------
         111101101000100000100101110100000
         100000100110000010001110110110111
         ---------------------------------
          111010011101000101010110000101110
          100000100110000010001110110110111
          ---------------------------------
           110101110110001110110001100110010
           100000100110000010001110110110111
           ---------------------------------
            101010100000011001111110100001010
            100000100110000010001110110110111
            ---------------------------------
              101000011001101111000001011110100
              100000100110000010001110110110111
              ---------------------------------
                100011111110110100111110100001100
                100000100110000010001110110110111
                ---------------------------------
                    110110001101101100000101110110000
                    100000100110000010001110110110111
                    ---------------------------------
                     101101010111011100010110000001110
                     100000100110000010001110110110111
                     ---------------------------------
                       110111000101111001100011011100100
                       100000100110000010001110110110111
                       ---------------------------------
                        10111100011111011101101101010011

remainder: 0b10111100011111011101101101010011 = 0xBC7DDB53
XOR the remainder with 0xFFFFFFFF:
0b01000011100000100010010010101100 = 0x438224AC
reverse bits:
0b00110101001001000100000111000010 = 0x352441C2

thus the CRC-32 hash for the ASCII string 'abc' is 0x352441C2

1
Se si desidera una maggiore velocità, esisteva un metodo elaborato da alcuni ingegneri di Intel verso il 2006 utilizzando in genere 4 o 8 byte della larghezza del bus dati della macchina contemporaneamente. Documento accademico: static.aminer.org/pdf/PDF/000/432/446/… Progetto su Sourceforge: sourceforge.net/projects/slicing-by-8 Pagina generale crc: create.stephan-brumme.com/crc32
Alan Corey

1
Ciao grazie sembra fantastico, ma come si ottiene esattamente il valore polinomiale? cosa rappresenta esattamente X? E quando dice x ^ 32, è x alla potenza di 32 o l'operatore bit a bit ^? stackoverflow.com/questions/62168128/...
bluejayke


1

Per ridurre crc32 a prendere il promemoria devi:

  1. Invertire i bit su ogni byte
  2. xo primi quattro byte con 0xFF (questo per evitare errori sugli 0 iniziali)
  3. Aggiungi padding alla fine (questo per fare in modo che gli ultimi 4 byte prendano parte all'hash)
  4. Calcola il promemoria
  5. Invertire di nuovo i bit
  6. xo di nuovo il risultato.

Nel codice questo è:


func CRC32 (file []byte) uint32 {
    for i , v := range(file) {
        file[i] = bits.Reverse8(v)
    }
    for i := 0; i < 4; i++ {
        file[i] ^= 0xFF
    }

    // Add padding
    file = append(file, []byte{0, 0, 0, 0}...)
    newReminder := bits.Reverse32(reminderIEEE(file))

    return newReminder ^ 0xFFFFFFFF
}

dove reminderIEEE è il puro promemoria su GF (2) [x]


1
Sto avendo un po '(gioco di parole) di difficoltà a capire questo? stackoverflow.com/questions/62168128/...
bluejayke

1
hey @bluejayke, controlla questa libreria github.com/furstenheim/sparse_crc32/blob/master/main.go implementa il crc32 per i file sparsi, puoi vedere lì i dettagli nitidi sul calcolo. Non è ottimizzato quindi è più facile da seguire rispetto alle normali implementazioni. Può essere che ciò che non capisci è la parte GF (2) [x]. Fondamentalmente x ^ 3 + x significa 1010, x ^ 4 + x + 1 significa 10011. Quindi devi eseguire la divisione, ad esempio x ^ 3 + x è x * (x ^ 2 + 1). quindi il promemoria di x ^ 3 + x su x è 0, ma su x ^ 2 sarebbe x ^ 2 * x + x, cioè il promemoria sarebbe x.
Gabriel Furstenheim,

1
@bluejayke e reminderIEEE significa promemoria contro un polinomio ben noto, il polinomio IEEE
Gabriel Furstenheim,

ciao ancora, grazie per la tua risposta. Sto solo cercando di capire (per scopi javascript) cosa rappresenta la "x" nel polinomio. "X" è una sorta di parola in codice per qualcosa che mi manca qui? Ci sono un sacco di termini che mi confondono qui, non ho mai sentito parlare di CRC32 prima, e anche dopo aver cercato non sono riuscito a trovarlo effettivamente spiegato. Per un PNG, ad esempio, dice che devo prendere il "CRC per ogni blocco", significa "per tutti i dati nel blocco"? Ma come "collegarlo" al polinomio? Cosa rappresenta "x"? Anche quando dice x ^ 32, è come Math.pow (x, 32) o bit per bit ^
bluejayke

1
Ciao @bluejayke, x è un'astrazione per semplificare i calcoli. Non dovrebbe essere sostituito da nulla. x ^ 2 Intendo x * x, come moltiplicazione formale. Qui chrisballance.com/wp-content/uploads/2015/10/CRC-Primer.html puoi trovare una bella spiegazione di quella divisione. Quello che ho provato con la mia risposta è stato di colmare il divario tra la divisione (in quel link) e il calcolo effettivo
Gabriel Furstenheim,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.