-
-
Save reprah/6083458 to your computer and use it in GitHub Desktop.
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
#!/usr/bin/ruby | |
require 'rubygems' | |
require 'active_support/all' | |
require 'digest/sha1' | |
def leftrotate(value, shift) | |
return ( ((value << shift) | (value >> (32 - shift))) & 0xffffffff) | |
end | |
# FIPS 180-2 -- relevant section #'s below | |
def sha1(message) | |
@hash_words = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0] # 5.3.1 | |
# 5.1.1 | |
bit_string = message.unpack('B*').join # Unpacks message into MSB (Most Significant Bit) binary representation String | |
message_length = bit_string.length | |
one_bit = "10000000" # the 1-bit (\x80) that represents where padding begins | |
case (message_length % 512) # check divisibility by blocksize (512 bits) | |
when 0..448 | |
bit_string << one_bit | |
bit_string << "0" until bit_string.length % 448 == 0 # add null bytes (\x00) until congruent to 448 % 512 | |
bit_string << message_length.to_s(2).rjust(64, '0') | |
when 449..511 | |
# Too long to still be divisible by 512 when padded with the "1-bit" and | |
# 64 bits representing the bitlength, so pad out to the next blocksize multiple | |
bit_string << one_bit | |
bit_string << "0" until bit_string.length % 512 == 0 | |
bit_string << "0" * 448 | |
bit_string << message_length.to_s(2).rjust(64, '0') | |
when 512 | |
# Already fits into a block, but have to pad to avoid ambiguity | |
bit_string << one_bit | |
bit_string << "0" until bit_string.length % 448 == 0 | |
bit_string << message_length.to_s(2).rjust(64, '0') | |
end | |
pad_string = [bit_string].pack('B*').unpack('N*') # Pack into string from MSB Binary and then unpack into Big-endian u_int32 chunks | |
# 6.1.2 | |
pad_string.in_groups_of(16).each do |chunk| # Split pad_string into 512b chunks (16 * 32b) -- 6.1.2 - 1. Prepare the message schedule | |
#Expand from sixteen to eighty -- 6.1.2 - 1. Prepare the message schedule | |
(16..79).each_with_object(chunk) { |i, chunk| chunk << leftrotate((chunk[i-3] ^ chunk[i-8] ^ chunk[i-14] ^ chunk[i-16] ), 1) } | |
@working_vars = Array.new(@hash_words) # Copy current @hash_words for next round. -- 6.1.2 - 2. Initialize the five working variables. | |
for i in (0..79) do # 6.1.2 - 3. & 4.1.1 - SHA-1 Functions | |
if (0 <= i && i <= 19) then | |
f = ((@working_vars[1] & @working_vars[2]) | (~@working_vars[1] & @working_vars[3])) | |
k = 0x5A827999 | |
elsif (20 <= i && i <= 39) then | |
f = (@working_vars[1] ^ @working_vars[2] ^ @working_vars[3]) | |
k = 0x6ED9EBA1 | |
elsif (40 <= i && i <= 59) then | |
f = ((@working_vars[1] & @working_vars[2]) | (@working_vars[1] & @working_vars[3]) | (@working_vars[2] & @working_vars[3])) | |
k = 0x8F1BBCDC | |
elsif (60 <= i && i <= 79) then | |
f = (@working_vars[1] ^ @working_vars[2] ^ @working_vars[3]) | |
k = 0xCA62C1D6 | |
end | |
# Complete round & Create array of working variables for next round. | |
temp = (leftrotate(@working_vars[0], 5) + f + @working_vars[4] + k + (chunk[i])) & 0xffffffff | |
@working_vars = [temp, @working_vars[0], leftrotate(@working_vars[1], 30), @working_vars[2], @working_vars[3]] | |
end | |
# 6.1.2 - 4. Compute the ith intermediate hash value | |
@working_vars.each_with_index {|a, i| @hash_words[i] = ((@hash_words[i] + a) & 0xffffffff)} # append | |
end | |
# Block: Append string with hex formatted partial result, padding 0's due to ruby truncating leading 0's from hex output | |
hash = @hash_words.collect {|x| x.to_s(16).rjust(8,'0')}.join | |
puts hash unless (Digest::SHA1.hexdigest(message) != hash ) # Check result against Ruby SHA-1 Implementation, only output if valid. | |
end | |
ARGF.each_line {|line| sha1(line)} #Run for every line on stdin. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment