2009-06-25 2 views
4

У нас есть очень старая, неподдерживаемая программа, которая копирует файлы через общие ресурсы SMB. У этого есть алгоритм контрольной суммы, чтобы определить, изменилось ли содержимое файла перед копированием. Алгоритм кажется легко обманутым - мы только что нашли пример, когда два файла, идентичные, за исключением одного «1», меняющегося на «2», возвращают ту же контрольную сумму. Вот алгоритм:Можно ли улучшить алгоритм контрольной суммы?

unsigned long GetFileCheckSum(CString PathFilename) 
{ 
     FILE* File; 
     unsigned long CheckSum = 0; 
     unsigned long Data = 0; 
     unsigned long Count = 0; 

     if ((File = fopen(PathFilename, "rb")) != NULL) 
     { 
       while (fread(&Data, 1, sizeof(unsigned long), File) != FALSE) 
       { 
         CheckSum ^= Data + ++Count; 
         Data = 0; 
       } 
       fclose(File); 
     } 
     return CheckSum; 
} 

Я не так много программиста (я сисадмин), но я знаю, исключающее на основе контрольной суммы будет довольно сырой. Какова вероятность того, что этот алгоритм вернет ту же контрольную сумму для двух файлов одного размера с другим содержимым? (Я не ожидаю точного ответа, «удаленный» или «вполне вероятный» в порядке.)

Как его можно улучшить без огромного удара производительности?

И наконец, что происходит с fread()? Я быстро просмотрел документацию, но я не мог понять. Устанавливается ли Data каждому байту файла? Редактировать: хорошо, поэтому он читает файл в unsigned long (предположим, что здесь 32-битная ОС) куски. Что содержит каждый кусок? Если содержимое файла abcd, то каково значение Data на первом проходе? Является ли это (в Perl):

(ord('a') << 24) & (ord('b') << 16) & (ord('c') << 8) & ord('d') 
+2

fread читает один элемент по адресу Data. Размер считываемого элемента - это размер unsigned long (по-моему, 8 байтов). –

+0

Размер unsigned long зависит от типа архитектуры процессора (32/64 бит), поэтому используется sizeof. – schnaader

+0

Исправьте пример Perl. –

ответ

3

Вы можете легко улучшить алгоритм, используя формулу, как это одна:

Checksum = (Checksum * a + Data * b) + c; 

Если, б и большие простые числа, это должно возвращать хорошие результаты , После этого вращение (не сдвигание!) Бит контрольной суммы еще немного улучшит его.

Использование простых чисел, это аналогичный алгоритм, используемый для Linear congruential generators - он гарантирует длительные периоды и хорошее распределение.

+0

Я не уверен, как это помогает распространению! Это помогает в усилении злоумышленного нападения. –

+0

Предполагая, что в файлах много текста ASCII, это гарантирует, что вы не всегда XORing около 5 байтов дисперсии вместе и разбросаете энтропию через контрольную сумму. –

0

Кажется, что ваш алгоритм не прилагает никаких усилий для работы с файлами, которые не являются точными кратными 4 байтам. Возвращаемое значение fread не является логическим, а количество фактически прочитанных байтов, которое будет отличаться от 4 в случае EOF или если произошла ошибка. Вы не проверяетесь ни на одно, но просто считаете, что если он не вернул 0, у вас есть 4 действительных байта в «данных», которые будут вычислять ваш хэш.

Если вы действительно хотите использовать хэш, я бы порекомендовал несколько вещей. Во-первых, используйте простой криптографический хеш, например MD5, а не CRC32. CRC32 подходит для проверки достоверности данных, но для того, чтобы охватить файловую систему и обеспечить отсутствие столкновений, это не такой отличный инструмент из-за парадоксальности дня, упомянутого в комментариях в другом месте. Во-вторых, не записывайте функцию самостоятельно. Найдите существующую реализацию. Наконец, рассмотрите просто использование rsync для репликации файлов, а не для развертывания собственного решения.

+0

Я думаю (если не считать ошибок и длины файла> sizeof (long)) хеш будет возвращать согласованные результаты, так как последние бит последнего чтения будут последовательно удерживаться от последней итерации. – BCS

+0

Это означает, что в коде есть два недостатка, чтобы обеспечить надлежащую функциональность. Если кто-то добавил код для сброса «данных» в 0 между каждым циклом, он также дал бы согласованные результаты, но теперь любые ранее сохраненные значения для CRC неверны. – Jherico

+0

Собственно, удаленный ответ Мартина был на правильном пути. В этом приложении вы пытаетесь определить, совпадают ли два определенных файла, а не один ли файл соответствует любому в коллекции. Таким образом, проблема с днем ​​рождения неприменима. – erickson

0

fread бит читает в файле по одному куску за раз. Каждый кусок - это размер длинного (в c это не определенный размер, но вы можете принять 32 или 64 бита). В зависимости от того, как он буферизуется, это может быть не плохо. OTOH, читая больший кусок в массив и зацикливаясь на нем, может быть намного быстрее.

6

MD5 обычно используется для проверки целостности передаваемых файлов. Исходный код легко доступен в C++. Он широко считается быстрым и точным алгоритмом.

Смотрите также Robust and fast checksum algorithm?

+0

Замечание: MD5 подходит только для проверки целостности файла из надежного источника. Можно создать два файла с одной и той же контрольной суммой MD5, если это сделано заранее, и оба сделаны одновременно. Но трудно создать файл с тем же MD5, что и другой. – rlbond

+0

Если вы не заботитесь о криптопрочности, MD4 немного проще и быстрее, а CRC-32 заметно проще и быстрее, чем MD5. Однако ни одна из них не будет равна скорости контрольной суммы OP (сломанной). – ephemient

+0

Я думаю, что MD4 и CRC32 будут равны скорости, так как они, вероятно, связаны с I/O. С современными процессорами даже MD5 может быть привязана к вводу/выводу. – MSalters

0

Даже «дорогие» криптографические хэш-функции, как правило, требуется несколько итераций, чтобы занять значительное количество времени. Хотя они больше не рекомендуются для криптографических целей, когда пользователи намеренно пытаются создавать конфликты, такие функции, как SHA1 и MD5, широко доступны и подходят для этой цели.

Если требуется меньшее значение хэша, CRC в порядке, но не очень. A n -бит CRC не сможет обнаружить небольшую часть изменений, длина которых больше n бит. Например, предположим, что только одна долларовая сумма в файле изменилась с 12 345 до 34 567 долларов. 32-битный CRC может пропустить это изменение.

Усечение результата более длинного криптографического хеша будет определять изменения более надежно, чем CRC.

0
{ 
    CheckSum ^= Data + ++Count; 
    Data = 0; 
} 

Я не думаю, что «++ Count» много работает. Код эквивалентен с

{ 
    CheckSum ^= Data; 
} 

XORing последовательность байтов недостаточно. Особенно с текстовыми файлами.

Предлагаю использовать hash function.

+1

Хорошо, ++ Count делает много работы, например, он предотвращает тривиальные столкновения, такие как chk (ABCD) = chk (DCBA), которые будут возникать при использовании^= данных (A, B, C, D предназначены для беззнаковых длин здесь) – schnaader

+0

Хорошо, но обратите внимание, что это влияет только на 4-й байт в каждом раунде, 3-й каждые 256 петель, 2-й каждые 65536 и т. Д. –

4

Я предлагаю вам взглянуть на Fletcher's checksum, в частности, fletcher-32, который должен быть довольно быстрым и обнаруживать различные вещи, которые не существовала бы в текущей цепочке XOR.

0

SHA-1 и (совсем недавно SHA-2) обеспечивают отличные функции хеширования, и я считаю, что медленно вытесняет MD5 из-за лучших свойств хэширования. Все из них (md2, sha и т. Д.) Имеют эффективные реализации и возвращают хэш буфера длиной несколько символов (хотя всегда фиксированная длина). доказуемо более надежны, чем сокращение хеша до целого числа. Если бы у меня были мои барабанщики, я бы использовал SHA-2. Следуйте за this link для библиотек, которые реализуют контрольные суммы SHA.

Если вы не хотите компилировать в этих библиотеках, Linux (и, возможно, cygwin) имеет следующие исполняемые файлы: md5sum, sha1sum, sha224sum, sha256sum, sha384sum, sha512sum; к которому вы можете предоставить свой файл, и они распечатывают контрольную сумму в виде шестнадцатеричной строки. Вы можете использовать POPEN для выполнения этих программ - с чем-то вроде этого:

const int maxBuf=1024; 
char buf[maxBuf]; 
FILE* f = popen("sha224sum myfile", "w"); 
int bytesRead = f.read(buf, maxBuf); 
fclose(f); 

Очевидно, что это будет работать довольно много медленнее, но делает для полезного первого прохода. Если скорость является проблемой, учитывая, что такие операции хэширования файлов, как это, и привязка ввода-вывода (память и доступ к диску будут вам узкими местами), я ожидал бы, что все эти алгоритмы будут выполняться так же быстро, как и для unsigned int , Perl и Python также поставляются с реализациями MD5 SHA1 и SHA2 и, вероятно, будут работать так же быстро, как в C/C++.

+0

SHA-1 имеет лучшие криптографические свойства, чем MD-5. Это не имеет значения. – MSalters

+0

Может быть. Зависит от приложения. Здесь обсуждаются (криптографические) проблемы с MD5. http://en.wikipedia.org/wiki/MD5 (В нем также говорится, что SHA1 нарушен для криптографических целей, BTW.) – user48956

Смежные вопросы