Skip to content

Instantly share code, notes, and snippets.

@aymkx
Last active November 28, 2017 03:27
Show Gist options
  • Save aymkx/01b2b0e09f6b302914554154a202720b to your computer and use it in GitHub Desktop.
Save aymkx/01b2b0e09f6b302914554154a202720b to your computer and use it in GitHub Desktop.
Implement MD5 in Ruby
module Binary
# or MD5.module_eval(using Module.new{refine Class {~}...})
INTEGER_SIZE = 32
refine Integer do
def bit_rotate(bit_width=INTEGER_SIZE,shift=1)
raise ArgumentError if bit_width.negative?
shift = shift % bit_width
((self << shift) + (self >> (bit_width - shift) & ((1 << shift) - 1))) & ((1 << bit_width) - 1)
end
def not(bit_width=INTEGER_SIZE)
raise ArgumentError if bit_width.negative?
(~self) & ((1 << bit_width) - 1)
end
end
refine Array do
def stack(bytes=1)
self.inject {|m,n| (m << (8*bytes)) + n}
end
# if little endian, then Array#reverse.stack
end
end
module MD5
using Binary
CONST_T = Array.new(64) {|n| ((1 << 32)*Math.sin(n+1).abs).floor}
SHIFT_COUNT = [[7,12,17,22], [5,9,14,20], [4,11,16,23], [6,10,15,21]]
DATA_STRIP_INDEX = Array.new(64) do |i|
case i
when (0..15) then i
when (16..31) then (5*i+1)%16
when (32..47) then (3*i+5)%16
when (48..63) then (7*i)%16
end
end
INITIAL_BUF = [0x67452301,0xefcdab89,0x98badcfe,0x10325476]
def md5(data)
if data.kind_of?(String) then data_type = 1
elsif data.kind_of?(File) then data_type = 2
else raise TypeError end
data_length = data_type == 1 ? data.bytesize : data.size
pad_length = (56 - ((data_length + 1) % 64)).modulo(64) + 1
pad = Array.new(pad_length,0); pad[0] = 0x80
pad += Array.new(8) {|i| (data_length >> (8*i)) & 0xff}
data_pad_length = data_length + pad.size
# That's all for padding
# Calculating follows
buffer = INITIAL_BUF
data_pad_length.div(64).times do |l|
if data_type == 1 then
if data_length >= 64*(l+1) then cache = data.unpack('C*')[(64*l),64]
elsif pad.size <= 64 then cache = data.unpack('C*')[(64*l)..-1] + pad
else cache = pad[-64,64] end
else
if data_length >= 64*(l+1) then cache = data.read(64).unpack('C*')
elsif pad.size <= 64 then cache = data.read.unpack('C*') + pad
else cache = pad[-64,64] end
end
data_strip = Array.new(16) {|i| cache[(4*i),4].stack}
buffer_cache = buffer
(0..15).each do |r|
a,b,c,d = buffer
a += ((b & c) | (b.not(32) & d)) + data_strip[DATA_STRIP_INDEX[r]] + CONST_T[r]
a = a.bit_rotate(32,SHIFT_COUNT[0][r%4]) & 0xffff_ffff
buffer = [d,a,b,c]
end
(16..31).each do |r|
a,b,c,d = buffer
a += ((b & d) | (c & d.not(32))) + data_strip[DATA_STRIP_INDEX[r]] + CONST_T[r]
a = a.bit_rotate(32,SHIFT_COUNT[1][r%4]) & 0xffff_ffff
buffer = [d,a,b,c]
end
(32..47).each do |r|
a,b,c,d = buffer
a += (b ^ c ^ d) + data_strip[DATA_STRIP_INDEX[r]] + CONST_T[r]
a = a.bit_rotate(32,SHIFT_COUNT[2][r%4]) & 0xffff_ffff
buffer = [d,a,b,c]
end
(48..63).each do |r|
a,b,c,d = buffer
a += (c ^ (b | d.not(32))) + data_strip[DATA_STRIP_INDEX[r]] + CONST_T[r]
a = a.bit_rotate(32,SHIFT_COUNT[3][r%4]) & 0xffff_ffff
buffer = [d,a,b,c]
end
4.times {|i| buffer[i] = (buffer[i] + buffer_cache[i]) & 0xffff_ffff}
end
buffer.pack('V*')
end
module_function :md5
class << self
def digest(data)
md5(data)
end
def hexdigest(data)
md5(data).unpack('H*').first
end
end
end
@aymkx
Copy link
Author

aymkx commented Nov 6, 2017

値が合わないけど原因がわからん。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment