Як обчислюється контрольна сума CRC32?


102

Можливо, я просто не бачу цього, але CRC32 здається або непотрібним, або недостатньо поясненим, де б я не міг знайти в Інтернеті.

Я розумію, що це залишок від арифметичного поділу значення повідомлення, що не ґрунтується на перенесенні, розділеного на (багатогранник) поліномом, але фактична реалізація цього мені уникає.

Я прочитав "Безболісний посібник з алгоритмів виявлення помилок CRC" , і повинен сказати, що це не було безболісно. Це досить добре перебирає теорію, але автор ніколи не доходить до простого "це все". Він говорить, які параметри є для стандартного алгоритму CRC32, але він нехтує чітко викласти, як ви до нього дійшли.

Мені здається, коли він каже: "це все", а потім додає: "о, до речі, це можна змінити або почати з різними початковими умовами", і не дає чіткої відповіді, яким буде остаточний шлях обчислення контрольної суми CRC32, враховуючи всі зміни, які він щойно додав.

  • Чи існує простіше пояснення того, як розраховується CRC32?

Я намагався кодувати на C, як формується таблиця:

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

але це, здається, породжує значення, несумісні зі значеннями, які я знайшов в інших місцях Інтернету. Я міг би використати значення, знайдені в Інтернеті, але я хочу зрозуміти, як вони були створені.

Будемо дуже вдячні за будь-яку допомогу у роз’ясненні цих неймовірно заплутаних цифр .


9
Ваш код для створення таблиці CRC32 виявляється правильним. Ваш lsbit-first ( зворотний ) поліном CRC32 0xEDB88320також може бути записаний msbit-first ( звичайним ) як 0x04C11DB7. Чи були знайдені в таблиці значення таблиці створені з використанням того самого полінома CRC?
jschmier

1
@jschmier привіт, я відчуваю, що я на крок за цим хлопцем, який задає питання? stackoverflow.com/questions/62168128 / ...
bluejayke

Якщо комусь ще цікаво прочитати "Безболісне керівництво до алгоритмів виявлення помилок CRC", на яке посилається вище, ця оригінальна URL-адреса шлангова, але Google легко знайшов кілька копій, зокрема цю: zlib.net/crc_v3.txt
Стефан,

Відповіді:


114

Поліном для 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

Або в шістнадцятковому та двійковому:

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

Найвищий термін (x 32 ), як правило, не написаний явно, тому він може бути представлений у шістнадцятковій формі так само

0x 04 C1 1D B7

Не соромтеся рахувати 1 і 0, але ви виявите, що вони збігаються з поліномом, де 1біт 0 (або перший біт), і xбіт 1 (або другий біт).

Чому цей поліном? Оскільки для даного полінома повинен бути стандарт, а стандарт був встановлений IEEE 802.3. Крім того, надзвичайно важко знайти поліном, який ефективно виявляє різні бітові помилки.

Ви можете уявити CRC-32 як серію "двійкової арифметики без перенесення" або, в основному, "XOR та операції зсуву". Це технічно називається поліноміальна арифметика.

Щоб краще зрозуміти це, подумайте про таке множення:

(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

Якщо ми припустимо, що х - основа 2, то отримаємо:

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

Чому? Оскільки 3x ^ 3 - це 11x ^ 11 (але нам потрібна лише 1 або 0 попередньо розрядних цифр), тому ми переносимо:

=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

Але математики змінили правила так, що це mod 2. Отже, в основному будь-який двійковий поліном mod 2 - це просто додавання без перенесення або XOR. Отже, наше вихідне рівняння виглядає так:

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

Я знаю, що це стрибок віри, але це виходить за рамки моїх можливостей програміста ліній. Якщо ви твердий студент CS або інженер, я кидаю виклик це розбити. Кожен виграє від цього аналізу.

Отже, щоб опрацювати повний приклад:

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

Тепер ми ділимо доповнене повідомлення на полі, використовуючи арифметику CRC. Це такий самий поділ, як і раніше:

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

Ділення дає частку, яку ми викидаємо, і залишок, який є розрахунковою контрольною сумою. На цьому обчислення закінчується. Зазвичай контрольна сума додається до повідомлення та передається результат. У цьому випадку передача буде: 11010110111110.

Використовуйте лише 32-розрядне число як дільник, а весь потік - як дивіденд. Викиньте частку, а залишок збережіть. Закріпіть залишок у кінці вашого повідомлення, і ви отримаєте CRC32.

Середній відгук хлопця:

         QUOTIENT
        ----------
DIVISOR ) DIVIDEND
                 = REMAINDER
  1. Візьміть перші 32 біти.
  2. Біт зсуву
  3. Якщо 32 біти менше, ніж DIVISOR, перейдіть до кроку 2.
  4. XOR 32 біти від DIVISOR. Перейдіть до кроку 2.

(Зверніть увагу, що потік повинен ділитися на 32 біти, або він повинен бути заповнений. Наприклад, 8-розрядний потік ANSI повинен бути забитий. Також у кінці потоку поділ зупинено.)


13
+1 для "Середнього огляду хлопця" наприкінці - можливо, розгляньте можливість перенести це право вгору - такий собі TL; DR: P
aaronsnoswell

4
@abstractnature Пам'ятайте, що ми ділимо багаточлени, а не лише двійкові числа. Ми не можемо зробити "звичайне" віднімання, оскільки не можемо "позичити" $ x ^ n $ з $ x ^ {n + 1} $; це різні речі. Крім того, оскільки біти лише 0 або 1, що б взагалі було -1? Справді, ми працюємо в кільці багаточленів з коефіцієнтами в полі $ Z / 2Z $, яке має лише два елементи, 0 і 1, і де $ 1 + 1 = 0 $. Поставивши коефіцієнти в поле, тоді поліноми утворюють те, що називається Евклідовим Доменом, що в основному просто дозволяє чітко визначити те, що ми намагаємось зробити.
calavicci

6
Просто для уточнення фактичного полінома дорівнює 100000100110000010001110110110111 = 0x104C11DB7. MSB є неявним, але все-таки його слід враховувати при реалізації. Оскільки він завжди буде встановлений, оскільки багаточлен повинен мати довжину 33 біти (отже, решта може бути довшим 32 біти), деякі люди опускають MSB.
Феліпе Т.

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. Математика працює не так. Коефіцієнти для полінома є mod (2) або GF (2), x залишаються поодинці, в результаті чого x ^ 6 + x ^ 5 + x ^ 4 + x ^ 3 + x ^ 2 + x ^ 1 + x ^ 0 (оскільки 3 мод (2) = 1). Tack the remainder on the end of your message- технічно залишок віднімається з 0 бітів, які були додані до повідомлення, але оскільки це mod (2) math, і додавання, і віднімання збігаються з XOR, а нульові біти XOR'ed з залишком однакові як залишок.
rcgldr

2
@MarcusJ - Why did you append four 0s though?- програмні алгоритми для обчислення CRC ефективно додають 0, хоча це не очевидно. Якщо показ обчислення CRC використовується за допомогою поділу довгою рукою, тоді потрібно додати 0, щоб приклад ділення відображався правильно.
rcgldr

11

Для IEEE802.3, CRC-32. Подумайте про все повідомлення як про послідовний бітовий потік, додайте 32 нулі до кінця повідомлення. Далі, ви ПОВИННІ змінити біти КОЖНОГО байта повідомлення і зробити одиниці, доповнюючи перші 32 біти. Тепер поділіть на поліном CRC-32, 0x104C11DB7. Нарешті, ви повинні доповнити 32-бітову залишок цього поділу біт-реверсом кожного з 4 байтів залишку. Це стає 32-розрядною CRC, яка додається до кінця повідомлення.

Причиною цієї дивної процедури є те, що перші реалізації Ethernet будуть серіалізувати повідомлення по одному байту за раз і передаватимуть найменш значущий біт кожного байту першим. Потім послідовний бітовий потік пройшов послідовний обчислювальний регістр зсуву CRC-32, який просто доповнювався і розсилався по дроту після завершення повідомлення. Причиною доповнення перших 32 бітів повідомлення є те, що ви не отримаєте абсолютно нульовий CRC, навіть якщо повідомлення було з нулями.


2
Це найкраща відповідь на даний момент, хоча я б замінив "біт-реверс кожного з 4 байт" на "біт-реверс 4 байтів, обробляючи їх як одну сутність", наприклад "abcdefgh ijklmnop qrstuvwx yzABCDEF" на "FEDCBAzy xwvutsrq ponmlkji hgfedcba '. Дивіться також: Підручник з хешу CRC-32 - Спільнота AutoHotkey .
vafylec

1
привіт, яке саме "повідомлення" точне; stackoverflow.com/questions/62168128 / ...
bluejayke

10

CRC досить простий; ви берете поліном, представлений у вигляді бітів та даних, і ділите поліном на дані (або ви представляєте дані як поліном і робите те саме). Залишок, який знаходиться між 0 і багаточленом, є CRC. Ваш код трохи важко зрозуміти, частково тому, що він неповний: temp та testcrc не оголошуються, тому незрозуміло, що індексується та скільки даних проходить через алгоритм.

Спосіб зрозуміти CRC - це спробувати обчислити кілька, використовуючи короткий фрагмент даних (16 біт або близько того) з коротким поліномом - можливо, 4 біти. Якщо ви практикуєтесь таким чином, ви дійсно зрозумієте, як ви можете піти щодо його кодування.

Якщо ви часто це робите, CRC досить повільно обчислюється в програмному забезпеченні. Апаратні обчислення набагато ефективніші і вимагають лише декількох затворів.


1
Для CRC32 або CRC32b, чи отримуємо ми значення колізійного зіткнення для двох різних рядків, отримуємо однаковий CRC
indianwebdevil

1
привіт, я трохи заплутався, що ви маєте на увазі під "поділом поліномів на дані"? stackoverflow.com/questions/62168128/… що таке X у поліномі, представленому? Чи використовувати я зовнішні байти з фрагмента?
bluejayke

7

На додаток до перевірки циклічної надмірності у Вікіпедії та обчислення статей CRC , я знайшов хороший довідковий документ під назвою Reversing CRC - Theory and Practice * .

По суті, існує три підходи до обчислення CRC: алгебраїчний, бітоорієнтований та таблично-керований. У статті Reversing CRC - Theory and Practice * кожен із цих трьох алгоритмів / підходів теоретично пояснюється в супроводі в ДОДАТКУ реалізації для CRC32 мовою програмування C.

* Зворотний CRC для посилання PDF
- теорія та практика.
Публічний звіт HU Берлін
SAR-PR-2006-05
травня 2006 р.
Автори:
Мартін Штігге, Генрік Плёц, Вольф Мюллер, Йенс-Петер Редліх


привіт, ти можеш трохи додати?
bluejayke

7

Я витратив якийсь час, намагаючись розкрити відповідь на це запитання, і нарешті сьогодні опублікував підручник з CRC-32: Підручник з хешу CRC-32 - Спільнота AutoHotkey

У цьому прикладі я демонструю, як розрахувати хеш CRC-32 для рядка 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
Якщо ви хочете збільшити швидкість, був розроблений деякими інженерами в Intel близько 2006 року, використовуючи, як правило, 4 або 8 байт ширини шини даних машини одночасно. Навчальний документ: static.aminer.org/pdf/PDF/000/432/446/… Проект на Sourceforge: sourceforge.net/projects/slicing-by-8 Загальна сторінка CRC
Алан Кори

1
Привіт, спасибі, виглядає чудово, але як саме ти отримуєш багатозначне значення? що саме X представляє? І коли в ньому сказано x ^ 32, це x до ступеня 32, або побітовий оператор ^? stackoverflow.com/questions/62168128 / ...
bluejayke


1

Щоб звести crc32 до прийняття нагадування, вам потрібно:

  1. Інвертувати біти на кожному байті
  2. xor перші чотири байти з 0xFF (це для уникнення помилок на провідних 0)
  3. Додайте відступ в кінці (це для того, щоб останні 4 байти взяли участь у хеші)
  4. Обчислити нагадування
  5. Знову змінити біти
  6. xor результат знову.

У коді це:


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
}

де нагадуванняIEEE - це чисто нагадування на GF (2) [x]


1
я маю трохи (каламбур) проблем із розумінням цього? stackoverflow.com/questions/62168128 / ...
bluejayke

1
привіт @bluejayke, перевірте цю бібліотеку github.com/furstenheim/sparse_crc32/blob/master/main.go, він реалізує crc32 для розріджених файлів, ви можете побачити там дрібні деталі про обчислення. Він не оптимізований, тому його легше виконувати, ніж звичайні реалізації. Може бути, що ви не розумієте, це частина GF (2) [x]. В основному x ^ 3 + x означає 1010, x ^ 4 + x + 1 означає 10011. Тоді вам потрібно виконати ділення, наприклад x ^ 3 + x - це x * (x ^ 2 + 1). отже, нагадування про x ^ 3 + x над x дорівнює 0, але для x ^ 2 це буде x ^ 2 * x + x, тобто нагадування буде x.
Габріель Фюрстенхайм

1
@bluejayke і нагадуванняIEEE означає нагадування про добре відомий поліном, поліном IEEE
Габріель Фюрстенхайм

привіт ще раз, дякую за вашу відповідь. Я просто намагаюся зрозуміти (для цілей javascript), що означає "х" у поліномі. "X" - це якесь кодове слово для того, чого я тут не маю? Тут є безліч термінів, які мене бентежать, я ніколи раніше не чув про CRC32, і навіть після пошуку я не міг знайти його насправді поясненого. Наприклад, для PNG, там сказано, що мені потрібно взяти "CRC для кожного фрагмента", чи означає це "для всіх даних у фрагменті"? Але як мені «підключити» його до полінома? Що означає "х"? Крім того, коли він пише x x 32, це як Math.pow (x, 32) або побітове ^
bluejayke

1
Привіт @bluejayke, x - це абстракція, яка полегшує обчислення. Не передбачається, що це буде замінено чимось. x ^ 2 Я маю на увазі x * x, як формальне множення. Тут chrisballance.com/wp-content/uploads/2015/10/CRC-Primer.html ви можете знайти гарне пояснення цього поділу. Що я спробував своєю відповіддю, так це заповнити прогалину між поділом (за цим посиланням) та фактичним обчисленням
Габріель Фюрстенхайм
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.