Почему Crypto++ и Ruby генерируют немного разные хэши SHA-1?

Я использую две разные библиотеки для генерации хэша SHA-1 для использования при проверке файлов — более старая версия Crypto++ и класс Digest::SHA1, реализованный Ruby. Хотя я видел и другие случаи несоответствия хэшей, вызванные различиями в кодировке, две библиотеки выдают хэши, которые почти идентичны.

Например, передача файла через каждый процесс приводит к следующим результатам:

Крипто++ 01c15e4f46d8181b984fa2a2c740f8f67130acac

Рубин: eac15e4f46d8181b984fa2a2c740f8f67130acac

Как видите, различаются только первые два символа хеш-строки, и такое поведение повторяется во многих файлах. Я просмотрел исходный код для каждой реализации, и единственное отличие, которое я обнаружил на первый взгляд, заключалось в шестнадцатеричном формате данных, который используется для 160-битного хеширования. Я понятия не имею, как этот шестнадцатеричный код используется в алгоритме, и я подумал, что мне, вероятно, будет быстрее задать вопрос, если кто-то сталкивался с этой проблемой раньше.

Я включил данные из соответствующих библиотек ниже. Я также включил значения из OpenSSL, так как каждая из трех библиотек имеет несколько разные значения.

Крипто++:

digest[0] = 0x67452301L;
digest[1] = 0xEFCDAB89L;
digest[2] = 0x98BADCFEL;
digest[3] = 0x10325476L;
digest[4] = 0xC3D2E1F0L;

Рубин:

context->state[0] = 0x67452301;
context->state[1] = 0xEFCDAB89;
context->state[2] = 0x98BADCFE;
context->state[3] = 0x10325476;
context->state[4] = 0xC3D2E1F0;

OpenSSL:

#define INIT_DATA_h0 0x67452301UL
#define INIT_DATA_h1 0xefcdab89UL
#define INIT_DATA_h2 0x98badcfeUL
#define INIT_DATA_h3 0x10325476UL
#define INIT_DATA_h4 0xc3d2e1f0UL

Кстати, вот код, используемый для генерации хеша в Ruby. У меня нет доступа к исходному коду реализации Crypto++.

File.class_eval do
    def self.hash_digest filename, options = {}
        opts = {:buffer_length => 1024, :method => :sha1}.update(options)
        hash_func = (opts[:method].to_s == 'sha1') ? Digest::SHA1.new : Digest::MD5.new
        open(filename, "r") do |f|
            while !f.eof
                b = f.read
                hash_func.update(b)
            end
        end
        hash_func.hexdigest
    end
end

person Ryan Bright    schedule 18.08.2010    source источник
comment
Все значения идентичны.   -  person Martin York    schedule 18.08.2010
comment
Эти наборы чисел абсолютно одинаковы. Я не уверен, откуда такая разница, но это не те наборы чисел.   -  person Eric Finn    schedule 18.08.2010
comment
Да, я по сути исключил, что это как-то связано с разницей. Учитывая, что все три библиотеки широко используются, для каждой из них не имеет смысла генерировать разные хэши на основе явных различий в алгоритмах. В основном поэтому я потерян.   -  person Ryan Bright    schedule 18.08.2010
comment
Я бы проверил любой код, который генерирует шестнадцатеричную строку, возможно, есть проблема, когда первый байт преобразуется в строку или неправильное преобразование типа при его объединении.   -  person Kris    schedule 19.08.2010
comment
какая версия ОС/руби? (подсказка: попробуйте открыть его с помощью «rb»)   -  person rogerdpack    schedule 19.08.2010


Ответы (2)


Я предполагаю, что вы ошиблись на байт при печати хэшей SHA-1. Можем ли мы увидеть код, который их печатает? Если нет, вот пара потенциально полезных диагностических средств:

  1. Создайте очень короткий файл (скажем, одно слово) и поместите его содержимое в виде шестнадцатеричной строки по адресу http://www.fileformat.info/tool/hash.htm. Однако вам нужно точно знать шестнадцатеричное содержимое файла. Вы можете использовать xxd для этого в Unix, но вам придется следить за проблемами с порядком байтов. Я не уверен, как это сделать на других ОС.

  2. Всегда ли запуск одного и того же файла через одну и ту же реализацию SHA-1 несколько раз выводит одно и то же значение в этом первом байте? Если да, то меняется ли это значение при смене файлов?

person DAMF    schedule 19.08.2010

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

Если я использую ваш метод в следующей программе, я получаю правильные результаты.

#!/usr/bin/env ruby
require 'digest/sha1'
require 'digest/md5'

File.class_eval do
  def self.hash_digest filename, options = {}
    opts = {:buffer_length => 1024, :method => :sha1}.update(options)
    hash_func = (opts[:method].to_s == 'sha1') ? Digest::SHA1.new : Digest::MD5.new
    open(filename, "r") do |f|
      while !f.eof
        b = f.read
        hash_func.update(b)
      end
    end
    hash_func.hexdigest
  end
end

puts File.hash_digest(ARGV[0])

И его выход по сравнению с OpenSSL.

tmp$ dd if=/dev/urandom of=random.bin bs=1MB count=1
1+0 records in
1+0 records out
1000000 bytes (1.0 MB) copied, 0.287903 s, 3.5 MB/s
tmp$ ./digest.rb random.bin
a511d8153426ebea4e4694cde78db4e3a9e413d1
tmp$ openssl sha1 random.bin
SHA1(random.bin)= a511d8153426ebea4e4694cde78db4e3a9e413d1

Так что в вашем методе хеширования нет ничего плохого. Что-то идет не так между возвращаемым значением и его печатью.

person AboutRuby    schedule 22.08.2010