class TimingAttack::Grouper

Constants

ALLOWED_TEST_SYMBOLS

Attributes

attacks[R]
long_tests[R]
short_tests[R]
spike_delta[R]
test_args[R]
test_hash[R]
test_method[R]

Public Class Methods

new(attacks: , group_by: {}) click to toggle source
# File lib/timing_attack/grouper.rb, line 4
def initialize(attacks: , group_by: {})
  @attacks = attacks
  setup_grouping_opts!(group_by)
  @short_tests = []
  @long_tests = []
  group_attacks
  serialize
  freeze
end

Public Instance Methods

serialize() click to toggle source
# File lib/timing_attack/grouper.rb, line 14
def serialize
  @serialize ||= {}.tap do |h|
    h[:attack_method] = test_method
    h[:attack_args]   = test_args
    h[:short]         = serialize_tests(short_tests)
    h[:long]          = serialize_tests(long_tests)
    h[:spike_delta]   = spike_delta
  end
end

Private Instance Methods

decorated_attacks() click to toggle source
# File lib/timing_attack/grouper.rb, line 86
def decorated_attacks
  return @decorated_attacks unless @decorated_attacks.nil?
  sorted = attacks.sort { |a,b| value_from_test(a) <=> value_from_test(b) }
  @decorated_attacks = sorted.each_with_object([]).with_index do |(attack, memo), index|
    delta = if index == 0
              0.0
            else
              value_from_test(attack) - value_from_test(sorted[index-1])
            end
    memo << { attack: attack, delta: delta }
  end
end
group_attacks() click to toggle source
# File lib/timing_attack/grouper.rb, line 77
def group_attacks
  spike = decorated_attacks.max { |a,b| a[:delta] <=> b[:delta] }
  index = decorated_attacks.index(spike)
  stripped = decorated_attacks.map {|a| a[:attack] }
  @short_tests = stripped[0..(index-1)]
  @long_tests = stripped[index..-1]
  @spike_delta = spike[:delta]
end
serialize_tests(test_arr) click to toggle source
# File lib/timing_attack/grouper.rb, line 71
def serialize_tests(test_arr)
  test_arr.each_with_object({}) do |test, ret|
    ret[test.input] = value_from_test(test)
  end
end
setup_grouping_opts!(group_by) click to toggle source
# File lib/timing_attack/grouper.rb, line 30
def setup_grouping_opts!(group_by)
  case group_by
  when Symbol
    setup_symbol_opts!(group_by)
  when Hash
    setup_hash_opts!(group_by)
  else
    raise ArgumentError.new("Don't know what to do with #{group_by.class} #{group_by}")
  end
end
setup_hash_opts!(hash) click to toggle source
# File lib/timing_attack/grouper.rb, line 57
def setup_hash_opts!(hash)
  raise ArgumentError.new("Must provide configuration to Grouper") if hash.empty?
  key, value = hash.first
  unless ALLOWED_TEST_SYMBOLS.include? key
    raise ArgumentError.new("Allowed keys are #{ALLOWED_TEST_SYMBOLS.join(', ')}")
  end
  @test_method = key
  @test_args = value.is_a?(Array) ? value : [value]
end
setup_symbol_opts!(symbol) click to toggle source
# File lib/timing_attack/grouper.rb, line 41
def setup_symbol_opts!(symbol)
  case symbol
  when :mean
    @test_method = :mean
    @test_args = []
  when :median
    @test_method = :percentile
    @test_args = [50]
  when :percentile
    @test_method = :percentile
    @test_args = [10]
  else
    raise ArgumentError.new("Allowed symbols are #{ALLOWED_TEST_SYMBOLS.join(', ')}")
  end
end
value_from_test(test) click to toggle source
# File lib/timing_attack/grouper.rb, line 67
def value_from_test(test)
  test.public_send(test_method, *test_args)
end