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.
35 36 37 38 39 |
# File 'lib/pool_of_entropy/core_prng.rb', line 35 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.
48 49 50 |
# File 'lib/pool_of_entropy/core_prng.rb', line 48 def mix_block_id @mix_block_id end |
#size ⇒ Integer (readonly)
The number of 64-byte blocks used in the internal state pool.
43 44 45 |
# File 'lib/pool_of_entropy/core_prng.rb', line 43 def size @size end |
Instance Method Details
#clone ⇒ PoolOfEntropy::CorePRNG
The clone of a PoolOfEntropy::CorePRNG object includes separate copy of internal state
60 61 62 |
# File 'lib/pool_of_entropy/core_prng.rb', line 60 def clone PoolOfEntropy::CorePRNG.new( self.size, self.state, self.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)
117 118 119 120 121 122 123 124 125 126 127 128 129 |
# File 'lib/pool_of_entropy/core_prng.rb', line 117 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
97 98 99 100 |
# File 'lib/pool_of_entropy/core_prng.rb', line 97 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)
78 79 80 81 82 83 84 85 |
# File 'lib/pool_of_entropy/core_prng.rb', line 78 def read_bytes *adjustments raw_digest = Digest::SHA512.digest( @state ) self.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
105 106 107 108 |
# File 'lib/pool_of_entropy/core_prng.rb', line 105 def read_float *adjustments num = read_bytes( *adjustments ).unpack('Q>*').first >> 11 num.to_f / 2 ** 53 end |
#read_hex(*adjustments) ⇒ String
Statistically flat distribution of 32 hex digits
90 91 92 |
# File 'lib/pool_of_entropy/core_prng.rb', line 90 def read_hex *adjustments read_bytes( *adjustments ).unpack('H*').first 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.
54 55 56 |
# File 'lib/pool_of_entropy/core_prng.rb', line 54 def state @state.clone end |
#update(data) ⇒ nil
Mixes supplied data into the curent state. This is called internally by #read_… methods as well.
68 69 70 71 72 73 |
# File 'lib/pool_of_entropy/core_prng.rb', line 68 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 |