Class: PoolOfEntropy

Inherits:
Object
  • Object
show all
Defined in:
lib/pool_of_entropy.rb,
lib/pool_of_entropy/version.rb

Overview

This class models a random number generator that can mix user input into the generation mechanism in a few different ways.

An object of the class has an internal state for generating numbers, plus holds processed user data for “mixing” into the output.

Examples:

Using default internal state, initialised using SecureRandom.random_bytes

prng = PoolOfEntropy.new
prng.rand( 20 )
# E.g. => 12

A customised PRNG, seeded with some user data, using webcam for “true” randomness

prng = PoolOfEntropy.new :size => 4, :blank => true, :seeds = [ 'My Name' ]
loop do
  prng.add_to_pool( Webcam.image.bytes ) # Imagined Webcam interface
  prng.rand( 20 )
  # E.g. => 12
  sleep 5
end

Defined Under Namespace

Classes: CorePRNG

Constant Summary collapse

VERSION =
"0.0.4"

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ PoolOfEntropy

Creates a new random number source. All parameters are optional.

Parameters:

  • options (Hash) (defaults to: {})

Options Hash (options):

  • :size, (Integer)

    number of 512-bit (64 byte) blocks to use as internal state, defaults to 1

  • :blank, (Boolean)

    if true then initial state is all zeroes, otherwise use SecureRandom

  • :seeds, (Array<String>)

    if provided these are sent to #add_to_pool during initialize



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/pool_of_entropy.rb', line 33

def initialize options = {}
  unless options.is_a? Hash
    raise TypeError, "Expecting an options hash, got #{options.inspect}"
  end

  size = size_from_options( options )

  initial_state = state_from_options( options, size )

  @core_prng = CorePRNG.new( size, initial_state )

  seed_from_options( options )

  @next_modifier_queue = []
  @fixed_modifier = nil
end

Instance Method Details

#add_to_pool(data) ⇒ PoolOfEntropy

Changes the internal state of the data pool used by the generator, by “mixing in” user-supplied data. This affects all future values from #rand() and cannot be undone.

Parameters:

  • data (String)

Returns:



120
121
122
123
# File 'lib/pool_of_entropy.rb', line 120

def add_to_pool data
  @core_prng.update( data )
  self
end

#clear_all_modifiersPoolOfEntropy

Empties the “next” modifier queue and clears the “all” modifier.

Returns:



127
128
129
130
131
# File 'lib/pool_of_entropy.rb', line 127

def clear_all_modifiers
  @next_modifier_queue = []
  @fixed_modifier = nil
  self
end

#clonePoolOfEntropy

Cloning creates a deep copy with identical PRNG state and modifiers

Returns:



52
53
54
55
56
57
58
# File 'lib/pool_of_entropy.rb', line 52

def clone
  copy = super
  copy.instance_variable_set( :@core_prng, @core_prng.clone )
  copy.instance_variable_set( :@fixed_modifier, @fixed_modifier.clone ) if @fixed_modifier
  copy.instance_variable_set( :@next_modifier_queue, @next_modifier_queue.map { |m| m.clone } )
  copy
end

#modify_all(modifier) ⇒ PoolOfEntropy

Stores the hash of a single string modifier that will be used to modify results of calls to #rand, until this modifier is reset. Temporary “next” modifiers and the “all” modifier are combined if both are in effect. Modifiers change the end result of a call to #rand(), but do not affect the internal state of the data pool used by the generator.

Parameters:

  • modifier (String, nil)

Returns:



107
108
109
110
111
112
113
# File 'lib/pool_of_entropy.rb', line 107

def modify_all modifier
  @fixed_modifier = modifier
  unless @fixed_modifier.nil?
    @fixed_modifier = Digest::SHA512.digest( @fixed_modifier.to_s )
  end
  self
end

#modify_next(*modifiers) ⇒ PoolOfEntropy

Stores the hash of one or more string modifiers that will be used just once each to modify results of calls to #rand. Temporary “next” modifiers and the “all” modifier are combined if both are in effect. Modifiers change the end result of a call to #rand(), but do not affect the internal state of the data pool used by the generator.

Parameters:

  • modifiers (Array<String>)

Returns:



88
89
90
91
92
93
94
95
96
97
# File 'lib/pool_of_entropy.rb', line 88

def modify_next *modifiers
  modifiers.each do |modifier|
    if modifier.nil?
      @next_modifier_queue << nil
    else
      @next_modifier_queue << Digest::SHA512.digest( modifier.to_s )
    end
  end
  self
end

#rand(max = 0) ⇒ Float, ...

Same functionality as Kernel#rand or Random#rand, but using current pool state to generate number, and including zero, one or two modifiers that are in effect.

Parameters:

  • max (Integer, Range) (defaults to: 0)

    if 0 then will return a Float

Returns:

  • (Float, Fixnum, Bignum)

    type depends on value of max



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/pool_of_entropy.rb', line 65

def rand max = 0
  if max.is_a? Range
    bottom = max.first
    top = max.last
    return( nil ) if top < bottom
    return bottom + generate_integer( ( top - bottom + 1 ) )
  else
    effective_max = max.to_i.abs
    if effective_max == 0
      return generate_float
    else
      return generate_integer( effective_max )
    end
  end
end