pool_of_entropy 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 455d13c29630cf9a7255e9d4f76d1ee8ac7dd32b
4
+ data.tar.gz: 4b61829c13593ed3aa534d6ca462b04ca7139b66
5
+ SHA512:
6
+ metadata.gz: f14bf6fdfc0466312c67608ca9bbb966ce5fbef1166d690d8baef622d4311fa9d8fa473bf5f5d292fdef38da9e8e08a5f2ef39ca20de677d59a9bb8ba9e6233f
7
+ data.tar.gz: acc6a94d9bd4f2e436d783489863fcf0e9bd97c3be5f25033b676aefdde9f6136f7e3c58bfd7013e845f3530a4cfa6aa718b3c9261683f88d1f68c972af959d2
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - "1.9.3"
4
+ - "2.0.0"
5
+ - "2.1.0"
6
+ - jruby-19mode
7
+ - jruby-head
data/DIEHARDER_TEST.md ADDED
@@ -0,0 +1,156 @@
1
+ # Results of dieharder randomness test
2
+
3
+ See http://www.phy.duke.edu/~rgb/General/dieharder.php for details about
4
+ the dieharder utility.
5
+
6
+ The test consumes a large amount of data, several orders of magnitude
7
+ more than the target use of PoolOfEntropy. It is a thorough test
8
+ for hidden bias, short cycles and patterns that may occur as faults
9
+ in "weak" PRNGs. No test of randomness can be 100% certain though.
10
+
11
+ The test PASS received implies PoolOfEntropy is free from a variety
12
+ of detectable faults that would prevent it being used as a source of
13
+ statistically random numbers. Not all PRNGs pass these tests -
14
+ for instance, C's standard library rand() fails most of them.
15
+ All modern cryptographic PRNGs should be expected to pass,
16
+ as should Ruby's built-in rand() - an implementation of the Mersenne
17
+ Twister algorithm.
18
+
19
+ ## Date run: 9 May 2014
20
+
21
+ Completing all the tests took roughly 3 days.
22
+
23
+ ## Command used
24
+
25
+ PoolOfEntropy::CorePRNG was used in its default, simplest state to
26
+ create a continuous stream of pseudo-random bytes, and this was
27
+ fed into the dieharder test running the "all tests" option:
28
+
29
+ ruby -rpool_of_entropy -e \
30
+ 'r=PoolOfEntropy::CorePRNG.new;loop do;print r.read_bytes;end' \
31
+ | dieharder -g 200 -a
32
+
33
+ ## Report from dieharder
34
+
35
+ #=============================================================================#
36
+ # dieharder version 3.31.1 Copyright 2003 Robert G. Brown #
37
+ #=============================================================================#
38
+ rng_name |rands/second| Seed |
39
+ stdin_input_raw| 2.40e+05 |3475765104|
40
+ #=============================================================================#
41
+ test_name |ntup| tsamples |psamples| p-value |Assessment
42
+ #=============================================================================#
43
+ diehard_birthdays| 0| 100| 100|0.70127033| PASSED
44
+ diehard_operm5| 0| 1000000| 100|0.49443406| PASSED
45
+ diehard_rank_32x32| 0| 40000| 100|0.58788457| PASSED
46
+ diehard_rank_6x8| 0| 100000| 100|0.08410111| PASSED
47
+ diehard_bitstream| 0| 2097152| 100|0.97953152| PASSED
48
+ diehard_opso| 0| 2097152| 100|0.97485622| PASSED
49
+ diehard_oqso| 0| 2097152| 100|0.70397562| PASSED
50
+ diehard_dna| 0| 2097152| 100|0.76551419| PASSED
51
+ diehard_count_1s_str| 0| 256000| 100|0.95727516| PASSED
52
+ diehard_count_1s_byt| 0| 256000| 100|0.42576929| PASSED
53
+ diehard_parking_lot| 0| 12000| 100|0.08478625| PASSED
54
+ diehard_2dsphere| 2| 8000| 100|0.27022174| PASSED
55
+ diehard_3dsphere| 3| 4000| 100|0.37750039| PASSED
56
+ diehard_squeeze| 0| 100000| 100|0.84447931| PASSED
57
+ diehard_sums| 0| 100| 100|0.07148373| PASSED
58
+ diehard_runs| 0| 100000| 100|0.84129944| PASSED
59
+ diehard_runs| 0| 100000| 100|0.01135930| PASSED
60
+ diehard_craps| 0| 200000| 100|0.08034019| PASSED
61
+ diehard_craps| 0| 200000| 100|0.62929296| PASSED
62
+ marsaglia_tsang_gcd| 0| 10000000| 100|0.56434071| PASSED
63
+ marsaglia_tsang_gcd| 0| 10000000| 100|0.28872723| PASSED
64
+ sts_monobit| 1| 100000| 100|0.80991806| PASSED
65
+ sts_runs| 2| 100000| 100|0.91086458| PASSED
66
+ sts_serial| 1| 100000| 100|0.88204963| PASSED
67
+ sts_serial| 2| 100000| 100|0.77896528| PASSED
68
+ sts_serial| 3| 100000| 100|0.99012882| PASSED
69
+ sts_serial| 3| 100000| 100|0.57917534| PASSED
70
+ sts_serial| 4| 100000| 100|0.29749662| PASSED
71
+ sts_serial| 4| 100000| 100|0.91618340| PASSED
72
+ sts_serial| 5| 100000| 100|0.61310658| PASSED
73
+ sts_serial| 5| 100000| 100|0.70187732| PASSED
74
+ sts_serial| 6| 100000| 100|0.63483611| PASSED
75
+ sts_serial| 6| 100000| 100|0.63638375| PASSED
76
+ sts_serial| 7| 100000| 100|0.87933422| PASSED
77
+ sts_serial| 7| 100000| 100|0.92340602| PASSED
78
+ sts_serial| 8| 100000| 100|0.08379368| PASSED
79
+ sts_serial| 8| 100000| 100|0.30058991| PASSED
80
+ sts_serial| 9| 100000| 100|0.47909085| PASSED
81
+ sts_serial| 9| 100000| 100|0.99388217| PASSED
82
+ sts_serial| 10| 100000| 100|0.62723409| PASSED
83
+ sts_serial| 10| 100000| 100|0.04085355| PASSED
84
+ sts_serial| 11| 100000| 100|0.40703003| PASSED
85
+ sts_serial| 11| 100000| 100|0.07446698| PASSED
86
+ sts_serial| 12| 100000| 100|0.45945558| PASSED
87
+ sts_serial| 12| 100000| 100|0.50603459| PASSED
88
+ sts_serial| 13| 100000| 100|0.19977550| PASSED
89
+ sts_serial| 13| 100000| 100|0.22347592| PASSED
90
+ sts_serial| 14| 100000| 100|0.68014498| PASSED
91
+ sts_serial| 14| 100000| 100|0.38635200| PASSED
92
+ sts_serial| 15| 100000| 100|0.44640327| PASSED
93
+ sts_serial| 15| 100000| 100|0.65586709| PASSED
94
+ sts_serial| 16| 100000| 100|0.97546459| PASSED
95
+ sts_serial| 16| 100000| 100|0.84301307| PASSED
96
+ rgb_bitdist| 1| 100000| 100|0.28629993| PASSED
97
+ rgb_bitdist| 2| 100000| 100|0.56483445| PASSED
98
+ rgb_bitdist| 3| 100000| 100|0.62133888| PASSED
99
+ rgb_bitdist| 4| 100000| 100|0.81437794| PASSED
100
+ rgb_bitdist| 5| 100000| 100|0.98632962| PASSED
101
+ rgb_bitdist| 6| 100000| 100|0.97669342| PASSED
102
+ rgb_bitdist| 7| 100000| 100|0.30828120| PASSED
103
+ rgb_bitdist| 8| 100000| 100|0.66955561| PASSED
104
+ rgb_bitdist| 9| 100000| 100|0.94528798| PASSED
105
+ rgb_bitdist| 10| 100000| 100|0.90457257| PASSED
106
+ rgb_bitdist| 11| 100000| 100|0.85673195| PASSED
107
+ rgb_bitdist| 12| 100000| 100|0.22076625| PASSED
108
+ rgb_minimum_distance| 2| 10000| 1000|0.53379172| PASSED
109
+ rgb_minimum_distance| 3| 10000| 1000|0.43597963| PASSED
110
+ rgb_minimum_distance| 4| 10000| 1000|0.31259419| PASSED
111
+ rgb_minimum_distance| 5| 10000| 1000|0.61615392| PASSED
112
+ rgb_permutations| 2| 100000| 100|0.95732489| PASSED
113
+ rgb_permutations| 3| 100000| 100|0.78107574| PASSED
114
+ rgb_permutations| 4| 100000| 100|0.64034376| PASSED
115
+ rgb_permutations| 5| 100000| 100|0.89199822| PASSED
116
+ rgb_lagged_sum| 0| 1000000| 100|0.63905487| PASSED
117
+ rgb_lagged_sum| 1| 1000000| 100|0.16144850| PASSED
118
+ rgb_lagged_sum| 2| 1000000| 100|0.66997505| PASSED
119
+ rgb_lagged_sum| 3| 1000000| 100|0.54744094| PASSED
120
+ rgb_lagged_sum| 4| 1000000| 100|0.15684876| PASSED
121
+ rgb_lagged_sum| 5| 1000000| 100|0.06950708| PASSED
122
+ rgb_lagged_sum| 6| 1000000| 100|0.04118395| PASSED
123
+ rgb_lagged_sum| 7| 1000000| 100|0.62558494| PASSED
124
+ rgb_lagged_sum| 8| 1000000| 100|0.06955632| PASSED
125
+ rgb_lagged_sum| 9| 1000000| 100|0.80323801| PASSED
126
+ rgb_lagged_sum| 10| 1000000| 100|0.95324347| PASSED
127
+ rgb_lagged_sum| 11| 1000000| 100|0.92104340| PASSED
128
+ rgb_lagged_sum| 12| 1000000| 100|0.68759225| PASSED
129
+ rgb_lagged_sum| 13| 1000000| 100|0.21127858| PASSED
130
+ rgb_lagged_sum| 14| 1000000| 100|0.97290617| PASSED
131
+ rgb_lagged_sum| 15| 1000000| 100|0.33770624| PASSED
132
+ rgb_lagged_sum| 16| 1000000| 100|0.14037461| PASSED
133
+ rgb_lagged_sum| 17| 1000000| 100|0.42891060| PASSED
134
+ rgb_lagged_sum| 18| 1000000| 100|0.01741981| PASSED
135
+ rgb_lagged_sum| 19| 1000000| 100|0.01825942| PASSED
136
+ rgb_lagged_sum| 20| 1000000| 100|0.82574915| PASSED
137
+ rgb_lagged_sum| 21| 1000000| 100|0.55886738| PASSED
138
+ rgb_lagged_sum| 22| 1000000| 100|0.96301438| PASSED
139
+ rgb_lagged_sum| 23| 1000000| 100|0.35504422| PASSED
140
+ rgb_lagged_sum| 24| 1000000| 100|0.86742325| PASSED
141
+ rgb_lagged_sum| 25| 1000000| 100|0.97050104| PASSED
142
+ rgb_lagged_sum| 26| 1000000| 100|0.45785583| PASSED
143
+ rgb_lagged_sum| 27| 1000000| 100|0.91905828| PASSED
144
+ rgb_lagged_sum| 28| 1000000| 100|0.53348048| PASSED
145
+ rgb_lagged_sum| 29| 1000000| 100|0.19985210| PASSED
146
+ rgb_lagged_sum| 30| 1000000| 100|0.63540309| PASSED
147
+ rgb_lagged_sum| 31| 1000000| 100|0.88671466| PASSED
148
+ rgb_lagged_sum| 32| 1000000| 100|0.87499608| PASSED
149
+ rgb_kstest_test| 0| 10000| 1000|0.94490633| PASSED
150
+ dab_bytedistrib| 0| 51200000| 1|0.21636554| PASSED
151
+ dab_dct| 256| 50000| 1|0.35659461| PASSED
152
+ dab_filltree| 32| 15000000| 1|0.49053255| PASSED
153
+ dab_filltree| 32| 15000000| 1|0.24508716| PASSED
154
+ dab_filltree2| 0| 5000000| 1|0.27633380| PASSED
155
+ dab_filltree2| 1| 5000000| 1|0.93106328| PASSED
156
+ dab_monobit2| 12| 65000000| 1|0.21748673| PASSED
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Dependencies are in pool_of_entropy.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Neil Slater
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,151 @@
1
+ # PoolOfEntropy
2
+ [![Gem Version](https://badge.fury.io/rb/pool_of_entropy.png)](http://badge.fury.io/rb/pool_of_entropy)
3
+ [![Build Status](https://travis-ci.org/neilslater/pool_of_entropy.png?branch=master)](http://travis-ci.org/neilslater/pool_of_entropy)
4
+ [![Coverage Status](https://coveralls.io/repos/neilslater/pool_of_entropy/badge.png?branch=master)](https://coveralls.io/r/neilslater/pool_of_entropy?branch=master)
5
+ [![Code Climate](https://codeclimate.com/github/neilslater/pool_of_entropy.png)](https://codeclimate.com/github/neilslater/pool_of_entropy)
6
+ [![Dependency Status](https://gemnasium.com/neilslater/pool_of_entropy.png)](https://gemnasium.com/neilslater/pool_of_entropy)
7
+
8
+ PoolOfEntropy is a pseudo random number generator (PRNG) based on secure hashes,
9
+ intended to bring back the feeling of 'personal luck' that some gamers may feel when rolling
10
+ their *own* dice. An instance of the PoolOfEntropy class could be assigned to a player, or
11
+ to each die in a game, and it can be influenced (similar to throwing a die differently), or
12
+ personalised by feeding in arbitrary data (e.g. a picture of the player, a favourite saying).
13
+ It can handle these influences whilst remaining unbiased and fair on each roll.
14
+
15
+ PoolOfEntropy is *probably* secure when used appropriately. However, cryptographic security is
16
+ not its purpose. The core purpose is for playing with random number generation and non-standard
17
+ sources of entropy. The choice of name is supposed to reflect this.
18
+
19
+ If you are looking for a secure PRNG in Ruby, good for generating session codes or
20
+ server-side secrets, use the standard library SecureRandom.
21
+
22
+ If you think that rolling all your dice on an anonymous server has removed a little bit of soul
23
+ from your game sessions, or if you want to generate unbiased random numbers using input from your
24
+ laptop's microphone or mobile's accellerometer as a source, then PoolOfEntropy might be for you.
25
+
26
+ ## Installation
27
+
28
+ Add this line to your application's Gemfile:
29
+
30
+ gem 'pool_of_entropy'
31
+
32
+ And then execute:
33
+
34
+ $ bundle
35
+
36
+ Or install it yourself as:
37
+
38
+ $ gem install pool_of_entropy
39
+
40
+ ## Usage
41
+
42
+ Create a new generator:
43
+
44
+ pool = PoolOfEntropy.new
45
+
46
+ Get a random number:
47
+
48
+ pool.rand( 20 )
49
+
50
+ Influence the next random number (but not any others):
51
+
52
+ pool.modify_next( 'I hope this works! Whatever!' )
53
+ pool.rand( 20 )
54
+
55
+ # Also
56
+ pool.modify_next( 'I hope this works! Whatever!' ).rand( 20 )
57
+
58
+ Influence all random numbers until change mind (but do not alter internal state):
59
+
60
+ pool.modify_all( 'Gawds help me in my hour of need!' )
61
+
62
+ # All these are modified in same way, the two modifier types "stack"
63
+ pool.rand( 20 )
64
+ pool.rand( 20 )
65
+ pool.modify_next( 'And I really mean it!' ).rand( 20 )
66
+
67
+ Alter internal state of pool (aka customise or "collect entropy"):
68
+
69
+ pool.update_state( 'Purple is my favourite colour.' )
70
+
71
+ ## Rationale
72
+
73
+ ### Properties of Software Random Numbers
74
+
75
+ Software PRNGs available are designed to produce data that cannot be statistically separated
76
+ from an ideal unbiased "truly random" source. There are quite a few algorithms that can do that,
77
+ with differing degrees of success. Current best-in-class generators are pretty good at creating
78
+ psuedo random data that has no discernable pattern.
79
+
80
+ Generators used for games also need another trait - they need to be unpredictable to the end users.
81
+ Often that is not strictly true in the academic sense, for example a well-informed user with enough
82
+ time and skill could predict the next output from Ruby's rand() method. However, when this really
83
+ needs to be true, you can use a Crytogaphically Secure PRNG (CSPRNG). Ruby's SecureRandom cannot
84
+ be predicted from outside the system. Part of how CSPRNGs achieve unpredicatbility is by collecting
85
+ entropy from sources within the computer - this might be timing of events from network cards and
86
+ keyboard presses, or by sampling from deliberately noisy circuits.
87
+
88
+ ### Properties of Dice
89
+
90
+ A humble 6-sided die achieves similar statistics and unpredictability, but in a different way.
91
+ Unbiased statistical randomness is achieved by making the shape and density as regular as possible.
92
+ It also assumes the user has "rolled well enough", which is quite tricky to define, but obviously just
93
+ placing the die with the number you want facing up does not count.
94
+
95
+ Unpredictability for the physical die comes from lack of precise control. Imperfections and
96
+ microscopic details of where the die is rolling have a large impact. Quantum mechanics may
97
+ also have an impact if a die collides and bounces enough. Also, a huge influence is how the
98
+ die is thrown, and that comes from the person throwing it. No-one can control their nerves
99
+ and muscles to a degree where they "roll well enough" but can consciously choose the
100
+ exact result on the die. However, the impulse you give to a die when you throw it caused
101
+ by your nerves, bones and muscles. This gives many people a feeling of agency and relationship
102
+ to the end result. It may just be a random number, but in some sense it is *your random
103
+ number because you generated it*.
104
+
105
+ ### Normal PRNGs Used To Simulate Dice
106
+
107
+ When it comes to finding computer-based sources of randomness, you will find many systems
108
+ that excel at producing results that are statistically random. Ruby's rand() is already
109
+ very good at that. Computer-based PRNGs can apparently be made closer to ideal unbiased
110
+ randomness than physical dice (or at least beyond any realistic ability to measure it).
111
+
112
+ Truly unpredictable sources are also easy enough to find. They make themselves unpredictable
113
+ by collecting entropy from sources on the machines where they run, that no-one can predict.
114
+
115
+ However, there has been a cost to the user's agency. If I was playing a game
116
+ using one of these sources, even though it was fair in the sense that the outcomes could
117
+ well be the same, it gives me the same feeling as if another player was rolling all the dice.
118
+ In a role-playing game, it feels the same as if the DM was rolling all the dice. Now sometimes
119
+ and for some (many/most?) people that's OK. But other times, part of the fun is in rolling
120
+ the dice yourself. I would be happy rolling computer dice, but only if somehow it was
121
+ "me" rolling them.
122
+
123
+ ### What PoolOfEntropy Attempts To Do
124
+
125
+ That is the mission of this gem: To create a simple PRNG where the results are connected as much
126
+ as possible to the end user. This has to be achieved without compromising the
127
+ good features of fairness and unpredictability that PRNGs and CSPRNGs have in general.
128
+
129
+ Luckily this can be achieved using an approach that many CSPRNGs already use - using a
130
+ data source to seed number generation. The main difference between PoolOfEntropy and
131
+ regular CSPRNGs used to protect your computer on the internet is how this "entropy" is sourced.
132
+ In a secure system, entropy is sourced from multiple places - anywhere that data can be
133
+ gathered that an imagined attacker will have a hard time guessing the value. In PoolOfEntropy
134
+ this is subverted - the end user supplies any data they like, and the gem treats it
135
+ as "entropy". Technically, if you were an attacker, this would not be called entropy at
136
+ all (because you know it) - however, to the machinery of the PRNG, or to me as a fellow
137
+ player in a dice game, it counts just fine.
138
+
139
+ By default PoolOfEntropy objects start off with some machine-collected entropy from SecureRandom
140
+ to avoid trivial attacks (of always using the dice in the exact same way). You could view this
141
+ as representing the environment or the die itself (all the scratches and imperfections that
142
+ you cannot control, and have no influence over). Or, under an honour system of not repeating
143
+ yourself you can switch off that default.
144
+
145
+ ## Contributing
146
+
147
+ 1. Fork it ( http://github.com/<my-github-username>/pool_of_entropy/fork )
148
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
149
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
150
+ 4. Push to the branch (`git push origin my-new-feature`)
151
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ desc "PoolOfEntropy unit tests"
5
+ RSpec::Core::RakeTask.new(:test) do |t|
6
+ t.pattern = "spec/*_spec.rb"
7
+ t.verbose = false
8
+ end
9
+
10
+ task :default => [:test]
@@ -0,0 +1,160 @@
1
+ require "pool_of_entropy/version"
2
+ require "pool_of_entropy/core_prng"
3
+
4
+ # This class models a random number generator that can mix user input into
5
+ # the generation mechanism in a few different ways.
6
+ #
7
+ # An object of the class has an internal state for generating numbers, plus
8
+ # holds processed user data for "mixing" into the output.
9
+ #
10
+ # @example Using default internal state, initialised using SecureRandom.random_bytes
11
+ # prng = PoolOfEntropy.new
12
+ # prng.rand( 20 )
13
+ # # E.g. => 12
14
+ #
15
+ # @example A customised PRNG, seeded with some user data, using webcam for "true" randomness
16
+ # prng = PoolOfEntropy.new :size => 4, :blank => true, :seeds = [ 'My Name' ]
17
+ # loop do
18
+ # prng.add_to_pool( Webcam.image.bytes ) # Imagined Webcam interface
19
+ # prng.rand( 20 )
20
+ # # E.g. => 12
21
+ # sleep 5
22
+ # end
23
+ #
24
+
25
+ class PoolOfEntropy
26
+
27
+ # Creates a new random number source. All parameters are optional.
28
+ # @param [Hash] options
29
+ # @option options [Integer] :size, number of 512-bit (64 byte) blocks to use as internal state, defaults to 1
30
+ # @option options [Boolean] :blank, if true then initial state is all zeroes, otherwise use SecureRandom
31
+ # @option options [Array<String>] :seeds, if provided these are sent to #add_to_pool during initialize
32
+ # @return [PoolOfEntropy]
33
+ def initialize options = {}
34
+ unless options.is_a? Hash
35
+ raise TypeError, "Expecting an options hash, got #{options.inspect}"
36
+ end
37
+
38
+ size = 1
39
+ if options[:size]
40
+ size = Integer( options[:size] )
41
+ if size < 1 || size > 256
42
+ raise ArgumentError, "Size of pool must be in Range 1..256, got #{size}"
43
+ end
44
+ end
45
+
46
+ initial_state = if options[:blank]
47
+ "\x0" * size * 64
48
+ else
49
+ SecureRandom.random_bytes( size * 64 )
50
+ end
51
+
52
+ @core_prng = CorePRNG.new( size, initial_state )
53
+
54
+ if options[:seeds]
55
+ unless options[:seeds].is_a? Array
56
+ raise TypeError, "Expected value for :seeds to be an Array, got #{options[:seeds].inspect}"
57
+ end
58
+ options[:seeds].each do |seed|
59
+ add_to_pool( seed )
60
+ end
61
+ end
62
+
63
+ @next_modifier_queue = []
64
+ @fixed_modifier = nil
65
+ end
66
+
67
+ # Cloning creates a deep copy with identical PRNG state and modifiers
68
+ # @return [PoolOfEntropy]
69
+ def clone
70
+ Marshal.load( Marshal.dump( self ) )
71
+ end
72
+
73
+ # Same functionality as Kernel#rand or Random#rand, but using
74
+ # current pool state to generate number, and including zero, one or
75
+ # two modifiers that are in effect.
76
+ # @param [Integer,Range] max if 0 then will return a Float
77
+ # @return [Float,Fixnum,Bignum] type depends on value of max
78
+ def rand max = 0
79
+ if max.is_a? Range
80
+ bottom = max.first
81
+ top = max.last
82
+ return( nil ) if top < bottom
83
+ return bottom + generate_integer( ( top - bottom + 1 ) )
84
+ else
85
+ effective_max = max.to_i.abs
86
+ if effective_max == 0
87
+ return generate_float
88
+ else
89
+ return generate_integer( effective_max )
90
+ end
91
+ end
92
+ end
93
+
94
+ # Stores the hash of one or more string modifiers that will be used
95
+ # just once each to modify results of calls to #rand. Temporary "next"
96
+ # modifiers and the "all" modifier are combined if both are in effect.
97
+ # Modifiers change the end result of a call to #rand(), but do *not*
98
+ # affect the internal state of the data pool used by the generator.
99
+ # @param [Array<String>] modifiers
100
+ # @return [PoolOfEntropy] self
101
+ def modify_next *modifiers
102
+ modifiers.each do |modifier|
103
+ if modifier.nil?
104
+ @next_modifier_queue << nil
105
+ else
106
+ @next_modifier_queue << Digest::SHA512.digest( modifier.to_s )
107
+ end
108
+ end
109
+ self
110
+ end
111
+
112
+ # Stores the hash of a single string modifier that will be used
113
+ # to modify results of calls to #rand, until this modifier is
114
+ # reset. Temporary "next" modifiers and the "all" modifier are
115
+ # combined if both are in effect. Modifiers change the end result
116
+ # of a call to #rand(), but do *not*
117
+ # affect the internal state of the data pool used by the generator.
118
+ # @param [String,nil] modifier
119
+ # @return [PoolOfEntropy] self
120
+ def modify_all modifier
121
+ @fixed_modifier = modifier
122
+ unless @fixed_modifier.nil?
123
+ @fixed_modifier = Digest::SHA512.digest( @fixed_modifier.to_s )
124
+ end
125
+ self
126
+ end
127
+
128
+ # Changes the internal state of the data pool used by the generator,
129
+ # by "mixing in" user-supplied data. This affects all future values
130
+ # from #rand() and cannot be undone.
131
+ # @param [String] data
132
+ # @return [PoolOfEntropy] self
133
+ def add_to_pool data
134
+ @core_prng.update( data )
135
+ self
136
+ end
137
+
138
+ # Empties the "next" modifier queue and clears the "all" modifier.
139
+ # @return [PoolOfEntropy] self
140
+ def clear_all_modifiers
141
+ @next_modifier_queue = []
142
+ @fixed_modifier = nil
143
+ self
144
+ end
145
+
146
+ private
147
+
148
+ def use_adjustments
149
+ [ @fixed_modifier, @next_modifier_queue.shift ].compact
150
+ end
151
+
152
+ def generate_float
153
+ @core_prng.read_float( *use_adjustments )
154
+ end
155
+
156
+ def generate_integer max
157
+ @core_prng.generate_integer( max, *use_adjustments )
158
+ end
159
+
160
+ end