class Blake::Main

Constants

C32

constants for BLAKE-32 and BLAKE-28 Concatenate and the values are the same as the values for the 1st half of C64

C64

constants for BLAKE-64 and BLAKE-48

IV28

The values here are the same as the low-order half-words of IV48

IV32

The values here are the same as the high-order half-words of IV64

IV48
IV64

IVx for BLAKE-x

MASK64BITS
SIGMA

the 10 permutations of: 0,…15}

V_STEP

Attributes

output_size[R]
salt[R]

Public Class Methods

new(output_size = 512, salt = nil) click to toggle source

private_class_method :new

# File lib/blake/main.rb, line 102
def initialize(output_size = 512, salt = nil)
  self.output_size = output_size
  self.salt = salt

  if output_words <= 32
    @word_size  = 4
    @pack_code  = 'L>'
    @num_rounds = 14
    @pi         = C32
    @rot_off    = [16, 12, 8, 7]
  else
    @word_size  = 8
    @pack_code  = 'Q>'
    @num_rounds = 16
    @pi         = C64
    @rot_off    = [32, 25, 16, 11]
  end
end

Public Instance Methods

block_size() click to toggle source
# File lib/blake/main.rb, line 125
def block_size
  @block_size ||= @word_size * 16
end
chacha(block) click to toggle source
# File lib/blake/main.rb, line 181
def chacha(block)
  v = @state[0..7] + @pi[0..7]
  (0..3).each { |i| v[8 + i] ^= @salt[i] }
  if @next_offset != 0
    v[12] ^= @next_offset & mask
    v[13] ^= @next_offset & mask
    v[14] ^= @next_offset >> (@word_size * 8)
    v[15] ^= @next_offset >> (@word_size * 8)
  end

  (0...@num_rounds).each do |r|
    (0..7).each do |i|
      step = V_STEP[i]
      a, b, c, d = step.map { |x| v[x] }

      j = SIGMA[r % 10][i *= 2]
      k = SIGMA[r % 10][i + 1]
      a = a + b + (block[j] ^ @pi[k]) & mask
      d = (d ^ a).ror(@rot_off[0], @word_size)
      c = c + d & mask
      b = (b ^ c).ror(@rot_off[1], @word_size)
      a = a + b + (block[k] ^ @pi[j]) & mask
      d = (d ^ a).ror(@rot_off[2], @word_size)
      c = c + d & mask
      b = (b ^ c).ror(@rot_off[3], @word_size)

      v[step[0]] = a
      v[step[1]] = b
      v[step[2]] = c
      v[step[3]] = d
    end
  end

  (0..7).each { |i| @state[i] ^= v[i] ^ v[8 + i] ^ @salt[i & 0x03] }
end
digest(input, salt = @salt) click to toggle source
# File lib/blake/main.rb, line 133
def digest(input, salt = @salt)
  # use a different salt just this once if one has been provided
  orig_salt = @salt
  self.salt = salt if salt != @salt

  # pad input and append its length in bits
  input.force_encoding('binary')
  total_bits = input.length * 8
  input << "\x80".force_encoding('binary') # mark the end of the input
  rem = (input.length + @word_size * 2) % block_size
  # pad to block size - (2 * word size)
  if rem.positive?
    input << ("\0" * (block_size - rem)).force_encoding('binary')
  end
  # set last marker bit
  input[-1] = (input[-1].ord | 0x01).chr if (output_words % 32).zero?
  # append high-order bytes of input bit length
  if @word_size == 8
    input << [total_bits >> 64].pack('Q>').force_encoding('binary')
  end
  # append low-order bytes of input bit length
  input << [total_bits & MASK64BITS].pack('Q>').force_encoding('binary')

  @state = case output_words
           when 28 then IV28.dup
           when 32 then IV32.dup
           when 48 then IV48.dup
           when 64 then IV64.dup
           end

  @next_offset = 0

  while input.length.positive?
    block = input.slice!(0, block_size).unpack(@pack_code + '*')
    @next_offset += block_size * 8
    # next_offset must only count input data, not padding, and must be 0 if the block contains only padding
    if @next_offset >= total_bits
      @next_offset = total_bits
      total_bits = 0
    end
    chacha(block)
  end

  @salt = orig_salt

  @state.pack(@pack_code + '*')[0...output_words]
end
mask() click to toggle source
# File lib/blake/main.rb, line 121
def mask
  @mask ||= 2**(@word_size * 8) - 1
end
output_words() click to toggle source
# File lib/blake/main.rb, line 129
def output_words
  @output_words ||= @output_size / 8
end

Private Instance Methods

output_size=(value) click to toggle source
# File lib/blake/main.rb, line 221
def output_size=(value)
  unless value.is_a? Integer
    raise TypeError, "Expected #{Integer}, got #{value.class}"
  end

  @output_size = value
end
salt=(salt) click to toggle source
# File lib/blake/main.rb, line 229
def salt=(salt)
  if !salt
    @salt = [0] * 4
  elsif salt.is_a?(Array) && salt.length == 4
    @salt = salt
  elsif salt.length == @word_size * 4
    @salt = salt.unpack(@pack_code + '*')
  else
    raise "salt must be #{@word_size * 4} bytes"
  end
end