Bagaimana cara menghitung checksum CRC32?

103

Mungkin saya hanya tidak melihatnya, tetapi CRC32 tampaknya rumit, atau tidak cukup dijelaskan di mana pun yang dapat saya temukan di web.

Saya mengerti bahwa itu adalah sisa dari pembagian aritmatika berbasis non-carry dari nilai pesan, dibagi dengan polinomial (generator), tetapi implementasi sebenarnya dari itu luput dari saya.

Saya telah membaca A Painless Guide To CRC Error Detection Algorithms , dan saya harus mengatakan itu bukan tanpa rasa sakit. Ini membahas teori dengan cukup baik, tetapi penulis tidak pernah sampai pada pertanyaan sederhana "ini dia." Dia memang mengatakan apa parameter untuk algoritma CRC32 standar, tetapi dia lalai menjelaskan dengan jelas bagaimana Anda mendapatkannya.

Bagian yang membuat saya tertarik adalah ketika dia mengatakan "ini dia" dan kemudian menambahkan, "oh ngomong-ngomong, ini bisa dibalik atau dimulai dengan kondisi awal yang berbeda," dan tidak memberikan jawaban yang jelas tentang cara akhirnya menghitung checksum CRC32 mengingat semua perubahan yang baru saja dia tambahkan.

  • Apakah ada penjelasan yang lebih sederhana tentang bagaimana CRC32 dihitung?

Saya mencoba membuat kode dalam C bagaimana tabel terbentuk:

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;
}

tetapi ini tampaknya menghasilkan nilai yang tidak sesuai dengan nilai yang saya temukan di tempat lain di Internet. Saya dapat menggunakan nilai yang saya temukan online, tetapi saya ingin memahami bagaimana nilai itu dibuat.

Bantuan apa pun untuk menyelesaikan angka-angka yang sangat membingungkan ini akan sangat kami hargai.

aquanar
sumber
9
Kode Anda untuk menghasilkan tabel CRC32 tampaknya benar. Polinomial CRC32 lsbit-first ( terbalik ) Anda 0xEDB88320juga dapat ditulis msbit-first ( normal ) sebagai 0x04C11DB7. Apakah nilai tabel yang Anda temukan di tempat lain dibuat menggunakan polinomial CRC yang sama?
jschmier
1
@jschmier hai, saya merasa seperti saya selangkah di belakang orang ini mengajukan pertanyaan? stackoverflow.com/questions/62168128/…
bluejayke
Jika ada orang lain yang penasaran untuk membaca "A Painless Guide To CRC Error Detection Algorithms" yang ditautkan di atas, URL asli tersebut disemprot tetapi Google dengan mudah menemukan beberapa salinan, termasuk yang ini: zlib.net/crc_v3.txt
Stéphane

Jawaban:

118

Polinomial untuk CRC32 adalah:

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

Atau dalam hex dan biner:

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

Istilah tertinggi (x 32 ) biasanya tidak ditulis secara eksplisit, sehingga dapat direpresentasikan dalam hex seperti

0x 04 C1 1D B7

Jangan ragu untuk menghitung 1 dan 0, tetapi Anda akan menemukannya cocok dengan polinomial, di mana 1bit 0 (atau bit pertama) danx bit 1 (atau bit kedua).

Mengapa polinomial ini? Karena perlu ada polinomial standar yang diberikan dan standar itu ditetapkan oleh IEEE 802.3. Juga sangat sulit untuk menemukan polinomial yang mendeteksi kesalahan bit berbeda secara efektif.

Anda dapat menganggap CRC-32 sebagai rangkaian "Aritmatika Biner tanpa Pengangkutan", atau pada dasarnya "operasi XOR dan shift". Ini secara teknis disebut Aritmatika Polinomial.

Untuk lebih memahaminya, pikirkan perkalian ini:

(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

Jika kita mengasumsikan x adalah basis 2 maka kita mendapatkan:

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

Mengapa? Karena 3x ^ 3 adalah 11x ^ 11 (tapi kita hanya membutuhkan 1 atau 0 digit awal) jadi kita bawa:

=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

Tetapi ahli matematika mengubah aturan tersebut sehingga menjadi mod 2. Jadi pada dasarnya semua polinomial biner mod 2 hanyalah penjumlahan tanpa membawa atau XOR. Jadi persamaan asli kita terlihat seperti ini:

=( 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)

Saya tahu ini adalah lompatan keyakinan tetapi ini di luar kemampuan saya sebagai programmer garis. Jika Anda adalah siswa atau insinyur CS inti, saya menantang untuk memecahnya. Semua orang akan mendapat manfaat dari analisis ini.

Jadi untuk mengerjakan contoh lengkap:

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

Sekarang kita membagi Pesan yang ditambah dengan Poly menggunakan aritmatika CRC. Ini adalah divisi yang sama seperti sebelumnya:

            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!!!!

Pembagian menghasilkan hasil bagi, yang kita buang, dan sisanya, yang merupakan checksum yang dihitung. Ini mengakhiri perhitungan. Biasanya, checksum kemudian ditambahkan ke pesan dan hasilnya dikirim. Dalam hal ini transmisi akan menjadi: 11010110111110.

Gunakan hanya angka 32-bit sebagai pembagi Anda dan gunakan seluruh aliran Anda sebagai dividen. Buang hasil bagi dan simpan sisanya. Tack sisanya di akhir pesan Anda dan Anda memiliki CRC32.

Ulasan pria rata-rata:

         QUOTIENT
        ----------
DIVISOR ) DIVIDEND
                 = REMAINDER
  1. Ambil 32 bit pertama.
  2. Ganti bit
  3. Jika 32 bit kurang dari DIVISOR, lanjutkan ke langkah 2.
  4. XOR 32 bit oleh DIVISOR. Lanjutkan ke langkah 2.

(Perhatikan bahwa aliran harus dapat dibagi dengan 32 bit atau harus diisi. Misalnya, aliran ANSI 8-bit harus di-padded. Juga di akhir aliran, pembagian dihentikan.)

ilkkachu
sumber
13
1 untuk "Ulasan Orang Biasa" di akhir - mungkin pertimbangkan untuk memindahkan hak ini ke atas - semacam TL; DR: P
aaronsnoswell
4
@abstractnature Ingatlah bahwa kita sedang membagi polinomial, bukan hanya bilangan biner. Kita tidak dapat melakukan pengurangan "normal" karena kita tidak dapat "meminjam" $ x ^ n $ dari $ x ^ {n + 1} $; mereka adalah hal yang berbeda. Juga, karena bit hanya 0 atau 1, apa yang akan -1 genap? Sungguh, kami bekerja di ring polinomial dengan koefisien di bidang $ Z / 2Z $, yang hanya memiliki dua elemen, 0 dan 1, dan di mana $ 1 + 1 = 0 $. Dengan meletakkan koefisien di bidang, maka polinomial membentuk apa yang disebut Domain Euclidean, yang pada dasarnya memungkinkan apa yang kita coba lakukan untuk didefinisikan dengan baik di tempat pertama.
calavicci
6
Hanya untuk memperjelas polinomial sebenarnya adalah 100000100110000010001110110110111 = 0x104C11DB7. MSB bersifat implisit, tetapi tetap harus diperhitungkan dalam implementasi. Karena ini akan selalu disetel karena polinomial harus sepanjang 33 bit (sehingga sisanya dapat sepanjang 32 bit) beberapa orang mengabaikan 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. Ini bukan cara kerja matematika. Koefisien untuk polinomial adalah mod (2) atau GF (2), x dibiarkan saja, menghasilkan x ^ 6 + x ^ 5 + x ^ 4 + x ^ 3 + x ^ 2 + x ^ 1 + x ^ 0 (karena 3 mod (2) = 1). Tack the remainder on the end of your message- secara teknis sisa dikurangi dari 0 bit yang ditambahkan ke pesan, tetapi karena ini adalah mod (2) matematika, baik menambah dan mengurangi sama dengan XOR, dan nol bit XOR'ed dengan sisanya sama sebagai sisanya.
rcgldr
2
@MarcusJ - Why did you append four 0s though?- algoritma perangkat lunak untuk menghitung crc secara efektif menambahkan 0s, meskipun tidak terlihat. Jika menampilkan penghitungan CRC menggunakan pembagian tangan panjang, maka 0 perlu ditambahkan agar contoh pembagian muncul dengan benar.
rcgldr
11

Untuk IEEE802.3, CRC-32. Pikirkan seluruh pesan sebagai aliran bit serial, tambahkan 32 angka nol di akhir pesan. Selanjutnya, Anda HARUS membalik bit SETIAP byte pesan dan melakukan 1 untuk melengkapi 32 bit pertama. Sekarang bagi dengan polinomial CRC-32, 0x104C11DB7. Akhirnya, Anda harus 1 melengkapi sisa 32-bit dari pembagian ini bit-reverse masing-masing dari 4 byte sisanya. Ini menjadi CRC 32-bit yang ditambahkan ke akhir pesan.

Alasan untuk prosedur aneh ini adalah bahwa implementasi Ethernet pertama akan membuat pesan menjadi serial satu byte pada satu waktu dan mengirimkan bit paling signifikan dari setiap byte terlebih dahulu. Aliran bit serial kemudian melewati komputasi register geser CRC-32 serial, yang hanya dilengkapi dan dikirim melalui kabel setelah pesan selesai. Alasan untuk melengkapi 32 bit pertama dari pesan tersebut adalah agar Anda tidak mendapatkan CRC nol semua meskipun pesan tersebut semuanya nol.

Pavlo Bobrek
sumber
2
Sejauh ini, ini adalah jawaban terbaik, meskipun saya akan mengganti 'bit-reverse masing-masing dari 4 byte', dengan 'bit-reverse 4 byte, memperlakukan mereka sebagai satu entitas' misalnya 'abcdefgh ijklmnop qrstuvwx yzABCDEF' menjadi 'FEDCBAzy xwvutsrq ponmlkji hgfedcba '. Lihat juga: tutorial hash CRC-32 - Komunitas AutoHotkey .
vafylec
1
hai, apa "pesan" tepat; y Anda membalikkan? stackoverflow.com/questions/62168128/…
bluejayke
10

CRC sangat sederhana; Anda mengambil polinomial yang direpresentasikan sebagai bit dan data, dan membagi polinomial tersebut menjadi data (atau Anda merepresentasikan data sebagai polinomial dan melakukan hal yang sama). Sisanya, antara 0 dan polinomial adalah CRC. Kode Anda agak sulit dipahami, sebagian karena tidak lengkap: temp dan testcrc tidak dideklarasikan, jadi tidak jelas apa yang diindeks, dan berapa banyak data yang berjalan melalui algoritme.

Cara untuk memahami CRC adalah dengan mencoba menghitung beberapa menggunakan potongan data pendek (16 bit atau lebih) dengan polinomial pendek - 4 bit, mungkin. Jika Anda berlatih dengan cara ini, Anda akan benar-benar memahami bagaimana Anda bisa melakukan pengkodeannya.

Jika Anda sering melakukannya, CRC cukup lambat untuk dihitung dalam perangkat lunak. Komputasi perangkat keras jauh lebih efisien, dan hanya membutuhkan beberapa gerbang.

Angin puyuh
sumber
1
Untuk CRC32 atau CRC32b, apakah kita mendapatkan makna tabrakan hash untuk dua string berbeda apakah kita mendapatkan CRC yang sama
indianwebdevil
1
hai, saya agak bingung apa yang Anda maksud dengan "membagi polinomial ke dalam data"? stackoverflow.com/questions/62168128/… apa X dalam polinomial yang diwakili? Apakah saya menggunakan byte lain dari chunk?
bluejayke
7

Selain pemeriksaan redundansi Siklik Wikipedia dan artikel Perhitungan CRC , saya menemukan sebuah makalah berjudul Reversing CRC - Theory and Practice * untuk menjadi referensi yang baik.

Pada dasarnya ada tiga pendekatan untuk menghitung CRC: pendekatan aljabar, pendekatan berorientasi bit, dan pendekatan berbasis tabel. Dalam Reversing CRC - Theory and Practice * , masing-masing dari tiga algoritma / pendekatan ini dijelaskan dalam teori disertai dalam LAMPIRAN dengan implementasi untuk CRC32 dalam bahasa pemrograman C.

* PDF Link
CRC Pembalik - Teori dan Praktek.
Laporan Publik HU Berlin
SAR-PR-2006-05
Mei 2006
Penulis:
Martin Stigge, Henryk Plötz, Wolf Müller, Jens-Peter Redlich

jschmier.dll
sumber
hai, bisakah kamu menjelaskan sedikit?
bluejayke
7

Saya menghabiskan waktu untuk mencoba mengungkap jawaban atas pertanyaan ini, dan akhirnya saya menerbitkan tutorial tentang CRC-32 hari ini: tutorial hash CRC-32 - Komunitas AutoHotkey

Dalam contoh ini, saya menunjukkan cara menghitung hash CRC-32 untuk string 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
vafylec.dll
sumber
1
Jika Anda menginginkan kecepatan lebih, ada metode yang dikerjakan oleh beberapa insinyur di Intel sekitar tahun 2006 menggunakan biasanya 4 atau 8 byte dari lebar bus data mesin secara bersamaan. Makalah akademik: static.aminer.org/pdf/PDF/000/432/446/… Proyek di Sourceforge: sourceforge.net/projects/slicing-by-8 Umum halaman crc: create.stephan-brumme.com/crc32
Alan Corey
1
Hai, terima kasih tampak hebat, tetapi bagaimana tepatnya Anda mendapatkan nilai polinomial? apa sebenarnya yang diwakili oleh X? Dan ketika dikatakan x ^ 32, apakah itu x pangkat 32, atau operator bitwise ^? stackoverflow.com/questions/62168128/…
bluejayke
1

Untuk mengurangi crc32 untuk menerima pengingat, Anda perlu:

  1. Balikkan bit pada setiap byte
  2. xor empat byte pertama dengan 0xFF (ini untuk menghindari kesalahan pada 0 di depan)
  3. Tambahkan padding di akhir (ini untuk membuat 4 byte terakhir mengambil bagian dalam hash)
  4. Hitung pengingat
  5. Balikkan bit lagi
  6. atau hasilnya lagi.

Dalam kode ini adalah:


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
}

di mana reminderIEEE adalah pengingat murni di GF (2) [x]

Gabriel Furstenheim
sumber
1
im mengalami sedikit (pun intended) dari masalah memahami ini? stackoverflow.com/questions/62168128/…
bluejayke
1
hai @bluejayke, periksa perpustakaan ini github.com/furstenheim/sparse_crc32/blob/master/main.go itu mengimplementasikan crc32 untuk file sparse, Anda dapat melihat di sana detail seluk beluk tentang komputasi. Ini tidak dioptimalkan sehingga lebih mudah diikuti daripada implementasi normal. Bisa jadi yang tidak anda pahami adalah bagian GF (2) [x]. Pada dasarnya x ^ 3 + x berarti 1010, x ^ 4 + x + 1 artinya 10011. Kemudian anda perlu melakukan pembagian, misal x ^ 3 + x adalah x * (x ^ 2 + 1). jadi pengingat dari x ^ 3 + x di atas x adalah 0, tetapi di atas x ^ 2 akan menjadi x ^ 2 * x + x, artinya, pengingatnya adalah x.
Gabriel Furstenheim
1
@bluejayke dan reminderIEEE berarti pengingat terhadap polinomial terkenal, polinomial IEEE
Gabriel Furstenheim
hai lagi, terima kasih atas tanggapan Anda. Saya hanya mencoba untuk memahami (untuk tujuan javascript) apa yang merepresentasikan "x" dalam polinomial. Apakah "x" semacam kata kode untuk sesuatu yang saya lewatkan di sini? Ada banyak istilah yang membingungkan saya di sini, saya belum pernah mendengar tentang CRC32 sebelumnya, dan bahkan setelah mencari saya tidak dapat menemukan penjelasannya. Untuk PNG misalnya, dikatakan saya perlu mengambil "CRC untuk setiap bagian", apakah itu berarti "untuk semua data dalam potongan"? Tetapi bagaimana cara "menghubungkannya" ke polinomial? Apa arti "x"? Juga ketika dikatakan x ^ 32, apakah itu seperti Math.pow (x, 32) atau bitwise ^
bluejayke
1
Hai @bluejayke, x adalah abstraksi untuk mempermudah komputasi. Itu tidak diharapkan untuk digantikan oleh apa pun. x ^ 2 Maksud saya x * x, sebagai perkalian formal. Di sini chrisballance.com/wp-content/uploads/2015/10/CRC-Primer.html Anda dapat menemukan penjelasan yang bagus tentang divisi itu. Apa yang saya coba dengan jawaban saya adalah mengisi celah antara pembagian (dalam tautan itu) dan perhitungan sebenarnya
Gabriel Furstenheim