Skip to content

Instantly share code, notes, and snippets.

@clemensg
Created October 4, 2012 13:51
Show Gist options
  • Save clemensg/3833651 to your computer and use it in GitHub Desktop.
Save clemensg/3833651 to your computer and use it in GitHub Desktop.
Minor changes to Christian Neukirchens Keccak implementation in pure Ruby
# To the extent possible under law, Christian Neukirchen has waived
# all copyright and related or neighboring rights to the source code
# in this file.
# http://creativecommons.org/publicdomain/zero/1.0/
#
# Minor changes by Clemens Gruber, 04.Oct. 2012:
# * Added time measurement to tests
# * Optimized rotl64 a little bit (roughly 30 percent faster),
# because rotl64 is the function which is called most often.
#
require 'digest'
module Digest
class Keccak < Digest::Class
RNDC = [0x0000000000000001, 0x0000000000008082, 0x800000000000808a,
0x8000000080008000, 0x000000000000808b, 0x0000000080000001,
0x8000000080008081, 0x8000000000008009, 0x000000000000008a,
0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
0x000000008000808b, 0x800000000000008b, 0x8000000000008089,
0x8000000000008003, 0x8000000000008002, 0x8000000000000080,
0x000000000000800a, 0x800000008000000a, 0x8000000080008081,
0x8000000000008080, 0x0000000080000001, 0x8000000080008008]
ROTC = [1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14,
27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44]
PILN = [10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4,
15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1]
ROUNDS = 24
def rotl64(x, y)
d = 1 << 64
# ((x << y) | (x >> (64 - y))) % d
# Change: Modulus as bit operation
((x << y) | (x >> (64 - y))) & (d-1)
end
def keccakf(st, rounds)
bc = []
0.upto(rounds-1) { |round|
# Theta
0.upto(4) { |i| bc[i] = st[i] ^ st[i+5] ^ st[i+10] ^ st[i+15] ^ st[i+20] }
0.upto(4) { |i|
t = bc[(i+4) % 5] ^ rotl64(bc[(i+1) % 5], 1)
0.step(24, 5) { |j| st[j+i] ^= t }
}
# Rho Pi
t = st[1]
0.upto(23) { |i|
j = PILN[i]
bc[0] = st[j]
st[j] = rotl64(t, ROTC[i])
t = bc[0]
}
# Chi
0.step(24, 5) { |j|
0.upto(4) { |i| bc[i] = st[j+i] }
0.upto(4) { |i| st[j+i] ^= (~bc[(i+1) % 5]) & bc[(i+2) % 5] }
}
# Iota
st[0] ^= RNDC[round]
}
end
def initialize(bitlen=512)
@mdlen = bitlen / 8
@buf = "".force_encoding("BINARY")
end
def <<(str)
@buf << str.force_encoding("BINARY")
self
end
alias_method :update, :<<
def reset
@buf.clear
self
end
def finish
rsiz = 200 - 2*@mdlen
rsizw = rsiz / 8
st = Array.new(25, 0)
str = @buf
str << "\x01"
str << ("\0" * (rsiz - (str.size % rsiz)))
str[-1] = (str[-1].ord | 0x80).chr
0.step(str.size-1, rsiz) { |j|
quads = str[j,rsiz].unpack("Q*")
0.upto(rsizw-1) { |i| st[i] ^= quads[i] }
keccakf(st, ROUNDS)
}
st.pack("Q*")[0,@mdlen]
end
end
SHA3 = Keccak
end
if $0 == __FILE__
# Benchmark added to compare performance-optimizations to the algorithm above
require 'benchmark'
res = Array.new
time = Benchmark.realtime do
test = ["2b6db7ced8665ebe9deb080295218426bdaa7c6da9add2088932cdffbaa1c14129bccdd70f369efb149285858d2b1d155d14de2fdb680a8b027284055182a0cae275234cc9c92863c1b4ab66f304cf0621cd54565f5bff461d3b461bd40df28198e3732501b4860eadd503d26d6e69338f4e0456e9e9baf3d827ae685fb1d817"].pack("H*")
res << (Digest::SHA3.new(224).hexdigest(test) ==
"af3e0cc6e64501f10fd39722e852355fd6d80d32190631e2f06c22ad")
res << (Digest::SHA3.new(256).hexdigest(test) ==
"d82e257d000dc9fa279a00e2961e3286d2fe1c02ef59833ab8a6a7101bc25054")
res << (Digest::SHA3.new(384).hexdigest(test) ==
"3adeb7eeecf9069f143a10151fd4506aeef3a0ef94ca65d4448acf1e892b8ebb0887631804dd64e153ad41fae0127a85")
res << (Digest::SHA3.new(512).hexdigest(test) ==
"aebba57c8ed5af6ec93f4aa45772ff5167b7ea88dfa71364f37d8fc5fdb7dc3b2c8331a08023f21d110b7d821e2dc7e860826235e7e6291912ac521384747354")
test = ["03a18688b10cc0edf83adf0a84808a9718383c4070c6c4f295098699ac2c"].pack("H*")
res << (Digest::SHA3.new(224).hexdigest(test) ==
"9a8455f41f693b91b3de46bf66ff09d42dc300b856b1dc2dfd12555c")
res << (Digest::SHA3.new(256).hexdigest(test) ==
"435cfc0d1afd8d5509a9ccbf49706575038685bf08db549d9714548240463ee9")
res << (Digest::SHA3.new(384).hexdigest(test) ==
"b4dbdfd9922afd1ce46ff1cb27c30e2aeaa967631a04001c7ef2b5eabd3c0678c0ff219be7b9fa04cf83dd40bc1b33b6")
res << (Digest::SHA3.new(512).hexdigest(test) ==
"48e55e0340f20466881a732aa88459ad4bcdef364c3bd045ae099f953d89f15957aef204265c3915ba42fe4235196be3d0f564676227c3c0deacfbaf68f9e717")
end
p res
puts "Duration: #{time*1000} ms elapsed."
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment