Является ли CRC32 аддитивным?

В нескольких местах я читал, что crc32 является аддитивным, и поэтому: CRC(A xor B) = CRC(A) xor CRC(B).

Приведенное выше утверждение было опровергнуто следующим кодом, который я написал:

import zlib
def crc32(data):
        return zlib.crc32(data) & 0xffffffff

print crc32(chr(ord("A") ^ ord("B")))
print crc32("A") ^ crc32("B")

Вывод программы:

1259060791
2567524794

Может ли кто-нибудь предоставить правильный код, подтверждающий эту теорию, или указать мне, где я потерпел неудачу?


person Ryan82    schedule 08.08.2011    source источник
comment
Сначала я неправильно понял это - я подумал, ну, конечно, я часто использую CRC32, но я мог бы выйти, когда захочу... Действительно, я мог бы...   -  person Joe Enos    schedule 08.08.2011
comment
Не могли бы вы привести какие-нибудь источники, в которых говорится, что crc(A ^ B) = crc(A) ^ crc(B) как google меня не устраивает.   -  person Hyperboreus    schedule 08.08.2011
comment
Разве вы не ответили на свой вопрос, доказав несостоятельность этой гипотезы?   -  person Jordan    schedule 08.08.2011
comment
CRC является аддитивным по отношению к конкатенации сообщений: CRC(A || B, iv) == CRC(B, CRC(A, iv)), где A и B — две части сообщения, || — соответствующий оператор конкатенации, а iv — «вектор инициализации» для вычисления CRC, например. общий 0xffffffff. Это означает, что, имея только значение CRC для сообщения A и сообщения B, CRC(A || B) можно тривиально вычислить без необходимости обращаться к фактическому сообщению A.   -  person JimmyB    schedule 04.06.2012


Ответы (4)


CRC является аддитивным в математическом смысле, поскольку хэш CRC — это просто остаток от деления без переноса всех данных (рассматриваемых как гигантское целое число), деленного на полиномиальную константу. Используя ваш пример, это похоже на такие вещи:

7 по модулю 5 = 2

6 мод 5 = 1

(7 мод 5) + (6 мод 5) = 3

(7 + 6) по модулю 5 = 3

В этой аналогии «5» — это наш многочлен CRC.

Вот пример для игры (на основе gcc):

#include <stdio.h>
#include <x86intrin.h>

int main(void)
{
        unsigned int crc_a = __builtin_ia32_crc32si( 0, 5);
        printf( "crc(5) = %08X\n", crc_a );
        unsigned int crc_b = __builtin_ia32_crc32si( 0, 7);
        printf( "crc(7) = %08X\n", crc_b );
        unsigned int crc_xor = crc_a ^ crc_b;
        printf( "crc(5) XOR crc(7) = %08X\n", crc_xor );
        unsigned int crc_xor2 = __builtin_ia32_crc32si( 0, 5 ^ 7);
        printf( "crc(5 XOR 7) = %08X\n", crc_xor2 );

        return 0;
}

Результат ожидаемый:

plxc15034> gcc -mcrc32 -Wall -O3 crctest.c
plxc15034> ./a.out
crc(5) = A6679B4B
crc(7) = 1900B8CA
crc(5) XOR crc(7) = BF672381
crc(5 XOR 7) = BF672381

Поскольку этот код использует инструкцию x86 CRC32, он будет работать только на Intel i7 или новее. Встроенная функция принимает текущий хэш CRC в качестве первого параметра и новые данные для накопления в качестве второго параметра. Возвращаемое значение — это новая работающая CRC.

Начальное текущее значение CRC, равное 0, в приведенном выше коде является критическим. Используя любое другое начальное значение, CRC не является «аддитивным» в практическом смысле, потому что вы эффективно отбрасываете информацию о целом, на которое вы делите. И это именно то, что происходит в вашем примере. Функции CRC никогда не инициализируют это начальное текущее значение CRC нулем, а обычно -1. Причина в том, что начальный CRC, равный 0, позволяет любому количеству начальных 0 в данных просто провалиться без изменения текущего значения CRC, которое остается равным 0. Таким образом, инициализация CRC равным 0 математически обоснована, но для практических целей расчета хэш, это последнее, что вам нужно.

person srking    schedule 09.08.2011

Алгоритм CRC-32 основан на полиномиальном делении с добавлением некоторых дополнительных шагов. Чистый полиномиальный остаток является аддитивным.

Под этим я подразумеваю: mod(poly1 + poly2, poly3) = mod(mod(poly1, poly3) + mod(poly2, poly3), poly3)

Алгоритм CRC-32 основан на этом и не является аддитивным. Чтобы вычислить CRC-32 массива байтов m:

  1. XOR первых 4 байтов на 0xFFFFFFFF.
  2. Обрабатывайте более ранние байты как более высокие полиномиальные степени, а биты более низкого порядка — как более высокие полиномиальные степени. Например, байты 0x01 0x04 будут полиномом x^15 + x^3.
  3. Умножьте многочлен на x^32.
  4. Возьмите остаток этого полинома, разделенный на полином CRC-32, 0x104C11DB7. Остаточный полином имеет степень ‹ 32.
  5. Относитесь к более низким степеням как к битам более высокого порядка. Например, многочлен x^2 будет 32-битным целым числом 0x40000000.
  6. XOR результата на 0xFFFFFFFF.

Чистая операция полиномиального остатка находится на шаге № 4. Шаги №1 и №6 делают алгоритм CRC-32 неаддитивным. Поэтому, если вы отмените действие шагов № 1 и № 6, вы можете изменить алгоритм CRC-32, чтобы он был аддитивным.

(См. также: Python CRC-32 woes)

person Nayuki    schedule 10.08.2011

Если a, b и c имеют одинаковую длину, CRC(a) xor CRC(b) xor CRC(c) равняется CRC(a xor b xor c). Возвращаясь к исходной формулировке, это означает, что CRC(a xor b) равно CRC(a) xor CRC(b) xor CRC(z), где z — последовательность нулей той же длины, что и две другие последовательности.

person supercat    schedule 23.02.2012
comment
Я могу подтвердить, что CRC(A ⊕ B) = CRC(A) ⊕ CRC(B) неверно, а CRC(A ⊕ B ⊕ C) = CRC(A) ⊕ CRC(B) ⊕ CRC(C), следовательно, CRC(A ⊕ B ⊕ C ⊕ D ⊕ E) = CRC(A) ⊕ CRC(B) ⊕ CRC(C) ⊕ CRC(D) ⊕ CRC(E) (если заменить C на C ⊕ D ⊕ E). Есть ли причина, по которой он работает для нечетного числа операндов, но не для четного числа? - person Victor; 09.03.2014
comment
Если бы CRC32 использовала вектор инициализации 0x00000000, то CRC(A ⊕ B) = CRC(A) ⊕ CRC(B) сохранялось бы, но нечувствительно к начальным нулям. CRC обычно использует вектор инициализации 0xFFFFFFFF (заставляя его вести себя как CRC с нулевой инициализацией последовательности, первые четыре байта которой объединены xor'ом с FF). Если определить CRCZ как CRC, выполняемую с нулевым вектором инициализации, то CRC(X) = CRCZ(x ⊕ FFFFFFFF00..) и CRC(X ⊕ Y) = CRCZ(x ⊕ y ⊕ FFFFFFFF00...). Двухэлементное выражение CRC(X) ⊕ CRC(Y)... - person supercat; 09.03.2014
comment
... эквивалентен CRCZ(x ⊕ FFFFFFFF00...) ⊕ CRCZ(y ⊕ FFFFFFFF00...), который, в свою очередь, эквивалентен CRCZ(x ⊕ Y ⊕ FFFFFFFF00... ⊕ FFFFFFFF00...) и, следовательно, CRCZ(x ⊕ Y) [который не равен CRCZ(x ⊕ Y ⊕ FFFFFFFF00...) и, следовательно, не равен CRC(x ⊕ Y)]. Для арифметической аналогии определите f(x)=-x и используйте умножение, а не xor. Тогда f(x·y·z) = -(x·y·z) = (-x)(-y)(-z) = f(x)·f(y)·f(z), но f( x·y) = -(x·y) = -((-x)·(-y)) = -(f(x)·f(y)). - person supercat; 09.03.2014

Это будет означать, что каждая битовая позиция результата CRC управляется только эквивалентной битовой позицией во входных данных. Рассмотрим ваш пример с B == 0.

Отношения, которые вы описываете, скорее всего, верны для некоторых примитивных алгоритмов xor или аддитивных алгоритмов контрольной суммы.

person janm    schedule 08.08.2011