aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/crypto/doc/src/crypto.xml35
-rw-r--r--lib/stdlib/doc/src/rand.xml43
-rw-r--r--lib/stdlib/src/rand.erl68
-rw-r--r--lib/stdlib/test/rand_SUITE.erl33
4 files changed, 120 insertions, 59 deletions
diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml
index 30835a8447..552d95d7dc 100644
--- a/lib/crypto/doc/src/crypto.xml
+++ b/lib/crypto/doc/src/crypto.xml
@@ -732,14 +732,17 @@
<func>
<name>rand_seed() -> rand:state()</name>
- <fsummary>Strong random number generation plugin state</fsummary>>
- <desc>
- Creates state object for <seealso marker="stdlib:rand">random number generation</seealso>,
- in order to generate cryptographically strong random numbers
- (based on OpenSSL's <c>BN_rand_range</c>),
- and saves it on process dictionary before returning it as well.
- See also <seealso marker="stdlib:rand#seed-1">rand:seed/1</seealso>
-
+ <fsummary>Strong random number generation plugin state</fsummary>
+ <desc>
+ <p>
+ Creates state object for
+ <seealso marker="stdlib:rand">random number generation</seealso>,
+ in order to generate cryptographically strong random numbers
+ (based on OpenSSL's <c>BN_rand_range</c>),
+ and saves it on process dictionary before returning it as well.
+ See also
+ <seealso marker="stdlib:rand#seed-1">rand:seed/1</seealso>.
+ </p>
<p><em>Example</em></p>
<pre>
_ = crypto:rand_seed(),
@@ -750,12 +753,16 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre>
<func>
<name>rand_seed_s() -> rand:state()</name>
- <fsummary>Strong random number generation plugin state</fsummary>>
- <desc>
- Creates state object for <seealso marker="stdlib:rand">random number generation</seealso>,
- in order to generate cryptographically strongly random numbers
- (based on OpenSSL's <c>BN_rand_range</c>).
- See also <seealso marker="stdlib:rand#seed_s-1">rand:seed_s/1</seealso>
+ <fsummary>Strong random number generation plugin state</fsummary>
+ <desc>
+ <p>
+ Creates state object for
+ <seealso marker="stdlib:rand">random number generation</seealso>,
+ in order to generate cryptographically strongly random numbers
+ (based on OpenSSL's <c>BN_rand_range</c>).
+ See also
+ <seealso marker="stdlib:rand#seed_s-1">rand:seed_s/1</seealso>.
+ </p>
</desc>
</func>
diff --git a/lib/stdlib/doc/src/rand.xml b/lib/stdlib/doc/src/rand.xml
index e7a5fb7fab..2ddf3021ac 100644
--- a/lib/stdlib/doc/src/rand.xml
+++ b/lib/stdlib/doc/src/rand.xml
@@ -139,7 +139,19 @@ S0 = rand:seed_s(exsplus),
<name name="alg_handler"/>
</datatype>
<datatype>
- <name name="alg_seed"/>
+ <name name="alg_state"/>
+ </datatype>
+ <datatype>
+ <name name="exs64_state"/>
+ <desc><p>Algorithm specific internal state</p></desc>
+ </datatype>
+ <datatype>
+ <name name="exsplus_state"/>
+ <desc><p>Algorithm specific internal state</p></desc>
+ </datatype>
+ <datatype>
+ <name name="exs1024_state"/>
+ <desc><p>Algorithm specific internal state</p></desc>
</datatype>
<datatype>
<name name="state"/>
@@ -147,8 +159,11 @@ S0 = rand:seed_s(exsplus),
</datatype>
<datatype>
<name name="export_state"/>
- <desc><p>Algorithm-dependent state that can be printed or saved to
- file.</p></desc>
+ <desc>
+ <p>
+ Algorithm-dependent state that can be printed or saved to file.
+ </p>
+ </desc>
</datatype>
</datatypes>
@@ -223,8 +238,11 @@ S0 = rand:seed_s(exsplus),
<fsummary>Seed random number generator.</fsummary>
<desc>
<marker id="seed-1"/>
- <p>Seeds random number generation with the specifed algorithm and
- time-dependent data if <anno>AlgOrStateOrExpState</anno> is an algorithm.</p>
+ <p>
+ Seeds random number generation with the specifed algorithm and
+ time-dependent data if <c><anno>AlgOrStateOrExpState</anno></c>
+ is an algorithm.
+ </p>
<p>Otherwise recreates the exported seed in the process dictionary,
and returns the state. See also
<seealso marker="#export_seed-0"><c>export_seed/0</c></seealso>.</p>
@@ -244,8 +262,11 @@ S0 = rand:seed_s(exsplus),
<name name="seed_s" arity="1"/>
<fsummary>Seed random number generator.</fsummary>
<desc>
- <p>Seeds random number generation with the specifed algorithm and
- time-dependent data if <anno>AlgOrStateOrExpState</anno> is an algorithm.</p>
+ <p>
+ Seeds random number generation with the specifed algorithm and
+ time-dependent data if <c><anno>AlgOrStateOrExpState</anno></c>
+ is an algorithm.
+ </p>
<p>Otherwise recreates the exported seed and returns the state.
See also <seealso marker="#export_seed-0">
<c>export_seed/0</c></seealso>.</p>
@@ -266,7 +287,7 @@ S0 = rand:seed_s(exsplus),
<fsummary>Return a random float.</fsummary>
<desc><marker id="uniform-0"/>
<p>Returns a random float uniformly distributed in the value
- range <c>0.0 &lt; <anno>X</anno> &lt; 1.0</c> and
+ range <c>0.0 =&lt; <anno>X</anno> &lt; 1.0</c> and
updates the state in the process dictionary.</p>
</desc>
</func>
@@ -277,7 +298,7 @@ S0 = rand:seed_s(exsplus),
<desc><marker id="uniform-1"/>
<p>Returns, for a specified integer <c><anno>N</anno> >= 1</c>,
a random integer uniformly distributed in the value range
- <c>1 &lt;= <anno>X</anno> &lt;= <anno>N</anno></c> and
+ <c>1 =&lt; <anno>X</anno> =&lt; <anno>N</anno></c> and
updates the state in the process dictionary.</p>
</desc>
</func>
@@ -287,7 +308,7 @@ S0 = rand:seed_s(exsplus),
<fsummary>Return a random float.</fsummary>
<desc>
<p>Returns, for a specified state, random float
- uniformly distributed in the value range <c>0.0 &lt;
+ uniformly distributed in the value range <c>0.0 =&lt;
<anno>X</anno> &lt; 1.0</c> and a new state.</p>
</desc>
</func>
@@ -298,7 +319,7 @@ S0 = rand:seed_s(exsplus),
<desc>
<p>Returns, for a specified integer <c><anno>N</anno> >= 1</c>
and a state, a random integer uniformly distributed in the value
- range <c>1 &lt;= <anno>X</anno> &lt;= <anno>N</anno></c> and a
+ range <c>1 =&lt; <anno>X</anno> =&lt; <anno>N</anno></c> and a
new state.</p>
</desc>
</func>
diff --git a/lib/stdlib/src/rand.erl b/lib/stdlib/src/rand.erl
index 60da53cd2b..dfd102f9ef 100644
--- a/lib/stdlib/src/rand.erl
+++ b/lib/stdlib/src/rand.erl
@@ -45,22 +45,31 @@
%% =====================================================================
%% This depends on the algorithm handler function
--type alg_seed() :: exs64_state() | exsplus_state() | exs1024_state() | term().
+-type alg_state() ::
+ exs64_state() | exsplus_state() | exs1024_state() | term().
%% This is the algorithm handler function within this module
--type alg_handler() :: #{type := alg(),
- max := integer() | infinity,
- next := fun((alg_seed()) -> {uint64(), alg_seed()}),
- uniform := fun((state()) -> {float(), state()}),
- uniform_n := fun((pos_integer(), state()) -> {pos_integer(), state()}),
- jump := fun((state()) -> state())}.
+-type alg_handler() ::
+ #{type := alg(),
+ max := integer() | infinity,
+ next :=
+ fun((alg_state()) -> {non_neg_integer(), alg_state()}),
+ uniform :=
+ fun((state()) -> {float(), state()}),
+ uniform_n :=
+ fun((pos_integer(), state()) -> {pos_integer(), state()}),
+ jump :=
+ fun((state()) -> state())}.
%% Algorithm state
--type state() :: {alg_handler(), alg_seed()}.
+-type state() :: {alg_handler(), alg_state()}.
-type builtin_alg() :: exs64 | exsplus | exs1024.
-type alg() :: builtin_alg() | atom().
--type export_state() :: {alg(), alg_seed()}.
--export_type([builtin_alg/0, alg/0, alg_handler/0, alg_seed/0, state/0, export_state/0]).
+-type export_state() :: {alg(), alg_state()}.
+-export_type(
+ [builtin_alg/0, alg/0, alg_handler/0, alg_state/0,
+ state/0, export_state/0]).
+-export_type([exs64_state/0, exsplus_state/0, exs1024_state/0]).
%% =====================================================================
%% API
@@ -74,7 +83,7 @@ export_seed() ->
_ -> undefined
end.
--spec export_seed_s(state()) -> export_state().
+-spec export_seed_s(State :: state()) -> export_state().
export_seed_s({#{type:=Alg}, Seed}) -> {Alg, Seed}.
%% seed(Alg) seeds RNG with runtime dependent values
@@ -83,11 +92,15 @@ export_seed_s({#{type:=Alg}, Seed}) -> {Alg, Seed}.
%% seed({Alg,Seed}) setup RNG with a previously exported seed
%% and return the NEW state
--spec seed(AlgOrStateOrExpState::builtin_alg() | state() | export_state()) -> state().
+-spec seed(
+ AlgOrStateOrExpState :: builtin_alg() | state() | export_state()) ->
+ state().
seed(Alg) ->
seed_put(seed_s(Alg)).
--spec seed_s(AlgOrStateOrExpState::builtin_alg() | state() | export_state()) -> state().
+-spec seed_s(
+ AlgOrStateOrExpState :: builtin_alg() | state() | export_state()) ->
+ state().
seed_s({AlgHandler, _Seed} = State) when is_map(AlgHandler) ->
State;
seed_s({Alg0, Seed}) ->
@@ -101,11 +114,15 @@ seed_s(Alg) ->
%% seed/2: seeds RNG with the algorithm and given values
%% and returns the NEW state.
--spec seed(Alg :: builtin_alg(), {integer(), integer(), integer()}) -> state().
+-spec seed(
+ Alg :: builtin_alg(), Seed :: {integer(), integer(), integer()}) ->
+ state().
seed(Alg0, S0) ->
seed_put(seed_s(Alg0, S0)).
--spec seed_s(Alg :: builtin_alg(), {integer(), integer(), integer()}) -> state().
+-spec seed_s(
+ Alg :: builtin_alg(), Seed :: {integer(), integer(), integer()}) ->
+ state().
seed_s(Alg0, S0 = {_, _, _}) ->
{Alg, Seed} = mk_alg(Alg0),
AS = Seed(S0),
@@ -117,7 +134,7 @@ seed_s(Alg0, S0 = {_, _, _}) ->
%% uniform/0: returns a random float X where 0.0 < X < 1.0,
%% updating the state in the process dictionary.
--spec uniform() -> X::float().
+-spec uniform() -> X :: float().
uniform() ->
{X, Seed} = uniform_s(seed_get()),
_ = seed_put(Seed),
@@ -127,7 +144,7 @@ uniform() ->
%% uniform/1 returns a random integer X where 1 =< X =< N,
%% updating the state in the process dictionary.
--spec uniform(N :: pos_integer()) -> X::pos_integer().
+-spec uniform(N :: pos_integer()) -> X :: pos_integer().
uniform(N) ->
{X, Seed} = uniform_s(N, seed_get()),
_ = seed_put(Seed),
@@ -137,7 +154,7 @@ uniform(N) ->
%% returns a random float X where 0.0 < X < 1.0,
%% and a new state.
--spec uniform_s(state()) -> {X::float(), NewS :: state()}.
+-spec uniform_s(State :: state()) -> {X :: float(), NewState :: state()}.
uniform_s(State = {#{uniform:=Uniform}, _}) ->
Uniform(State).
@@ -145,7 +162,8 @@ uniform_s(State = {#{uniform:=Uniform}, _}) ->
%% uniform_s/2 returns a random integer X where 1 =< X =< N,
%% and a new state.
--spec uniform_s(N::pos_integer(), state()) -> {X::pos_integer(), NewS::state()}.
+-spec uniform_s(N :: pos_integer(), State :: state()) ->
+ {X :: pos_integer(), NewState :: state()}.
uniform_s(N, State = {#{uniform_n:=Uniform, max:=Max}, _})
when 0 < N, N =< Max ->
Uniform(N, State);
@@ -159,7 +177,7 @@ uniform_s(N, State0 = {#{uniform:=Uniform}, _})
%% after a large number of call defined for each algorithm.
%% The large number is algorithm dependent.
--spec jump(state()) -> NewS :: state().
+-spec jump(state()) -> NewState :: state().
jump(State = {#{jump:=Jump}, _}) ->
Jump(State).
@@ -168,7 +186,7 @@ jump(State = {#{jump:=Jump}, _}) ->
%% and write back the new value to the internal state,
%% then returns the new value.
--spec jump() -> NewS :: state().
+-spec jump() -> NewState :: state().
jump() ->
seed_put(jump(seed_get())).
@@ -186,7 +204,7 @@ normal() ->
%% The Ziggurat Method for generating random variables - Marsaglia and Tsang
%% Paper and reference code: http://www.jstatsoft.org/v05/i08/
--spec normal_s(state()) -> {float(), NewS :: state()}.
+-spec normal_s(State :: state()) -> {float(), NewState :: state()}.
normal_s(State0) ->
{Sign, R, State} = get_52(State0),
Idx = R band 16#FF,
@@ -249,7 +267,7 @@ mk_alg(exs1024) ->
%% Reference URL: http://xorshift.di.unimi.it/
%% =====================================================================
--type exs64_state() :: uint64().
+-opaque exs64_state() :: uint64().
exs64_seed({A1, A2, A3}) ->
{V1, _} = exs64_next(((A1 band ?UINT32MASK) * 4294967197 + 1)),
@@ -284,7 +302,7 @@ exs64_jump(_) ->
%% Modification of the original Xorshift128+ algorithm to 116
%% by Sebastiano Vigna, a lot of thanks for his help and work.
%% =====================================================================
--type exsplus_state() :: nonempty_improper_list(uint58(), uint58()).
+-opaque exsplus_state() :: nonempty_improper_list(uint58(), uint58()).
-dialyzer({no_improper_lists, exsplus_seed/1}).
@@ -353,7 +371,7 @@ exsplus_jump(S, [AS0|AS1], J, N) ->
%% Reference URL: http://xorshift.di.unimi.it/
%% =====================================================================
--type exs1024_state() :: {list(uint64()), list(uint64())}.
+-opaque exs1024_state() :: {list(uint64()), list(uint64())}.
exs1024_seed({A1, A2, A3}) ->
B1 = (((A1 band ?UINT21MASK) + 1) * 2097131) band ?UINT21MASK,
diff --git a/lib/stdlib/test/rand_SUITE.erl b/lib/stdlib/test/rand_SUITE.erl
index fe5eaccda5..098eefeb61 100644
--- a/lib/stdlib/test/rand_SUITE.erl
+++ b/lib/stdlib/test/rand_SUITE.erl
@@ -356,14 +356,23 @@ basic_normal_1(0, {#{type:=Alg}, _}, Sum, SumSq) ->
%% Test that the user can write algorithms.
plugin(Config) when is_list(Config) ->
- _ = lists:foldl(fun(_, S0) ->
- {V1, S1} = rand:uniform_s(10000, S0),
- true = is_integer(V1),
- {V2, S2} = rand:uniform_s(S1),
- true = is_float(V2),
- S2
- end, crypto_seed(), lists:seq(1, 200)),
- ok.
+ try crypto:strong_rand_bytes(1) of
+ <<_>> ->
+ _ = lists:foldl(
+ fun(_, S0) ->
+ {V1, S1} = rand:uniform_s(10000, S0),
+ true = is_integer(V1),
+ {V2, S2} = rand:uniform_s(S1),
+ true = is_float(V2),
+ S2
+ end, crypto_seed(), lists:seq(1, 200)),
+ ok
+ catch
+ error:low_entropy ->
+ {skip,low_entropy};
+ error:undef ->
+ {skip,no_crypto}
+ end.
%% Test implementation
crypto_seed() ->
@@ -397,7 +406,13 @@ crypto_uniform_n(N, State0) ->
measure(Suite) when is_atom(Suite) -> [];
measure(_Config) ->
ct:timetrap({minutes,15}), %% valgrind needs a lot of time
- Algos = [crypto64|algs()],
+ Algos =
+ try crypto:strong_rand_bytes(1) of
+ <<_>> -> [crypto64]
+ catch
+ error:low_entropy -> [];
+ error:undef -> []
+ end ++ algs(),
io:format("RNG uniform integer performance~n",[]),
_ = measure_1(random, fun(State) -> {int, random:uniform_s(10000, State)} end),
_ = [measure_1(Algo, fun(State) -> {int, rand:uniform_s(10000, State)} end) || Algo <- Algos],