- #--
- # sha2.rb - defines Digest::SHA2 class which wraps up the SHA256,
- # SHA384, and SHA512 classes.
- #++
- # Copyright (c) 2006 Akinori MUSHA <knu@iDaemons.org>
- #
- # 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