Skip to content

Instantly share code, notes, and snippets.

@sarahhodne
Created July 29, 2011 16:55
Show Gist options
  • Save sarahhodne/1114217 to your computer and use it in GitHub Desktop.
Save sarahhodne/1114217 to your computer and use it in GitHub Desktop.
#--
# sha2.rb - defines Digest::SHA2 class which wraps up the SHA256,
# SHA384, and SHA512 classes.
#++
# Copyright (c) 2006 Akinori MUSHA <[email protected]>
#
# All rights reserved. You can redistribute and/or modify it under the same
# terms as Ruby.
#
# $Id: sha2.rb 11708 2007-02-12 23:01:19Z shyouhei $
require 'digest'
#require 'ext/digest/sha2/sha2'
module Digest
#
# A meta digest provider class for SHA256, SHA384 and SHA512.
#
class SHA2 < Digest::Class
SHA384_BLOCK_LENGTH = 128
SHA384_DIGEST_LENGTH = 48
SHA384_DIGEST_STRING_LENGTH = (SHA384_DIGEST_LENGTH * 2 + 1)
SHA512_BLOCK_LENGTH = 128
SHA512_DIGEST_LENGTH = 64
SHA512_DIGEST_STRING_LENGTH = (SHA512_DIGEST_LENGTH * 2 + 1)
# call-seq:
# Digest::SHA2.new(bitlen = 256) -> digest_obj
#
# Creates a new SHA2 hash object with a given bit length.
def initialize(bitlen = 256)
case bitlen
when 256
@sha2 = Digest::SHA256.new
when 384
@sha2 = Digest::SHA384.new
when 512
@sha2 = Digest::SHA512.new
else
raise ArgumentError, "unsupported bit length: %s" % bitlen.inspect
end
@bitlen = bitlen
end
# :nodoc:
def reset
@sha2.reset
self
end
# :nodoc:
def update(str)
@sha2.update(str)
self
end
alias << update
def finish
@sha2.digest!
end
private :finish
def block_length
@sha2.block_length
end
def digest_length
@sha2.digest_length
end
# :nodoc:
def initialize_copy(other)
@sha2 = other.instance_eval { @sha2.clone }
end
# :nodoc:
def inspect
"#<%s:%d %s>" % [self.class.name, @bitlen, hexdigest]
end
end
class SHA256 < Digest::Base
UNPACK = { :byte => 'C', :word32 => 'L', :word64 => 'Q' }
PACK = { :byte => 'C', :word32 => 'L', :word64 => 'Q' }
LENGTH = { :byte => 1, :word32 => 4, :word64 => 8 }
INITIAL_HASH_VALUE = [
0x6a09e667,
0xbb67ae85,
0x3c6ef372,
0xa54ff53a,
0x510e527f,
0x9b05688c,
0x1f83d9ab,
0x5be0cd19
].pack(PACK[:word32]*8)
K = [
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
]
BLOCK_LENGTH = 64
DIGEST_LENGTH = 32
DIGEST_STRING_LENGTH = (DIGEST_LENGTH * 2 + 1)
SHORT_BLOCK_LENGTH = (BLOCK_LENGTH - 8)
def a2s_b(a)
a.map {|i| i % 2**8}.pack('C'*a.length)
end
def s2a_b(s)
s.unpack('C'*s.length)
end
def a2s_32(a)
a.map {|i| i % 2**32}.pack('L'*a.length)
end
def s2a_32(s)
s.unpack('L'*(s.length/4))
end
def a2s_64(a)
a.map {|i| i % 2**64}.pack('Q'*a.length)
end
def s2a_64(s)
s.unpack('Q'*(s.length/8))
end
def initialize
reset
end
def update(data)
# Calling with no data is valid - we do nothing
return if data.length == 0
usedspace = (@context[:bitcount] >> 3) % BLOCK_LENGTH
if usedspace > 0
# Calculate how much free space is available in the buffer
freespace = BLOCK_LENGTH - usedspace
if data.length >= freespace
# Fill the buffer completely and process it
@context[:buffer][usedspace,freespace] = data.slice!(0..freespace)
@context[:bitcount] += freespace << 3
transform(@context[:buffer])
else
# The buffer is not yet full
@context[:buffer][usedspace,data.length] = data
@context[:bitcount] += data.length << 3
end
end
while data.length >= BLOCK_LENGTH
# Process as many complete blocks as we can
transform(data.slice!(0..BLOCK_LENGTH))
@context[:bitcount] += BLOCK_LENGTH << 3
end
if data.length > 0
# There's left-overs, so save 'em
@context[:buffer] = data
@context[:bitcount] += data.length << 3
end
end
def finish
buffer = s2a_b(@context[:buffer])
usedspace = (@context[:bitcount] >> 3) % BLOCK_LENGTH
if usedspace > 0
# Begin padding with a 1 bit
buffer[usedspace] = 0x80
usedspace += 1
if usedspace <= SHORT_BLOCK_LENGTH
# Set-up for the last transform:
buffer[usedspace,(SHORT_BLOCK_LENGTH - usedspace)] = [0]*(SHORT_BLOCK_LENGTH - usedspace)
else
if usedspace < BLOCK_LENGTH
buffer[usedspace,(BLOCK_LENGTH - usedspace)] = [0]*(BLOCK_LENGTH - usedspace)
end
# Do second-to-last transform:
transform(s2a_b(buffer))
# And set-up for the last transform:
buffer = [0]*SHORT_BLOCK_LENGTH
end
else
# Set-up for the last transform:
buffer = [0]*SHORT_BLOCK_LENGTH
# Begin padding with a 1 bit:
buffer[0] = 0x80
end
# Set the bit count:
buffer[SHORT_BLOCK_LENGTH,8] = @context[:bitcount].pack('Q').unpack('CCCC')
# Final transform:
transform(@context[:buffer])
d = @context[:state][0..DIGEST_LENGTH]
@context = {}
d
end
def reset
@context = {
:bitcount => 0,
:state => INITIAL_HASH_VALUE,
:buffer => "\x0"*BLOCK_LENGTH
}
end
def transform(data)
# Initialize registers with the previous intermediate value
registers = s2a_32(@context[:state])
w256 = s2a_32(@context[:buffer])
data = s2a_32(data)
# Rounds 0 to 15 (unrolled)
16.times do |j|
t1 = registers[7] +
Sigma1(registers[4]) +
ch(*registers[4..6]) +
K[j] +
(w256[j] = data[j])
registers[3] += t1
registers[7] = t1 + Sigma0(registers[0]) + maj(*registers[0..2])
registers.map {|i| i % 2**32 }
w256.map {|i| i % 2**32 }
registers.unshift(registers.pop)
end
(16...64).each do |j|
s0 = w256[(j+1)&0x0f]
s0 = sigma0(s0)
s1 = w256[(j+14)&0x0f]
s1 = sigma1(s1)
t1 = registers[7] +
Sigma1(registers[4]) +
ch(*registers[4..6]) +
K[j] +
(w256[j&0x0f] += s1 + w256[(j+9)&0x0f] + s0)
registers[3] += t1
registers[7] = t1 + Sigma0(registers[0]) + maj(*registers[0..2])
registers.map {|i| i % 2**32 }
w256.map {|i| i % 2**32 }
registers.unshift(registers.pop)
end
@context[:state] = a2s_32(registers)
@context[:buffer] = a2s_32(w256)
end
private :transform
def sigma0(x)
(s32(7, (x)) ^ s32(18, (x)) ^ r(3 , (x)))
end
private :sigma0
def Sigma0(x)
(s32(2, (x)) ^ s32(13, (x)) ^ s32(22, (x)))
end
private :Sigma0
def sigma1(x)
(s32(17, (x)) ^ s32(19, (x)) ^ r(10, (x)))
end
private :sigma1
def Sigma1(x)
(s32(6, (x)) ^ s32(11, (x)) ^ s32(25, (x)))
end
private :Sigma1
def s32(b,x)
(((x) >> (b)) | ((x) << (32 - (b))))
end
private :s32
def r(b,x)
((x) >> (b))
end
private :r
def ch(x,y,z)
(((x) & (y)) ^ ((~(x)) & (z)))
end
private :ch
def maj(x,y,z)
(((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
end
private :maj
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment