Last active
November 28, 2017 03:27
-
-
Save aymkx/01b2b0e09f6b302914554154a202720b to your computer and use it in GitHub Desktop.
Implement MD5 in Ruby
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
値が合わないけど原因がわからん。