Skip to content

Instantly share code, notes, and snippets.

@havenwood
Last active December 15, 2015 21:08
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save havenwood/b39abc979402ecd1e496 to your computer and use it in GitHub Desktop.
Save havenwood/b39abc979402ecd1e496 to your computer and use it in GitHub Desktop.
The hash function RIPEMD-128 in Ruby
require 'digest'
require 'stringio'
module Digest
class RMD128 < Digest::Class
##
# http://homes.esat.kuleuven.be/~bosselae/ripemd/rmd128.txt
#
# RIPEMD-128 is an iterative hash function that operates on 32-bit words.
# The round function takes as input a 4-word chaining variable and a 16-word
# message block and maps this to a new chaining variable. All operations are
# defined on 32-bit words. Padding is identical to that of MD4.
MASK = (1 << 32).pred
private_constant :MASK
# RIPEMD-128: definitions
# nonlinear functions at bit level: exor, mux, -, mux
F = [ ->(x, y, z) { x ^ y ^ z },
->(x, y, z) { x & y | (x ^ MASK) & z },
->(x, y, z) { x | (y ^ MASK) ^ z},
->(x, y, z) { x & z | y & (z ^ MASK) }
].freeze
private_constant :F
# added constants (hexadecimal)
K = [0x00000000, 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc].freeze
KK = [0x50a28be6, 0x5c4dd124, 0x6d703ef3, 0x00000000].freeze
private_constant :K, :KK
# selection of message word
R = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8,
3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12,
1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2].freeze
RR = [5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12,
6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2,
15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13,
8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14].freeze
private_constant :R, :RR
# amount for rotate left (rol)
S = [11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8,
7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12,
11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5,
11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12].freeze
SS = [8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6,
9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11,
9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5,
15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8].freeze
private_constant :S, :SS
# initial value (hexadecimal)
WORDS = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476].freeze
private_constant :WORDS
def initialize
@buffer = ''
end
def block_length
64
end
def reset
@buffer.clear
self
end
def << string
@buffer << string
self
end
alias update <<
def finish
string_io = StringIO.new @buffer
block = ''
term = false
last = false
until last
string_io.read 64, block
# the same padding as http://rosettacode.org/wiki/RIPEMD-160#Ruby
case block_size = block.size
when 64
x = block.unpack 'V16'
when 56..63
block << "\x80" << ("\0" * (63 - block_size))
x = block.unpack 'V16'
term = true
when 0..55
block << (term ? "\0" : "\x80") << ("\0" * (55 - block_size))
x = block.unpack 'V14'
bit_length = @buffer.size << 3
x.push bit_length & MASK, bit_length >> 32
last = true
end
h0, h1, h2, h3 = WORDS
a, b, c, d = WORDS
aa, bb, cc, dd = WORDS
j = 0
WORDS.size.times do |round|
f, ff = F[round], F[WORDS.size.pred - round]
k, kk = K[round], KK[round]
16.times do
a, d, c, b = d, c, b, rol(a + f[b, c, d] + x[R[j]] + k, S[j])
aa, dd, cc, bb = dd, cc, bb, rol(aa + ff[bb, cc, dd] + x[RR[j]] + kk, SS[j])
j += 1
end
end
h0, h1, h2, h3 = (h1 + c + dd) & MASK, (h2 + d + aa) & MASK,
(h3 + a + bb) & MASK, (h0 + b + cc) & MASK
end
[h0, h1, h2, h3].pack "V#{WORDS.size}"
end
private
def rol value, shift
((value << shift) & MASK) | (value & MASK) >> (32 - shift)
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment