Skip to content

Commit

Permalink
Implemented MD5 in Crystal
Browse files Browse the repository at this point in the history
  • Loading branch information
Ary Borenszweig committed Mar 4, 2015
1 parent 10882b3 commit 48461ba
Showing 1 changed file with 249 additions and 12 deletions.
261 changes: 249 additions & 12 deletions src/crypto/md5.cr
Original file line number Diff line number Diff line change
@@ -1,18 +1,255 @@
require "openssl/md5"

This comment has been minimized.

Copy link
@kostya

kostya Mar 4, 2015

Contributor

why this better than use openssl/md5?

This comment has been minimized.

Copy link
@asterite

asterite Mar 4, 2015

Member

It's not better. In fact, we did some benchmarks and it's a bit slower (it turns out openssl/md5 is implemented in assembler). But it's like 2 times slower, and openssl/md5 is very fast, so for now it's acceptable.

The problem was that the compiler uses MD5 after this commit and so it started depending on libcrypto. That meant changing our omnibus recipe to take that into account. That, or implementing MD5 in Crystal. So we did this last thing, which is easier for us for now, and also makes libraries using only MD5 free from the libcrypto dependency.

This comment has been minimized.

Copy link
@asterite

asterite Mar 4, 2015

Member

Once we add inline assembler support we can improve this ;-)


class Crypto::MD5
def self.hex_digest(data : String)
hash = OpenSSL::MD5.hash(data)
hash_str = String.new(32) do |buffer|
0.upto(15) do |i|
buffer[i * 2] = to_hex((hash[i]) >> 4)
buffer[i * 2 + 1] = to_hex(hash[i] & 0x0f)
context = Context.new
context.update(data.to_unsafe, data.bytesize.to_u32)
context.final
context.hex
end

struct Context
def initialize
@i = StaticArray(UInt32, 2).new(0_u32)
@buf = StaticArray(UInt32, 4).new(0_u32)
@buf[0] = 0x67452301_u32
@buf[1] = 0xEFCDAB89_u32
@buf[2] = 0x98BADCFE_u32
@buf[3] = 0x10325476_u32
@in = StaticArray(UInt8, 64).new(0_u8)
@digest :: UInt8[16]
end

def update(inBuf, inLen)
in :: UInt32[16]

# compute number of bytes mod 64
mdi = (@i[0] >> 3) & 0x3F

# update number of bits
@i[1] += 1 if (@i[0] + (inLen << 3)) < @i[0]
@i[0] += (inLen << 3)
@i[1] += (inLen >> 29)

inLen.times do
# add new character to buffer, increment mdi
@in[mdi] = inBuf.value
mdi += 1
inBuf += 1

# transform if necessary
if mdi == 0x40
ii = 0
16.times do |i|
in[i] = (@in[ii+3].to_u32 << 24) |
(@in[ii+2].to_u32 << 16) |
(@in[ii+1].to_u32 << 8) |
(@in[ii])
ii += 4
end
transform in
mdi = 0
end
end
{32, 32}
end
end

private def self.to_hex(c)
((c < 10 ? 48_u8 : 87_u8) + c)
S11 = 7
S12 = 12
S13 = 17
S14 = 22

S21 = 5
S22 = 9
S23 = 14
S24 = 20

S31 = 4
S32 = 11
S33 = 16
S34 = 23

S41 = 6
S42 = 10
S43 = 15
S44 = 21

PADDING = begin
padding = StaticArray(UInt8, 64).new(0_u8)
padding[0] = 0x80_u8
padding
end

def f(x, y, z)
(x & y) | ((~x) & z)
end

def g(x, y, z)
(x & z) | (y & (~z))
end

def h(x, y, z)
x ^ y ^ z
end

def i(x, y, z)
y ^ (x | (~z))
end

def rotate_left(x, n)
(x << n) | (x >> (32-n))
end

def ff(a, b, c, d, x, s, ac)
a += f(b, c, d) + x + ac.to_u32
a = rotate_left a, s
a += b
end

def gg(a, b, c, d, x, s, ac)
a += g(b, c, d) + x + ac.to_u32
a = rotate_left a, s
a += b
end

def hh(a, b, c, d, x, s, ac)
a += h(b, c, d) + x + ac.to_u32
a = rotate_left a, s
a += b
end

def ii(a, b, c, d, x, s, ac)
a += i(b, c, d) + x + ac.to_u32
a = rotate_left a, s
a += b
end

def transform(in)
a, b, c, d = @buf

# Round 1
a = ff( a, b, c, d, in[ 0], S11, 3614090360); # 1
d = ff( d, a, b, c, in[ 1], S12, 3905402710); # 2
c = ff( c, d, a, b, in[ 2], S13, 606105819); # 3
b = ff( b, c, d, a, in[ 3], S14, 3250441966); # 4
a = ff( a, b, c, d, in[ 4], S11, 4118548399); # 5
d = ff( d, a, b, c, in[ 5], S12, 1200080426); # 6
c = ff( c, d, a, b, in[ 6], S13, 2821735955); # 7
b = ff( b, c, d, a, in[ 7], S14, 4249261313); # 8
a = ff( a, b, c, d, in[ 8], S11, 1770035416); # 9
d = ff( d, a, b, c, in[ 9], S12, 2336552879); # 10
c = ff( c, d, a, b, in[10], S13, 4294925233); # 11
b = ff( b, c, d, a, in[11], S14, 2304563134); # 12
a = ff( a, b, c, d, in[12], S11, 1804603682); # 13
d = ff( d, a, b, c, in[13], S12, 4254626195); # 14
c = ff( c, d, a, b, in[14], S13, 2792965006); # 15
b = ff( b, c, d, a, in[15], S14, 1236535329); # 16

# Round 2
a = gg( a, b, c, d, in[ 1], S21, 4129170786); # 17
d = gg( d, a, b, c, in[ 6], S22, 3225465664); # 18
c = gg( c, d, a, b, in[11], S23, 643717713); # 19
b = gg( b, c, d, a, in[ 0], S24, 3921069994); # 20
a = gg( a, b, c, d, in[ 5], S21, 3593408605); # 21
d = gg( d, a, b, c, in[10], S22, 38016083); # 22
c = gg( c, d, a, b, in[15], S23, 3634488961); # 23
b = gg( b, c, d, a, in[ 4], S24, 3889429448); # 24
a = gg( a, b, c, d, in[ 9], S21, 568446438); # 25
d = gg( d, a, b, c, in[14], S22, 3275163606); # 26
c = gg( c, d, a, b, in[ 3], S23, 4107603335); # 27
b = gg( b, c, d, a, in[ 8], S24, 1163531501); # 28
a = gg( a, b, c, d, in[13], S21, 2850285829); # 29
d = gg( d, a, b, c, in[ 2], S22, 4243563512); # 30
c = gg( c, d, a, b, in[ 7], S23, 1735328473); # 31
b = gg( b, c, d, a, in[12], S24, 2368359562); # 32

# Round 3
a = hh( a, b, c, d, in[ 5], S31, 4294588738); # 33
d = hh( d, a, b, c, in[ 8], S32, 2272392833); # 34
c = hh( c, d, a, b, in[11], S33, 1839030562); # 35
b = hh( b, c, d, a, in[14], S34, 4259657740); # 36
a = hh( a, b, c, d, in[ 1], S31, 2763975236); # 37
d = hh( d, a, b, c, in[ 4], S32, 1272893353); # 38
c = hh( c, d, a, b, in[ 7], S33, 4139469664); # 39
b = hh( b, c, d, a, in[10], S34, 3200236656); # 40
a = hh( a, b, c, d, in[13], S31, 681279174); # 41
d = hh( d, a, b, c, in[ 0], S32, 3936430074); # 42
c = hh( c, d, a, b, in[ 3], S33, 3572445317); # 43
b = hh( b, c, d, a, in[ 6], S34, 76029189); # 44
a = hh( a, b, c, d, in[ 9], S31, 3654602809); # 45
d = hh( d, a, b, c, in[12], S32, 3873151461); # 46
c = hh( c, d, a, b, in[15], S33, 530742520); # 47
b = hh( b, c, d, a, in[ 2], S34, 3299628645); # 48

# Round 4
a = ii( a, b, c, d, in[ 0], S41, 4096336452); # 49
d = ii( d, a, b, c, in[ 7], S42, 1126891415); # 50
c = ii( c, d, a, b, in[14], S43, 2878612391); # 51
b = ii( b, c, d, a, in[ 5], S44, 4237533241); # 52
a = ii( a, b, c, d, in[12], S41, 1700485571); # 53
d = ii( d, a, b, c, in[ 3], S42, 2399980690); # 54
c = ii( c, d, a, b, in[10], S43, 4293915773); # 55
b = ii( b, c, d, a, in[ 1], S44, 2240044497); # 56
a = ii( a, b, c, d, in[ 8], S41, 1873313359); # 57
d = ii( d, a, b, c, in[15], S42, 4264355552); # 58
c = ii( c, d, a, b, in[ 6], S43, 2734768916); # 59
b = ii( b, c, d, a, in[13], S44, 1309151649); # 60
a = ii( a, b, c, d, in[ 4], S41, 4149444226); # 61
d = ii( d, a, b, c, in[11], S42, 3174756917); # 62
c = ii( c, d, a, b, in[ 2], S43, 718787259); # 63
b = ii( b, c, d, a, in[ 9], S44, 3951481745); # 64

@buf[0] += a
@buf[1] += b
@buf[2] += c
@buf[3] += d
end

def final
in :: UInt32[16]

# save number of bits
in[14] = @i[0]
in[15] = @i[1]

# compute number of bytes mod 64
mdi = ((@i[0] >> 3) & 0x3F).to_i32

# pad out to 56 mod 64
pad_len = (mdi < 56) ? (56 - mdi) : (120 - mdi)
update PADDING.to_unsafe, pad_len

# append length in bits and transform
ii = 0
14.times do |i|
in[i] = (@in[ii+3].to_u32 << 24) |
(@in[ii+2].to_u32 << 16) |
(@in[ii+1].to_u32 << 8) |
(@in[ii])
ii += 4
end
transform in

# store buffer in digest
ii = 0
4.times do |i|
@digest[ii] = (@buf[i] & 0xff).to_u8
@digest[ii + 1] = ((@buf[i] >> 8) & 0xFF).to_u8
@digest[ii + 2] = ((@buf[i] >> 16) & 0xFF).to_u8
@digest[ii + 3] = ((@buf[i] >> 24) & 0xFF).to_u8
ii += 4
end
end

def hex
String.new(32) do |buffer|
0.upto(15) do |i|
buffer[i * 2] = to_hex((@digest[i]) >> 4)
buffer[i * 2 + 1] = to_hex(@digest[i] & 0x0f)
end
{32, 32}
end
end

def to_hex(c)
((c < 10 ? 48_u8 : 87_u8) + c)
end
end
end
end

0 comments on commit 48461ba

Please sign in to comment.