pool_of_entropy 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.yardopts +7 -0
- data/DIEHARDER_TEST.md +20 -7
- data/RATIONALE.md +74 -0
- data/README.md +66 -87
- data/lib/pool_of_entropy.rb +32 -20
- data/lib/pool_of_entropy/core_prng.rb +23 -19
- data/lib/pool_of_entropy/version.rb +1 -1
- data/spec/core_prng_spec.rb +2 -0
- data/spec/pool_of_entropy_spec.rb +25 -2
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4bfa3a0e0d42e79d2af5b13539692da02db0d33c
|
4
|
+
data.tar.gz: 97f2e7d2a51605afeb88dbf2c1560e8829b91595
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2a67971cfcae21b459bd249154c7b3ab74ec94f36c7b08c146928196084840a074a83c01faad59fd4e8eb23be888e02a74945c9b01b77a2264e44d908723d5e9
|
7
|
+
data.tar.gz: 73db8b9f6f8d8c8db1873a4d3be54eecf41793f0b7a6ab60a3c8225a2526de41d7508c07e7844b1adb60479dd155fa647511a08a9488d3695bb89a9499cfce99
|
data/.yardopts
ADDED
data/DIEHARDER_TEST.md
CHANGED
@@ -8,15 +8,28 @@ more than the target use of PoolOfEntropy. It is a thorough test
|
|
8
8
|
for hidden bias, short cycles and patterns that may occur as faults
|
9
9
|
in "weak" PRNGs. No test of randomness can be 100% certain though.
|
10
10
|
|
11
|
-
|
11
|
+
All modern cryptographic PRNGs should be expected to pass Dieharder. Although
|
12
|
+
passing the test does not imply a secure random number generator, failing
|
13
|
+
it consistently implies an insceure one, that could be predicted.
|
14
|
+
Ruby's built-in rand(), an implementation of the Mersenne Twister algorithm,
|
15
|
+
should also pass.
|
16
|
+
|
17
|
+
Note that there are over 100 tests, and a test that scores a p-value below
|
18
|
+
0.01 would be marked "WEAK". For a perfect PRNG it is reasonable to expect
|
19
|
+
maybe one to five "WEAK" results in a single run. It was just chance -
|
20
|
+
there is roughly a 32% chance that a perfect PRNG gets all Dieharder tests to
|
21
|
+
cleanly pass. The usual way to deal with WEAK results is to repeat the offending
|
22
|
+
tests and show that other p-values are just as likely.
|
23
|
+
|
24
|
+
Generally, when a PRNG fails a Dieharder test systematically, you see a
|
25
|
+
very strong signal, p-value below 0.00001 for several related tests.
|
26
|
+
|
27
|
+
The test report below implies PoolOfEntropy is free from a variety
|
12
28
|
of detectable faults that would prevent it being used as a source of
|
13
|
-
statistically random numbers.
|
14
|
-
|
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.
|
29
|
+
statistically random numbers. As it happens, there were no "WEAK" results
|
30
|
+
this time.
|
18
31
|
|
19
|
-
## Date run: 9 May 2014
|
32
|
+
## Date run: 9-12 May 2014
|
20
33
|
|
21
34
|
Completing all the tests took roughly 3 days.
|
22
35
|
|
data/RATIONALE.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Rationale for PoolOfEntropy
|
2
|
+
|
3
|
+
## Properties of Software Random Numbers
|
4
|
+
|
5
|
+
Software PRNGs available are designed to produce data that cannot be statistically separated
|
6
|
+
from an ideal unbiased "truly random" source. There are quite a few algorithms that can do that,
|
7
|
+
with differing degrees of success. Current best-in-class generators are pretty good at creating
|
8
|
+
psuedo random data that has no discernable pattern.
|
9
|
+
|
10
|
+
Generators used for games also need another trait - they need to be unpredictable to the end users.
|
11
|
+
Often that is not strictly true in the academic sense, for example a well-informed user with enough
|
12
|
+
time and skill could predict the next output from Ruby's rand() method. However, when this really
|
13
|
+
needs to be true, you can use a Crytogaphically Secure PRNG (CSPRNG). Ruby's SecureRandom cannot
|
14
|
+
be predicted from outside the system. Part of how CSPRNGs achieve unpredicatbility is by collecting
|
15
|
+
entropy from sources within the computer - this might be timing of events from network cards and
|
16
|
+
keyboard presses, or by sampling from deliberately noisy circuits.
|
17
|
+
|
18
|
+
## Properties of Dice
|
19
|
+
|
20
|
+
A humble 6-sided die achieves similar statistics and unpredictability, but in a different way.
|
21
|
+
Unbiased statistical randomness is achieved by making the shape and density as regular as possible.
|
22
|
+
It also assumes the user has "rolled well enough", which is quite tricky to define, but obviously just
|
23
|
+
placing the die with the number you want facing up does not count.
|
24
|
+
|
25
|
+
Unpredictability for the physical die comes from lack of precise control. Imperfections and
|
26
|
+
microscopic details of where the die is rolling have an impact. Quantum mechanics, which
|
27
|
+
as far as we know is inherently probability-based, may also have an impact if a die collides
|
28
|
+
and bounces enough. Also, a big influence, mechanically and philosophically, is how the
|
29
|
+
die is thrown - and that comes from the person throwing it. No-one can control their nerves
|
30
|
+
and muscles to a degree where they "roll well enough" but can consciously choose the
|
31
|
+
exact result on the die. However, the impulse you give to a die when you throw it is caused
|
32
|
+
by your nerves, bones and muscles. This gives many people a feeling of agency and relationship
|
33
|
+
to the end result. It may just be a random number, but in some sense it is *your random
|
34
|
+
number because you generated it*.
|
35
|
+
|
36
|
+
## Normal PRNGs Used To Simulate Dice
|
37
|
+
|
38
|
+
When it comes to finding computer-based sources of randomness, you will find many systems
|
39
|
+
that excel at producing results that are statistically random. Ruby's rand() is already
|
40
|
+
very good at that. Computer-based PRNGs can apparently be made closer to ideal unbiased
|
41
|
+
randomness than physical dice (or at least beyond any realistic ability to measure it).
|
42
|
+
|
43
|
+
Truly unpredictable sources are also easy enough to find. They make themselves unpredictable
|
44
|
+
by collecting entropy from sources on the machines where they run, that no-one can predict.
|
45
|
+
|
46
|
+
However, there has been a cost to the user's agency. If I was playing a game
|
47
|
+
using one of these sources, even though it was fair in the sense that the outcomes could
|
48
|
+
well be the same, it gives me the same feeling as if another player was rolling all the dice.
|
49
|
+
In a role-playing game, it feels the same as if the DM was rolling all the dice. Now sometimes
|
50
|
+
and for some (many/most?) people that's OK. But other times, part of the fun is in rolling
|
51
|
+
the dice yourself. I would be happy rolling computer dice, but only if somehow it was
|
52
|
+
"me" rolling them.
|
53
|
+
|
54
|
+
## What PoolOfEntropy Attempts To Do
|
55
|
+
|
56
|
+
That is the mission of this gem: To create a simple PRNG where the results are connected as much
|
57
|
+
as possible to the end user. This has to be achieved without compromising the
|
58
|
+
good features of fairness and unpredictability that PRNGs and CSPRNGs have in general.
|
59
|
+
|
60
|
+
Luckily this can be achieved using an approach that many CSPRNGs already use - using a
|
61
|
+
data source to seed number generation. The main difference between PoolOfEntropy and
|
62
|
+
regular CSPRNGs used to protect your computer on the internet is how this "entropy" is sourced.
|
63
|
+
In a secure system, entropy is sourced from multiple places - anywhere that data can be
|
64
|
+
gathered that an imagined attacker will have a hard time guessing the value. In PoolOfEntropy
|
65
|
+
this is subverted - the end user supplies any data they like, and the gem treats it
|
66
|
+
as "entropy". Technically, if you were an attacker, this would not be called entropy at
|
67
|
+
all (because you know it) - however, to the machinery of the PRNG, or to me as a fellow
|
68
|
+
player in a dice game, it counts just fine.
|
69
|
+
|
70
|
+
By default PoolOfEntropy objects start off with some machine-collected entropy from SecureRandom
|
71
|
+
to avoid trivial attacks (of always using the dice in the exact same way). You could view this
|
72
|
+
as representing the environment or the die itself (all the scratches and imperfections that
|
73
|
+
you cannot control, and have no influence over). Or, under an honour system of not repeating
|
74
|
+
yourself you can switch off that default.
|
data/README.md
CHANGED
@@ -12,9 +12,10 @@ to each die in a game, and it can be influenced (similar to throwing a die diffe
|
|
12
12
|
personalised by feeding in arbitrary data (e.g. a picture of the player, a favourite saying).
|
13
13
|
It can handle these influences whilst remaining unbiased and fair on each roll.
|
14
14
|
|
15
|
-
PoolOfEntropy is *probably* secure when used appropriately
|
16
|
-
not its purpose. The core purpose is for playing with
|
17
|
-
sources of entropy. The choice of name is
|
15
|
+
PoolOfEntropy is *probably* secure when used appropriately, and in a very limited sense.
|
16
|
+
However, cryptographic security is not its purpose. The core purpose is for playing with
|
17
|
+
random number generation and non-standard sources of entropy. The choice of name is
|
18
|
+
supposed to reflect this.
|
18
19
|
|
19
20
|
If you are looking for a secure PRNG in Ruby, good for generating session codes or
|
20
21
|
server-side secrets, use the standard library SecureRandom.
|
@@ -43,108 +44,86 @@ Create a new generator:
|
|
43
44
|
|
44
45
|
pool = PoolOfEntropy.new
|
45
46
|
|
46
|
-
Get a random number
|
47
|
+
Get a random number. Not feeding the generator with any customisation
|
48
|
+
means it is completely deterministic based on current internal state. The
|
49
|
+
analogy here might be "trusting to Fate":
|
47
50
|
|
48
51
|
pool.rand( 20 )
|
49
52
|
|
50
|
-
Influence the next random number (but not any others)
|
53
|
+
Influence the next random number (but not any others). This is analogous to
|
54
|
+
shaking or throwing dice in a certain way. Only the next result from rand()
|
55
|
+
is affected:
|
51
56
|
|
52
|
-
pool.modify_next( '
|
57
|
+
pool.modify_next( 'Shake the die.' )
|
53
58
|
pool.rand( 20 )
|
54
59
|
|
55
60
|
# Also
|
56
|
-
pool.modify_next( '
|
61
|
+
pool.modify_next( 'Shake the die.' ).rand( 20 )
|
57
62
|
|
58
|
-
|
63
|
+
# This next result will not be influenced,
|
64
|
+
# we re-join the deterministic sequence of the PRNG:
|
65
|
+
pool.rand
|
66
|
+
|
67
|
+
Influence the next three random numbers. Data supplied to modify_next is
|
68
|
+
put in a queue, first in, first out:
|
69
|
+
|
70
|
+
pool.modify_next( 'Shake the die lots.' )
|
71
|
+
pool.modify_next( 'Roll the die cautiously.' )
|
72
|
+
pool.modify_next( 'Drop the die from a great height and watch it bounce.' )
|
73
|
+
|
74
|
+
pool.rand # influenced by 'Shake the die lots.'
|
75
|
+
pool.rand # etc
|
76
|
+
pool.rand
|
77
|
+
|
78
|
+
pool.rand # . . . and back to main sequence
|
79
|
+
|
80
|
+
Influence all random numbers from this point forward. This is analogous to
|
81
|
+
having a personal style of throwing dice, or perhaps a different environment
|
82
|
+
to throw them in.
|
59
83
|
|
60
84
|
pool.modify_all( 'Gawds help me in my hour of need!' )
|
61
85
|
|
62
|
-
# All these are modified in same way
|
86
|
+
# All these are modified in same way
|
63
87
|
pool.rand( 20 )
|
64
88
|
pool.rand( 20 )
|
89
|
+
|
90
|
+
# The two modifier types "stack", and this is modified twice
|
65
91
|
pool.modify_next( 'And I really mean it!' ).rand( 20 )
|
66
92
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
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.
|
93
|
+
Remove modfiers:
|
94
|
+
|
95
|
+
# Just the "all" modifier
|
96
|
+
pool.modify_all( nil )
|
97
|
+
|
98
|
+
# Insert a pause into the "next" queue
|
99
|
+
pool.modify_next( nil )
|
100
|
+
|
101
|
+
# Re-set "next" and "all" modifiers
|
102
|
+
pool.clear_all_modifiers
|
103
|
+
|
104
|
+
Alter internal state of pool. This mixes in any entropy in the supplied
|
105
|
+
data, and changes the deterministic sequence going forward. This is
|
106
|
+
analogous to long-term alterations to dice, the environment, or
|
107
|
+
person throwing the dice.
|
108
|
+
|
109
|
+
pool.add_to_pool( 'Purple is my favourite colour.' )
|
110
|
+
|
111
|
+
All the inputs can be any length String, from any source. If the data
|
112
|
+
contains *any* "true randomness" (however you want to define it, and however
|
113
|
+
the String is formatted), then PoolOfEntropy
|
114
|
+
will process that (using SHA-512) into unbiased results. If you care
|
115
|
+
about your own source of randomness being more "important" than
|
116
|
+
the initial state of the PRNG, or its continued deterministic ticking,
|
117
|
+
then make use of the modifiers and/or add data to the pool frequently.
|
118
|
+
|
119
|
+
## More information
|
120
|
+
|
121
|
+
* [Rationale](RATIONALE.md)
|
122
|
+
* [Dieharder test of statistical randomness](DIEHARDER_TEST.md)
|
144
123
|
|
145
124
|
## Contributing
|
146
125
|
|
147
|
-
1. Fork it ( http://github.com
|
126
|
+
1. Fork it ( http://github.com/neilslater/pool_of_entropy/fork )
|
148
127
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
149
128
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
150
129
|
4. Push to the branch (`git push origin my-new-feature`)
|
data/lib/pool_of_entropy.rb
CHANGED
@@ -35,30 +35,13 @@ class PoolOfEntropy
|
|
35
35
|
raise TypeError, "Expecting an options hash, got #{options.inspect}"
|
36
36
|
end
|
37
37
|
|
38
|
-
size =
|
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
|
38
|
+
size = size_from_options( options )
|
45
39
|
|
46
|
-
initial_state =
|
47
|
-
"\x0" * size * 64
|
48
|
-
else
|
49
|
-
SecureRandom.random_bytes( size * 64 )
|
50
|
-
end
|
40
|
+
initial_state = state_from_options( options, size )
|
51
41
|
|
52
42
|
@core_prng = CorePRNG.new( size, initial_state )
|
53
43
|
|
54
|
-
|
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
|
44
|
+
seed_from_options( options )
|
62
45
|
|
63
46
|
@next_modifier_queue = []
|
64
47
|
@fixed_modifier = nil
|
@@ -157,4 +140,33 @@ class PoolOfEntropy
|
|
157
140
|
@core_prng.generate_integer( max, *use_adjustments )
|
158
141
|
end
|
159
142
|
|
143
|
+
def state_from_options( options, size )
|
144
|
+
if options[:blank]
|
145
|
+
"\x0" * size * 64
|
146
|
+
else
|
147
|
+
SecureRandom.random_bytes( size * 64 )
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def size_from_options( options )
|
152
|
+
size = 1
|
153
|
+
if options[:size]
|
154
|
+
size = Integer( options[:size] )
|
155
|
+
if size < 1 || size > 256
|
156
|
+
raise ArgumentError, "Size of pool must be in Range 1..256, got #{size}"
|
157
|
+
end
|
158
|
+
end
|
159
|
+
size
|
160
|
+
end
|
161
|
+
|
162
|
+
def seed_from_options( options )
|
163
|
+
if options[:seeds]
|
164
|
+
unless options[:seeds].is_a? Array
|
165
|
+
raise TypeError, "Expected value for :seeds to be an Array, got #{options[:seeds].inspect}"
|
166
|
+
end
|
167
|
+
options[:seeds].each do |seed|
|
168
|
+
add_to_pool( seed )
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
160
172
|
end
|
@@ -33,20 +33,8 @@ class PoolOfEntropy::CorePRNG
|
|
33
33
|
# @param [Integer] mix_block_id
|
34
34
|
# @return [PoolOfEntropy::CorePRNG]
|
35
35
|
def initialize size = 1, initial_state = SecureRandom.random_bytes( 64 * Integer(size) ), mix_block_id = 0
|
36
|
-
@size =
|
37
|
-
|
38
|
-
raise ArgumentError, "Size of pool must be in Range 1..256, got #{@size}"
|
39
|
-
end
|
40
|
-
unless initial_state.is_a? String
|
41
|
-
raise TypeError, "Initial state must be a String, got #{initial_state.inspect}"
|
42
|
-
end
|
43
|
-
@state = initial_state.clone
|
44
|
-
@state.force_encoding( 'BINARY' )
|
45
|
-
|
46
|
-
if @state.size != size * 64
|
47
|
-
raise ArgumentError, "Initial state bad size - expected #{size * 64} bytes, got #{@state.size} bytes"
|
48
|
-
end
|
49
|
-
|
36
|
+
@size = validate_size( size )
|
37
|
+
@state = validate_state( initial_state, size )
|
50
38
|
@mix_block_id = Integer( mix_block_id ) % @size
|
51
39
|
end
|
52
40
|
|
@@ -129,7 +117,6 @@ class PoolOfEntropy::CorePRNG
|
|
129
117
|
def generate_integer top, *adjustments
|
130
118
|
power = 1
|
131
119
|
sum = 0
|
132
|
-
lower_bound = 0
|
133
120
|
words = []
|
134
121
|
|
135
122
|
loop do
|
@@ -137,11 +124,8 @@ class PoolOfEntropy::CorePRNG
|
|
137
124
|
sum = 2**32 * sum + words.shift
|
138
125
|
power *= 2**32
|
139
126
|
lower_bound = sum * top / power
|
140
|
-
|
141
|
-
break if lower_bound == upper_bound
|
127
|
+
break lower_bound if lower_bound == ( (sum + 1) * top ) / power
|
142
128
|
end
|
143
|
-
|
144
|
-
lower_bound
|
145
129
|
end
|
146
130
|
|
147
131
|
private
|
@@ -158,4 +142,24 @@ class PoolOfEntropy::CorePRNG
|
|
158
142
|
folded_32bits.pack('L>*')
|
159
143
|
end
|
160
144
|
|
145
|
+
def validate_size i
|
146
|
+
size = Integer( i )
|
147
|
+
if size < 1 || size > 256
|
148
|
+
raise ArgumentError, "Size of pool must be in Range 1..256, got #{size}"
|
149
|
+
end
|
150
|
+
size
|
151
|
+
end
|
152
|
+
|
153
|
+
def validate_state( initial_state, size )
|
154
|
+
unless initial_state.is_a? String
|
155
|
+
raise TypeError, "Initial state must be a String, got #{initial_state.inspect}"
|
156
|
+
end
|
157
|
+
state = initial_state.clone
|
158
|
+
state.force_encoding( 'BINARY' )
|
159
|
+
|
160
|
+
if state.size != size * 64
|
161
|
+
raise ArgumentError, "Initial state bad size - expected #{size * 64} bytes, got #{state.size} bytes"
|
162
|
+
end
|
163
|
+
state
|
164
|
+
end
|
161
165
|
end
|
data/spec/core_prng_spec.rb
CHANGED
@@ -35,6 +35,8 @@ describe PoolOfEntropy::CorePRNG do
|
|
35
35
|
end
|
36
36
|
|
37
37
|
it "should fail with bad state data" do
|
38
|
+
expect { PoolOfEntropy::CorePRNG.new( 1, :boo ) }.to raise_error TypeError
|
39
|
+
expect { PoolOfEntropy::CorePRNG.new( 1, [] ) }.to raise_error TypeError
|
38
40
|
expect { PoolOfEntropy::CorePRNG.new( 1, '' ) }.to raise_error ArgumentError
|
39
41
|
expect { PoolOfEntropy::CorePRNG.new( 1, "\x0" * 63 ) }.to raise_error ArgumentError
|
40
42
|
expect { PoolOfEntropy::CorePRNG.new( 1, "\x0" * 200 ) }.to raise_error ArgumentError
|
@@ -20,6 +20,12 @@ describe PoolOfEntropy do
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
+
it "should fail when param is not a hash" do
|
24
|
+
expect { PoolOfEntropy.new( [:size,12] ) }.to raise_error TypeError
|
25
|
+
expect { PoolOfEntropy.new( '' ) }.to raise_error TypeError
|
26
|
+
expect { PoolOfEntropy.new( :size ) }.to raise_error TypeError
|
27
|
+
end
|
28
|
+
|
23
29
|
it "should fail with incorrect :size param" do
|
24
30
|
expect { PoolOfEntropy.new( :size => -12 ) }.to raise_error ArgumentError
|
25
31
|
expect { PoolOfEntropy.new( :size => -1 ) }.to raise_error ArgumentError
|
@@ -48,7 +54,7 @@ describe PoolOfEntropy do
|
|
48
54
|
(10..20).map { |x| pool.rand(x) }.should == [8, 3, 0, 12, 11, 2, 4, 8, 1, 11, 18]
|
49
55
|
end
|
50
56
|
|
51
|
-
it "should accept and use :
|
57
|
+
it "should accept and use :seeds array" do
|
52
58
|
pool = PoolOfEntropy.new( :blank => true, :seeds => ['foo'] )
|
53
59
|
pool.should be_a PoolOfEntropy
|
54
60
|
(10..20).map { |x| pool.rand(x) }.should == [9, 1, 3, 7, 8, 12, 14, 5, 11, 5, 6]
|
@@ -57,10 +63,15 @@ describe PoolOfEntropy do
|
|
57
63
|
pool.should be_a PoolOfEntropy
|
58
64
|
(10..20).map { |x| pool.rand(x) }.should == [8, 1, 5, 8, 2, 7, 11, 8, 8, 13, 14]
|
59
65
|
end
|
66
|
+
|
67
|
+
it "should fail if :seeds param is not an array" do
|
68
|
+
expect { PoolOfEntropy.new( :seeds => -12 ) }.to raise_error TypeError
|
69
|
+
expect { PoolOfEntropy.new( :seeds => { :seeds => [2] } ) }.to raise_error TypeError
|
70
|
+
expect { PoolOfEntropy.new( :seeds => 'more_seeds' ) }.to raise_error TypeError
|
71
|
+
end
|
60
72
|
end
|
61
73
|
end
|
62
74
|
|
63
|
-
|
64
75
|
describe "instance methods" do
|
65
76
|
pool_types = [
|
66
77
|
[
|
@@ -239,6 +250,18 @@ describe PoolOfEntropy do
|
|
239
250
|
pool_copy.rand.should == pool.rand
|
240
251
|
end
|
241
252
|
end
|
253
|
+
|
254
|
+
it "treats a nil modifier as 'do not modify'" do
|
255
|
+
pool_copy = pool.clone
|
256
|
+
10.times do
|
257
|
+
pool_copy.modify_next( 'hello', nil, 'goodbye' )
|
258
|
+
pool_copy.rand.should_not == pool.rand
|
259
|
+
pool_copy.rand.should == pool.rand
|
260
|
+
pool_copy.rand.should_not == pool.rand
|
261
|
+
pool_copy.rand.should == pool.rand
|
262
|
+
pool_copy.rand.should == pool.rand
|
263
|
+
end
|
264
|
+
end
|
242
265
|
end # modify_next
|
243
266
|
|
244
267
|
describe "#modify_all" do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pool_of_entropy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Neil Slater
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-05-
|
11
|
+
date: 2014-05-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: yard
|
@@ -91,9 +91,11 @@ extra_rdoc_files: []
|
|
91
91
|
files:
|
92
92
|
- ".gitignore"
|
93
93
|
- ".travis.yml"
|
94
|
+
- ".yardopts"
|
94
95
|
- DIEHARDER_TEST.md
|
95
96
|
- Gemfile
|
96
97
|
- LICENSE.txt
|
98
|
+
- RATIONALE.md
|
97
99
|
- README.md
|
98
100
|
- Rakefile
|
99
101
|
- lib/pool_of_entropy.rb
|