Created
July 29, 2011 16:55
-
-
Save sarahhodne/1114217 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
#-- | |
# 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