lib/miscreant/internals/util.rb in miscreant-0.1.0 vs lib/miscreant/internals/util.rb in miscreant-0.2.0
- old
+ new
@@ -5,79 +5,75 @@
module Internals
# Internal utility functions
module Util # :nodoc:
module_function
- # Perform a doubling operation as described in the CMAC and SIV papers
- def dbl(value)
- overflow = 0
- words = value.unpack("N4").reverse
+ # :nodoc: Lookup table for the number of trailing zeroes in a byte
+ CTZ_TABLE = [
+ 8, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
+ ].freeze
- words.map! do |word|
- new_word = (word << 1) & 0xFFFFFFFF
- new_word |= overflow
- overflow = (word & 0x80000000) >= 0x80000000 ? 1 : 0
- new_word
- end
+ # Perform a constant time-ish comparison of two bytestrings
+ def ct_equal(a, b)
+ return false unless a.bytesize == b.bytesize
- result = words.reverse.pack("N4")
- result[-1] = (result[-1].ord ^ select(overflow, 0x87, 0)).chr
- result
- end
+ l = a.unpack("C*")
+ r = 0
+ i = -1
- # Pad value with a 0x80 value and zeroes up to the given length
- def pad(message, length)
- padded_length = message.length + length - (message.length % length)
- message += "\x80"
- message.ljust(padded_length, "\0")
+ b.each_byte { |v| r |= v ^ l[i += 1] }
+ r.zero?
end
# Perform a constant time(-ish) branch operation
- def select(subject, result_if_one, result_if_zero)
+ def ct_select(subject, result_if_one, result_if_zero)
(~(subject - 1) & result_if_one) | ((subject - 1) & result_if_zero)
end
- # Perform an xor on arbitrary bytestrings
- def xor(a, b)
- length = [a.length, b.length].min
- output = "\0" * length
- length.times do |i|
- output[i] = (a[i].ord ^ b[i].ord).chr
- end
- output
+ # Count the number of zeros in a given 8-bit integer
+ #
+ # @param value [Integer] an integer in the 8-bit unsigned range (0-255)
+ def ctz(value)
+ CTZ_TABLE.fetch(value)
end
- # XOR the second value into the end of the first
- def xorend(a, b)
- difference = a.length - b.length
-
- left = a.slice(0, difference)
- right = a.slice(difference..-1)
-
- left + xor(right, b)
+ # Pad value with a 0x80 value and zeroes up to the given length
+ def pad(message, length)
+ padded_length = message.length + length - (message.length % length)
+ message += "\x80"
+ message.ljust(padded_length, "\0")
end
- # Zero out the top bits in the last 32-bit words of the IV
- def zero_iv_bits(iv)
- # "We zero-out the top bit in each of the last two 32-bit words
- # of the IV before assigning it to Ctr"
- # -- http://web.cs.ucdavis.edu/~rogaway/papers/siv.pdf
- iv = iv.dup
- iv[8] = (iv[8].ord & 0x7f).chr
- iv[12] = (iv[12].ord & 0x7f).chr
- iv
- end
+ # Ensure a string is a valid bytestring (potentially of a given length)
+ def validate_bytestring(name, string, length: nil)
+ raise TypeError, "expected String for #{name}, got #{string.class}" unless string.is_a?(String)
+ raise ArgumentError, "#{name} must be Encoding::BINARY" unless string.encoding == Encoding::BINARY
- # Perform a constant time-ish comparison of two bytestrings
- def ct_equal(a, b)
- return false unless a.bytesize == b.bytesize
+ case length
+ when Array
+ raise ArgumentError, "#{name} must be #{length.join(' or ')} bytes long" unless length.include?(string.bytesize)
+ when Integer
+ raise ArgumentError, "#{name} must be #{length}-bytes long" unless string.bytesize == length
+ else
+ raise TypeError, "bad length parameter: #{length.inspect} (#{length.class}" unless length.nil?
+ end
- l = a.unpack("C*")
- r = 0
- i = -1
-
- b.each_byte { |v| r |= v ^ l[i += 1] }
- r.zero?
+ string
end
end
end
end