pool_of_entropy 0.0.1 → 0.0.2
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 +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
|