Class: HKDF

Inherits:
Object
  • Object
show all
Defined in:
lib/hkdf.rb

Constant Summary collapse

DefaultAlgorithm =
'SHA256'
DefaultReadSize =
512 * 1024

Instance Method Summary collapse

Constructor Details

#initialize(source, options = {}) ⇒ HKDF

Returns a new instance of HKDF.



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# File 'lib/hkdf.rb', line 8

def initialize(source, options = {})
  source = StringIO.new(source) if source.is_a?(String)

  algorithm = options.fetch(:algorithm, DefaultAlgorithm)
  @digest = OpenSSL::Digest.new(algorithm)
  @info = options.fetch(:info, '')

  salt = options[:salt]
  salt = 0.chr * @digest.digest_length if salt.nil? or salt.empty?
  read_size = options.fetch(:read_size, DefaultReadSize)

  @prk = _generate_prk(salt, source, read_size)
  @position = 0
  @blocks = []
  @blocks << ''
end

Instance Method Details

#_generate_blocks(length) ⇒ Object



71
72
73
74
75
76
77
# File 'lib/hkdf.rb', line 71

def _generate_blocks(length)
  start = @blocks.size
  block_count = (length.to_f / @digest.digest_length).ceil
  start.upto(block_count) do |n|
    @blocks << OpenSSL::HMAC.digest(@digest, @prk, @blocks[n - 1] + @info + n.chr)
  end
end

#_generate_prk(salt, source, read_size) ⇒ Object



63
64
65
66
67
68
69
# File 'lib/hkdf.rb', line 63

def _generate_prk(salt, source, read_size)
  hmac = OpenSSL::HMAC.new(salt, @digest)
  while block = source.read(read_size)
    hmac.update(block)
  end
  hmac.digest
end

#algorithmObject



25
26
27
# File 'lib/hkdf.rb', line 25

def algorithm
  @digest.name
end

#inspectObject



59
60
61
# File 'lib/hkdf.rb', line 59

def inspect
  "#{to_s[0..-2]} algorithm=#{@digest.name.inspect} info=#{@info.inspect}>"
end

#max_lengthObject



29
30
31
# File 'lib/hkdf.rb', line 29

def max_length
  @max_length ||= @digest.digest_length * 255
end

#next_bytes(length) ⇒ Object

Raises:

  • (RangeError)


43
44
45
46
47
48
49
50
51
52
53
# File 'lib/hkdf.rb', line 43

def next_bytes(length)
  new_position = length + @position
  raise RangeError.new("requested #{length} bytes, only #{max_length} available") if new_position > max_length

  _generate_blocks(new_position)

  start = @position
  @position = new_position

  @blocks.join('').slice(start, length)
end

#next_hex_bytes(length) ⇒ Object



55
56
57
# File 'lib/hkdf.rb', line 55

def next_hex_bytes(length)
  next_bytes(length).unpack('H*').first
end

#rewindObject



39
40
41
# File 'lib/hkdf.rb', line 39

def rewind
  seek(0)
end

#seek(position) ⇒ Object

Raises:

  • (RangeError)


33
34
35
36
37
# File 'lib/hkdf.rb', line 33

def seek(position)
  raise RangeError.new("cannot seek past #{max_length}") if position > max_length

  @position = position
end