Class: PoolOfEntropy::CorePRNG
- Inherits:
-
Object
- Object
- PoolOfEntropy::CorePRNG
- Defined in:
- lib/pool_of_entropy/core_prng.rb
Overview
This class implements a random number generator based on SHA-512
An object of the class has internal state that is modified on each call to #update or any #read_... method (internally the #read_... methods call #update). The #read_... methods generate a pseudo-random number from the current state pool using SHA-512 and use it in the return value. It is not feasible to determine the internal state from the pseudo-random data received, and not possible to manipulate results from the PRNG in a predictable manner without knowing the internal state.
Instance Attribute Summary collapse
-
#mix_block_id ⇒ Integer
readonly
Identifies the next 64-byte block in the pool that will be altered by a read or update process.
-
#size ⇒ Integer
readonly
The number of 64-byte blocks used in the internal state pool.
Instance Method Summary collapse
-
#clone ⇒ PoolOfEntropy::CorePRNG
The clone of a PoolOfEntropy::CorePRNG object includes separate copy of internal state.
-
#generate_integer(top, *adjustments) ⇒ Fixnum, Bignum
Statistically flat distribution from range (0...top).
-
#initialize(size = 1, initial_state = SecureRandom.random_bytes(64 * Integer(size)), mix_block_id = 0) ⇒ PoolOfEntropy::CorePRNG
constructor
Creates a new random number source.
-
#read_bignum(*adjustments) ⇒ Bignum, Fixnum
Statistically flat distribution from range 0...2**128.
-
#read_bytes(*adjustments) ⇒ String
Statistically flat distribution of 128 bits (16 bytes).
-
#read_float(*adjustments) ⇒ Float
Statistically flat distribution from interval 0.0...1.0, with 53-bit precision.
-
#read_hex(*adjustments) ⇒ String
Statistically flat distribution of 32 hex digits.
-
#state ⇒ PoolOfEntropy::CorePRNG
A clone of the internal state pool.
-
#update(data) ⇒ nil
Mixes supplied data into the curent state.
Constructor Details
#initialize(size = 1, initial_state = SecureRandom.random_bytes(64 * Integer(size)), mix_block_id = 0) ⇒ PoolOfEntropy::CorePRNG
Creates a new random number source. All parameters are optional.
36 37 38 39 40 |
# File 'lib/pool_of_entropy/core_prng.rb', line 36 def initialize(size = 1, initial_state = SecureRandom.random_bytes(64 * Integer(size)), mix_block_id = 0) @size = validate_size(size) @state = validate_state(initial_state, size) @mix_block_id = Integer(mix_block_id) % @size end |
Instance Attribute Details
#mix_block_id ⇒ Integer (readonly)
Identifies the next 64-byte block in the pool that will be altered by a read or update process.
49 50 51 |
# File 'lib/pool_of_entropy/core_prng.rb', line 49 def mix_block_id @mix_block_id end |
#size ⇒ Integer (readonly)
The number of 64-byte blocks used in the internal state pool.
44 45 46 |
# File 'lib/pool_of_entropy/core_prng.rb', line 44 def size @size end |
Instance Method Details
#clone ⇒ PoolOfEntropy::CorePRNG
The clone of a PoolOfEntropy::CorePRNG object includes separate copy of internal state
61 62 63 |
# File 'lib/pool_of_entropy/core_prng.rb', line 61 def clone PoolOfEntropy::CorePRNG.new(size, state, mix_block_id) end |
#generate_integer(top, *adjustments) ⇒ Fixnum, Bignum
Statistically flat distribution from range (0...top). If necessary, it will read more data to ensure absolute fairness. This method can generate an unbiased distribution of Bignums up to roughly half the maximum bit size allowed by Ruby (i.e. much larger than 2**128 generated in a single read)
123 124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/pool_of_entropy/core_prng.rb', line 123 def generate_integer(top, *adjustments) power = 1 sum = 0 words = [] loop do words = read_bytes(*adjustments).unpack('L>*') if words.empty? sum = ((2**32) * sum) + words.shift power *= 2**32 lower_bound = sum * top / power break lower_bound if lower_bound == ((sum + 1) * top) / power end end |
#read_bignum(*adjustments) ⇒ Bignum, Fixnum
Statistically flat distribution from range 0...2**128
101 102 103 104 |
# File 'lib/pool_of_entropy/core_prng.rb', line 101 def read_bignum(*adjustments) nums = read_bytes(*adjustments).unpack('Q>*') nums.inject(0) { |sum, v| (sum << 64) + v } end |
#read_bytes(*adjustments) ⇒ String
Statistically flat distribution of 128 bits (16 bytes)
80 81 82 83 84 85 86 87 |
# File 'lib/pool_of_entropy/core_prng.rb', line 80 def read_bytes(*adjustments) raw_digest = Digest::SHA512.digest(@state) update(raw_digest) adjustments.compact.each do |adjust| raw_digest = Digest::SHA512.digest(raw_digest + adjust) end fold_bits(fold_bits(raw_digest)) end |
#read_float(*adjustments) ⇒ Float
Statistically flat distribution from interval 0.0...1.0, with 53-bit precision
110 111 112 113 |
# File 'lib/pool_of_entropy/core_prng.rb', line 110 def read_float(*adjustments) num = read_bytes(*adjustments).unpack1('Q>*') >> 11 num.to_f / (2**53) end |
#read_hex(*adjustments) ⇒ String
Statistically flat distribution of 32 hex digits
93 94 95 |
# File 'lib/pool_of_entropy/core_prng.rb', line 93 def read_hex(*adjustments) read_bytes(*adjustments).unpack1('H*') end |
#state ⇒ PoolOfEntropy::CorePRNG
A clone of the internal state pool. In combination with #mix_block_id, describes the whole PRNG. If this value is supplied to an end user, then they can easily predict future values of the PRNG.
55 56 57 |
# File 'lib/pool_of_entropy/core_prng.rb', line 55 def state @state.clone end |
#update(data) ⇒ nil
Mixes supplied data into the curent state. This is called internally by #read_... methods as well.
69 70 71 72 73 74 |
# File 'lib/pool_of_entropy/core_prng.rb', line 69 def update(data) new_block = Digest::SHA512.digest(@state + data.to_s) @state[64 * @mix_block_id, 64] = new_block @mix_block_id = (@mix_block_id + 1) % @size nil end |