Skip to content

Commit 0c7ece4

Browse files
committed
Update after feedback
1 parent ad12db2 commit 0c7ece4

File tree

3 files changed

+94
-70
lines changed

3 files changed

+94
-70
lines changed

lib/crypto/src/crypto.erl

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2344,7 +2344,7 @@ This function is equivalent to `rand_seed_s/0`.
23442344
23452345
The created generator fetches random data with OpenSSL's `RAND_bytes`,
23462346
just like `strong_rand_bytes/1`, and caches it in the generator's state.
2347-
Then 56 bit numbers are extracted from the cache, which makes calculations
2347+
Then 56 bit numbers are extracted from the cache, which makes operations
23482348
in module `m:rand` fast on 64 bit machines.
23492349
23502350
The generator also implements extracting bytes efficiently.
@@ -2483,11 +2483,14 @@ that can be reproduced by re-using the same `Seed`.
24832483
See `rand:seed_s/1`, and for example `rand:uniform_s/2`,
24842484
and compare to `rand_seed_alg/1`.
24852485
2486+
The state objects created by this function has cached data so they use
2487+
much more memory than the generators in the `m:rand` module.
2488+
24862489
#### With `Arg = crypto_aes`
24872490
2488-
To get a long period the Xoroshiro928 generator from the `m:rand` module
2489-
is used as a counter (with period 2^928 - 1) and the generator state
2490-
is scrambled through AES-256 to create a 58-bit pseudo random value.
2491+
The Xoroshiro928 generator from the `m:rand` module is used as a counter.
2492+
The generator's state is scrambled through AES-256 to create a 58-bit
2493+
pseudo random value. This gives a long period (2^928 - 1).
24912494
24922495
The result should be statistically completely unpredictable random values,
24932496
since the scrambling is cryptographically strong and the period is
@@ -2496,11 +2499,13 @@ cryptographically strong since there is no re-keying schedule,
24962499
and since the sequence is repeated for the same seed.
24972500
24982501
- If you need cryptographically strong random numbers use `rand_seed_alg_s/1`
2499-
with `Alg =:= crypto` or `Alg =:= crypto_cache`.
2502+
with `Alg =:= crypto` or `Alg =:= crypto_cache`.
25002503
- If you need to be able to repeat the sequence use this function
2501-
with `Alg =:= crypto_aes`.
2502-
- If you do not need the statistical quality of this generator,
2504+
with `Alg =:= crypto_aes` (or `Alg =:= crypto_prng1`, below).
2505+
- If you do not need the statistical quality of these generators,
25032506
there are faster generators in the `m:rand` module.
2507+
This generator is about 3 times slower than the `rand` module's
2508+
[_default algorithm_](`m:rand#default-algorithm`), *amortized*.
25042509
25052510
#### _Example_
25062511
@@ -2525,20 +2530,20 @@ Thanks to the used generator the state object supports the
25252530
Numbers are generated in batches and for speed reasons cached
25262531
in the generator's state. The cache size can be changed from its default
25272532
value using the [crypto app's ](crypto_app.md) configuration parameter
2528-
`rand_cache_size`. The *amortized* performance is nevertheless
2529-
about 4 times slower than the default PRNG in the `m:rand` module.
2533+
`rand_cache_size`.
25302534
25312535
Generating bytes, see `rand:bytes_s/2`, is done from the cached numbers,
25322536
which limits the performance as for generating numbers. `Alg = crypto`,
25332537
is faster, for larger numbers of bytes significantly faster,
2534-
but cannot be used to reproduce a sequence.
2538+
but cannot be used to reproduce a sequence. Another alternative
2539+
is `Arg = crypto_prng1` that follows here.
25352540
2536-
#### With `Arg = crypto_prng1` *Since OTP @OTP-19882@*.
2541+
#### With `Arg = crypto_prng1` *Since OTP @OTP-19882@*.
25372542
25382543
The created generator uses a stream cipher to encrypt data blocks of zeros,
25392544
which effectively results in the stream cipher's key stream as binary data.
25402545
That binary data is cached in the generator's state, and 58 bit numbers
2541-
are extracted, to make calculations fast in the `m:rand` module.
2546+
are extracted, to make operations fast in the `m:rand` module.
25422547
The cache size can be changed from its default value using the
25432548
[crypto app's ](crypto_app.md) configuration parameter `rand_cache_size`.
25442549
@@ -2551,13 +2556,12 @@ but the generated numbers are not to be regarded as
25512556
cryptographically strong since there is no re-keying schedule,
25522557
and since the sequence is repeated for the same seed.
25532558
2554-
For generating numbers his generator is about 2 times slower
2559+
For generating numbers, this generator is about 2 times slower
25552560
than the default PRNG in the `m:rand` module, *amortized*.
2556-
For generating bytes, this generator is significantly faster
2557-
than the default generator in the `m:rand` module, for any number
2561+
For generating bytes, it is significantly faster, for any number
25582562
of bytes, *amortized*. Compared to `Alg = crypto`, this generator
2559-
has much less overhead so for small numbers of bytes it is much faster.
2560-
The break-even comes a bit above the cache size, and over that
2563+
has much less overhead, so for small numbers of bytes it is much faster.
2564+
Break-even comes a bit above the cache size, and over that
25612565
`Alg = crypto` is faster, but cannot be used to reproduce a sequence.
25622566
25632567
#### _Example_
@@ -2610,15 +2614,14 @@ but avoids a call to the hash function that is used when seeding.
26102614
<<77,185,41,162,118,82,190>>
26112615
6> BytesB.
26122616
<<160,61,224,29,177,30,68>>
2613-
7> Sc0 = crypto:rand_seed_alg_s(crypto_prng1, "my seed").
2614-
8> Sd0 = rand:jump(Sa0).
2615-
9> Sd0 = rand:jump(Sc0).
2617+
7> SA0 = crypto:rand_seed_alg_s(crypto_prng1, "my seed").
2618+
8> SB0 = rand:jump(SA0).
26162619
%% Same values again
2617-
10> {BytesA, Sc1} = rand:bytes_s(7, Sc0).
2618-
11> {BytesB, Sd1} = rand:bytes_s(7, Sd0).
2620+
9> {BytesA, SA1} = rand:bytes_s(7, SA0).
2621+
10> {BytesB, SB1} = rand:bytes_s(7, SB0).
26192622
```
26202623
2621-
#### Crypto algorithm details
2624+
#### _Algorithm details_
26222625
26232626
The `Seed` is hashed with SHA-384 to create a Key and IV
26242627
for AES-256 that is run in CTR mode over blocks of zero data.

lib/stdlib/src/rand.erl

Lines changed: 67 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ that can be used to start from a known state.
5454

5555
This property, and others, make the algorithms in this module
5656
unsuitable for cryptographical applications, but in the `m:crypto` module
57-
there are suitable generators, for this module's
57+
there are such generators, for this module's
5858
[plug-in framework](#plug-in-framework).
5959
See `crypto:rand_seed_s/0` and `crypto:rand_seed_alg_s/1`.
6060

@@ -78,77 +78,75 @@ that add essential or useful funcionality:
7878
* Automatic [seeding](`seed/1`).
7979
* Seeding support for [manual seeding](`seed/2`) to avoid common pitfalls.
8080
* Generating [integers](`t:integer/0`) with
81-
[uniform distribution](`uniform/1`), in *any* range, without bias.
82-
The range is not limited; it may be larger than
83-
the base generator's size (but that costs some performance).
81+
[uniform distribution](`uniform/1`), on *any* range, without bias.
8482
* Generating [floating-point numbers](`t:float/0`) with
8583
[uniform distribution](`uniform/0`).
8684
* Generating [floating-point numbers](`t:float/0`) with
8785
[normal distribution](`normal/0`), standard normal distribution
8886
or [specified mean and variance](`normal/2`).
89-
Note that these operations may under some circumstances not be able
90-
to reproduce a sequence. See the functions' documentation.
9187
* Generating any number of [bytes](`bytes/1`).
92-
* [Jumping](`jump/1`) the generator ahead, in algorithms that support that.
88+
* [Jumping](`jump/1`) the generator ahead (multiple non-overlapping
89+
sequences), in algorithms that support that.
9390

9491
[](){: #usage }
9592
### Usage and examples
9693

9794
Decide if the PRNG state should be stored in the process dictionary
9895
of the calling process (implicit state), or in a state variable
9996
for the calling code to keep track of (explicit state).
100-
The seed and generation functions named with a suffix `_s` handle
101-
an explicit state, and the functions without the suffix operate on
102-
the implicit state. There are are a few exceptions to this rule.
10397

104-
First initialize (seed) a generator, which selects a PRNG
98+
Initialize (seed) a generator, which selects the PRNG
10599
algorithm and creates the initial state. Either use an explicit
106100
`Seed` value which makes it possible to reproduce the PRNG sequence,
107-
or use an automatic seed.
101+
or use an automatic seed. If you use the implicit state and omit this step,
102+
you will get the [_default algorithm_](#default-algorithm)
103+
with an automatic seed.
108104

109-
Then the generator functions can be called.
110-
111-
If a generator function that operates on the implicit state
112-
is called when no implicit state has been generated,
113-
an automatic seed is used to create the implicit state.
105+
Then the generator functions that, for example; generate range limited
106+
uniformly distributed integers, shuffle a list, and so on, can be called.
114107

115108
#### Seeding the generator
116109

117110
Seeding (initializing) is done by calling one of the `seed/1` or
118-
`seed_s/1` functions, which also select which [algorithm](#algorithms)
119-
to use. The `seed/1` functions store the generator and state
111+
`seed_s/1` functions, which also selects which [algorithm](#algorithms)
112+
to use. The `seed/1` functions store the generator and initial state
120113
in the process dictionary, while the `seed_s/1` functions
121-
only return the state, which requires the calling code
122-
to handle the state and updates to it.
114+
only return the initial state.
123115

124116
The seed functions that do not have a `Seed` argument
125-
create an automatic seed that should be unique to the created
117+
create an automatic seed which is designed to be unique to the created
126118
generator instance; see `seed_s/1`.
127119

128120
If an automatic seed is not desired, the seed functions that have a
129-
[`Seed`](`t:seed/0`) argument can be used. The argument has
121+
[`Seed`](`t:seed/0`) argument should be used. The argument has
130122
3 possible formats; see the `t:seed/0` type description.
131123

124+
There are also seeding functions for generators in the `m:crypto` module.
125+
See the section [Plug-In Generators](`m:crypto#plug-in-generators`).
126+
132127
#### Using the generator
133128

134129
The [Plug-in framework API](#plug-in-framework-api) generator functions
135-
named with the suffix `_s` take an explicit state as their last argument
136-
and return the new state as the last element in the returned tuple.
137-
The process dictionary is not used.
138-
139-
Sibling functions without that suffix take operate on the implicit state
140-
stored in the process dictionary, and only return their "interesting "
141-
output value. If the process dictionary has no PRNG state,
142-
[`seed(default)`](`seed/1`) is implicitly called to create an automatic seed
130+
named with the suffix `_s`, with a few exceptions, take an explicit state
131+
as their last argument and return the new state as the last element
132+
in the returned tuple. The new state shall be used when calling
133+
the next generator function, and so on. The process dictionary is not used.
134+
135+
Sibling functions without that suffix operate on the implicit state
136+
stored in the process dictionary, and only return their "interesting"
137+
output value. If the process dictionary has no stored implicit state,
138+
[`seed(default)`](`seed/1`) is called to create an automatic seed
143139
for the [_default algorithm_](#default-algorithm), as initial state.
144140

145141
*Generator functions*:
146142

147143
* `uniform/1` and `uniform_s/2` generate *uniformly distributed integers*
148-
on **any** (unlimited) range.
144+
on **any** (unlimited) specified range, without bias.
149145
* `uniform/0`, `uniform_s/1`, `uniform_real/0` and `uniform_real_s/1`
150-
generate *uniformly distributed floating point numbers*.
146+
generate *uniformly distributed floating point numbers*
147+
on the range [0.0, 1.0).
151148
* `bytes/1` and `bytes_s/2` generate *uniformly distributed bytes*.
149+
See the note under `bytes_s/2` about efficiency.
152150
* `shuffle/1` and `shuffle_s/2` *shuffle a list*.
153151

154152
Those generator functions use one or more raw numbers from the generator
@@ -165,7 +163,7 @@ to do correctly and efficiently.
165163
Those generator functions have to use a number of floating point
166164
calculations, that on different platforms with different math library
167165
implementations, optimizations, compilation flags such as
168-
gcc:s `-ffast-math`, etc, may produce slightly different values.
166+
gcc's `-ffast-math`, etc, may produce slightly different values.
169167

170168
Furthermore these slightly different values may cause the implementation
171169
to do a recursive retry on one platform that is not done on another,
@@ -323,7 +321,9 @@ from different seeds it is assured that the generated sequences
323321
do not overlap. The alternative of using different seeds
324322
may accidentally start the generators in sequence positions
325323
that are close to each other, but a jump function jumps
326-
to a sequence position very far ahead.
324+
to a sequence position so far ahead that the generator
325+
at the jumped from position will never arrive
326+
at the jumped to position.
327327

328328
To create numbers with normal distribution the
329329
[Ziggurat Method by Marsaglia and Tsang](http://www.jstatsoft.org/v05/i08)
@@ -438,8 +438,19 @@ relying on them will produce the same pseudo random sequences as before.
438438
> #### Note {: .info }
439439
>
440440
> The builtin random number generator algorithms are not cryptographically
441-
> strong. If a cryptographically strong random number generator is needed,
441+
> strong. If a *cryptographically strong* random number generator is needed,
442442
> use for example `crypto:rand_seed_s/0` or `crypto:rand_seed_alg_s/1`.
443+
>
444+
> There are also generators for *cryptographically unpredictable*
445+
> pseudo random numbers: see `crypto:rand_seed_alg/2` and
446+
> `crypto:rand_seed_alg_s/2`. They are generated using cryptographical
447+
> primitives so the statistical quality is impeccable, but the
448+
> generated sequence can be repeated, and therefore cannot be regarded as
449+
> *cryptographically strong*.
450+
>
451+
> The generators in the [`crypto`](`m:crypto#plug-in-generators`)
452+
> module are much slower at generating numbers and/or require
453+
> a much larger state than the generators in this module.
443454
444455
For all these generators except `exro928ss` and `exsss` the lowest bit(s)
445456
have got a slightly less random behaviour than all other bits.
@@ -482,14 +493,14 @@ special purpose algorithms that do not use the
482493
[plug-in framework](#plug-in-framework), mainly for performance reasons.
483494

484495
Since these algorithms lack the plug-in framework support, generating numbers
485-
in a range other than the base generator's range may become a problem.
496+
on a range other than the base generator's range may become a problem.
486497

487498
There are at least four ways to do this, assuming the `Range` is less than
488499
the generator's range:
489500

490501
[](){: #modulo-method }
491502
- **Modulo**
492-
To generate a number `V` in the range `0 .. Range-1`:
503+
To generate a number `V` on the range `0 .. Range-1`:
493504

494505
> Generate a number `X`.
495506
> Use `V = X rem Range` as your value.
@@ -570,8 +581,9 @@ the generator's range:
570581
but in practice you ensure that the probability of rejection is low.
571582
Then the probability for yet another iteration decreases exponentially
572583
so the expected mean number of iterations will often be between 1 and 2.
573-
Also, since the base generator is a full length generator,
574-
a value that will break the loop must eventually be generated.
584+
Also, since base generators in general are full length generators,
585+
they traverse all values of their state, so a value that will break the loop
586+
must eventually be generated.
575587

576588
These methods can be combined, such as using
577589
the [Modulo](#modulo-method) method and only if the generator value
@@ -584,7 +596,7 @@ will not create a bignum.
584596
The recommended way to generate a floating point number
585597
(IEEE 745 Double, that has got a 53-bit mantissa) in the range
586598
`0 .. 1`, that is `0.0 =< V < 1.0` is to generate a 53-bit number `X`
587-
and then use `V = X * (1.0/((1 bsl 53)))` as your value.
599+
and then use `V = X * 2#1.0*e-53` as your value.
588600
This will create a value of the form N*2^-53 with equal probability
589601
for every possible N for the range.
590602
""".
@@ -1189,6 +1201,13 @@ From the specified `State`, generates a random number `X ::` `t:integer/0`,
11891201
uniformly distributed in the specified range `1 =< X =< N`.
11901202
Returns the number `X` and the updated `NewState`.
11911203

1204+
The range is not limited, it may be larger than the base generator's
1205+
size, although that costs some performance since multiple
1206+
base generator numbers have to be used and probably also bignum operations.
1207+
1208+
The generated numbers are bias free, even if the range is
1209+
not a divisor of the base generator size, or larger than the same.
1210+
11921211
#### _Shell Example_
11931212

11941213
```erlang
@@ -1537,7 +1556,7 @@ with that number of random bytes.
15371556

15381557
The selected algorithm is used to generate as many random numbers
15391558
as required to compose the `t:binary/0`. Returns the generated
1540-
[`Bytes`](`t:binary/0`) and a [`NewState`](`t:state/0`).
1559+
[`Bytes`](`t:binary/0`) and the [`NewState`](`t:state/0`).
15411560

15421561
> ### Note {: .info }
15431562
>
@@ -1555,7 +1574,9 @@ as required to compose the `t:binary/0`. Returns the generated
15551574
>
15561575
> A plug-in generator may implement a dedicated callback
15571576
> for generating bytes, to mitigate this problem, which in that case
1558-
> is stated in the generator's documentation.
1577+
> is stated in the generator's documentation. See
1578+
> [`crypto:rand_seed_alg(crypto_prng1, Seed)`](`crypto:rand_seed_alg_s/2`),
1579+
> which is repeatable and efficient.
15591580
15601581
#### _Shell Example_
15611582

@@ -1885,9 +1906,9 @@ has the same probability.
18851906

18861907
In other words, the quality of the shuffling depends only on
18871908
the quality of the backend [random number generator](#algorithms)
1888-
and [seed](`seed_s/1`). If a cryptographically unpredictable
1889-
shuffling is needed, use for example `crypto:rand_seed_alg_s/1`
1890-
to initialize the random number generator.
1909+
and [seed](`seed_s/1`). If a cryptographical quality shuffling is needed,
1910+
use for example `crypto:rand_seed_alg_s/2` to initialize
1911+
the random number generator.
18911912

18921913
Returns the shuffled list [`ShuffledList`](`t:list/0`)
18931914
and the [`NewState`](`t:state/0`).

lib/stdlib/test/rand_SUITE.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ seed(Config) when is_list(Config) ->
214214
seed_1(Alg) ->
215215
%% For all repeatable PRNGS, the initial state should be
216216
%% possible to clone, but not necessarily compare,
217-
%% since we want to be able to optimize the implementatin
217+
%% since we want to be able to optimize the implementation
218218
%%
219219
%% Choosing algo and seed
220220
S1 = rand_crypto_seed(Alg, {0, 0, 0}),

0 commit comments

Comments
 (0)