很久以来一直都以为eD2k Hash就是MD4(罪魁祸首),这几天才发现错得很离谱。
花了点时间查资料,写了个用以生成eD2k链接的模块,包括AICH部分。代码有个偷懒的地方,读取文件的缓存大小必须为PART_SIZE,如果要拿去用的话请留意。Ruby 1.8.6和1.8.7的File.size在Windows平台下有bug,当文件尺寸比较大时会返回负数,这一点也请留意。
部分代码移植自pyaich和Python的Base64模块,GPL许可证。
require 'openssl' module OtNtH module Digest # fit for eD2k Hash PART_SIZE = 9_728_000 BLOCK_SIZE = 184_320 class ED2K def initialize @h = '' @l = 0 end def update(part) @h += OpenSSL::Digest::MD4.new(part).digest @l = part.size end def hexdigest @h += OpenSSL::Digest::MD4.new('').digest if @l == PART_SIZE OpenSSL::Digest::MD4.new(@h).hexdigest end def name 'ED2K' end end # class ED2K class AICH class HashTree attr_accessor :dataSize, :isLeft, :baseSize, :hash, :leftTree, :rightTree def initialize(dataSize, isLeftBranch, baseSize) @dataSize = dataSize @isLeft = isLeftBranch @baseSize = baseSize @hash = nil @leftTree = nil @rightTree = nil end def loadLowestLevelHashes(hashList) if @dataSize <= @baseSize then @hash = hashList.shift return true else extra = @dataSize % @baseSize != 0 ? 1 : 0 numBlocks = @dataSize / @baseSize + extra extra = isLeft ? 1 : 0 sizeLeft = ((numBlocks + extra)/2) * @baseSize sizeRight = @dataSize - sizeLeft leftBaseSize = sizeLeft <= PART_SIZE ? BLOCK_SIZE : PART_SIZE @leftTree = HashTree.new(sizeLeft, true, leftBaseSize) rightBaseSize = sizeRight <= PART_SIZE ? BLOCK_SIZE : PART_SIZE @rightTree = HashTree.new(sizeRight, false, rightBaseSize) return @leftTree.loadLowestLevelHashes(hashList) && @rightTree.loadLowestLevelHashes(hashList) end end def reCalculateHash if @leftTree and @rightTree then return false if (not @leftTree.reCalculateHash) or (not @rightTree.reCalculateHash) h = OpenSSL::Digest::SHA1.new h.update(@leftTree.hash) h.update(@rightTree.hash) @hash = h.digest return true else return true end end end def initialize @hash_list = [] @data_size = 0 end def update(part) psize = part.size @data_size += psize pos = 0 while pos < psize @hash_list.push OpenSSL::Digest::SHA1.new(part[pos, BLOCK_SIZE]).digest pos += BLOCK_SIZE end end def hexdigest blockSize = @data_size <= PART_SIZE ? BLOCK_SIZE : PART_SIZE tree = HashTree.new(@data_size, true, blockSize) tree.loadLowestLevelHashes(@hash_list) tree.reCalculateHash return Base32.encode32(tree.hash) end def name 'ED2K' end end # class AICH end # module Digest module Base32 B32TAB = 'abcdefghijklmnopqrstuvwxyz23456789' module_function def encode32(str) parts = [] quanta, leftover = str.size.divmod(5) if leftover != 0 then str += ("\0" * (5 - leftover)) quanta += 1 end quanta.times do |i| c1, c2, c3 = str[i*5..(i+1)*5].unpack('nnC') c2 += (c1 & 1) << 16 c3 += (c2 & 3) << 8 parts.push( B32TAB[c1 >> 11], B32TAB[(c1 >> 6) & 0x1f], B32TAB[(c1 >> 1) & 0x1f], B32TAB[c2 >> 12], B32TAB[(c2 >> 7) & 0x1f], B32TAB[(c2 >> 2) & 0x1f], B32TAB[c3 >> 5], B32TAB[c3 & 0x1f] ) end encoded = parts.join('') case leftover when 1 encoded[0..-6] + '=====' when 2 encoded[0..-4] + '====' when 3 encoded[0..-3] + '===' when 4 encoded[0..-1] + '=' else encoded end end end # module Base32 end # module OtNtH if __FILE__ == $0 then exit if not File.exists?(ARGV[0]) a = OtNtH::Digest::AICH.new e = OtNtH::Digest::ED2K.new File.open(ARGV[0], 'rb') do |f| buf = '' while f.read(OtNtH::Digest::PART_SIZE, buf) a.update(buf) e.update(buf) end end puts "ed2k://|file|#{File.basename(ARGV[0])}|#{File.size(ARGV[0])}|#{e.hexdigest}|h=#{a.hexdigest}|/" end
1 条评论 :
pyaich 打不开网址:https://sharesource.org/hg/pyaich/file/1a8c8b91e371/aich.py
能提供 python 代码吗?
发表评论