aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/crypto/doc/src/crypto.xml29
-rw-r--r--lib/crypto/src/crypto.erl30
-rw-r--r--lib/crypto/test/crypto_SUITE.erl102
-rw-r--r--lib/stdlib/doc/src/rand.xml24
-rw-r--r--lib/stdlib/src/rand.erl36
5 files changed, 195 insertions, 26 deletions
diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml
index dc725a41be..7a5bd62c26 100644
--- a/lib/crypto/doc/src/crypto.xml
+++ b/lib/crypto/doc/src/crypto.xml
@@ -767,6 +767,35 @@
</func>
<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>
+
+ <p><em>Example</em></p>
+ <pre>
+crypto:rand_seed(),
+_IntegerValue = rand:uniform(42), % [1; 42]
+_FloatValue = rand:uniform(). % [0.0; 1.0]</pre>
+ </desc>
+ </func>
+
+ <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>
+ </desc>
+ </func>
+
+ <func>
<name>stream_init(Type, Key) -> State</name>
<fsummary></fsummary>
<type>
diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl
index 2c3208a3d5..4ae7a9cdd6 100644
--- a/lib/crypto/src/crypto.erl
+++ b/lib/crypto/src/crypto.erl
@@ -32,6 +32,8 @@
-export([exor/2, strong_rand_bytes/1, mod_pow/3]).
-export([strong_rand_range/1]).
-export([strong_rand_float/0]).
+-export([rand_seed/0]).
+-export([rand_seed_s/0]).
-export([rand_uniform/2]).
-export([block_encrypt/3, block_decrypt/3, block_encrypt/4, block_decrypt/4]).
-export([next_iv/2, next_iv/3]).
@@ -290,6 +292,8 @@ stream_decrypt(State, Data0) ->
-spec strong_rand_bytes(non_neg_integer()) -> binary().
-spec strong_rand_range(pos_integer() | binary()) -> binary().
-spec strong_rand_float() -> float().
+-spec rand_seed() -> rand:state().
+-spec rand_seed_s() -> rand:state().
-spec rand_uniform(crypto_integer(), crypto_integer()) ->
crypto_integer().
@@ -311,7 +315,6 @@ strong_rand_range(BinRange) when is_binary(BinRange) ->
<<BinResult/binary>> ->
BinResult
end.
-
strong_rand_range_nif(_BinRange) -> ?nif_stub.
@@ -325,6 +328,31 @@ strong_rand_float() ->
Value - 1.0.
+rand_seed() ->
+ rand:seed(rand_seed_s()).
+
+rand_seed_s() ->
+ {#{ type => crypto,
+ max => infinity,
+ next => fun rand_plugin_next/1,
+ uniform => fun rand_plugin_uniform/1,
+ uniform_n => fun rand_plugin_uniform/2,
+ jump => fun rand_plugin_jump/1},
+ no_seed}.
+
+rand_plugin_next(Seed) ->
+ {bytes_to_integer(strong_rand_range(1 bsl 64)), Seed}.
+
+rand_plugin_uniform(State) ->
+ {strong_rand_float(), State}.
+
+rand_plugin_uniform(Max, State) ->
+ {bytes_to_integer(strong_rand_range(Max)) + 1, State}.
+
+rand_plugin_jump(State) ->
+ State.
+
+
rand_uniform(From,To) when is_binary(From), is_binary(To) ->
case rand_uniform_nif(From,To) of
<<Len:32/integer, MSB, Rest/binary>> when MSB > 127 ->
diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl
index 0d80786fbc..482a07d634 100644
--- a/lib/crypto/test/crypto_SUITE.erl
+++ b/lib/crypto/test/crypto_SUITE.erl
@@ -38,7 +38,9 @@ all() ->
exor,
rand_uniform,
strong_rand_range,
- strong_rand_float
+ strong_rand_float,
+ rand_plugin,
+ rand_plugin_s
].
groups() ->
@@ -526,6 +528,17 @@ strong_rand_float(Config) when is_list(Config) ->
Samples).
%%--------------------------------------------------------------------
+rand_plugin() ->
+ [{doc, "crypto rand plugin testing (implicit state / process dictionary)"}].
+rand_plugin(Config) when is_list(Config) ->
+ rand_plugin_aux(implicit_state).
+
+rand_plugin_s() ->
+ [{doc, "crypto rand plugin testing (explicit state)"}].
+rand_plugin_s(Config) when is_list(Config) ->
+ rand_plugin_aux(explicit_state).
+
+%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
hash(_, [], []) ->
@@ -991,6 +1004,14 @@ crypto_rand_uniform(L,H) ->
ct:fail({"Not in interval", R1, L, H})
end.
+foldallmap(_Fun, AccN, []) ->
+ {true, AccN};
+foldallmap(Fun, AccN, [H|T]) ->
+ case Fun(H, AccN) of
+ {true, AccM} -> foldallmap(Fun, AccM, T);
+ {{false, Result}, AccM} -> {Result, AccM}
+ end.
+
allmap(_Fun, []) ->
true;
allmap(Fun, [H|T]) ->
@@ -999,6 +1020,85 @@ allmap(Fun, [H|T]) ->
{false, Result} -> Result
end.
+rand_plugin_aux(StateType) ->
+ {Seeder, SeedExporter, FloatGenerator, IntegerGenerator} = rand_plugin_functions(StateType),
+ State0 = Seeder(),
+ {crypto, no_seed} = SeedExporter(State0),
+ {FloatTestResult, State1} = rand_plugin_aux_floats(State0, FloatGenerator),
+ case FloatTestResult of
+ true ->
+ {IntegerTestResult, _State2} = rand_plugin_aux_integers(State1, IntegerGenerator),
+ IntegerTestResult;
+ {false, _} ->
+ FloatTestResult
+ end.
+
+% returns {Seeder, SeedExporter, FloatGenerator, IntegerGenerator} with consistent signatures
+rand_plugin_functions(implicit_state) ->
+ {fun () -> crypto:rand_seed(), implicit_state end,
+ fun (implicit_state) -> rand:export_seed() end,
+ fun (implicit_state) -> {rand:uniform(), implicit_state} end,
+ fun (N, implicit_state) -> {rand:uniform(N), implicit_state} end};
+rand_plugin_functions(explicit_state) ->
+ {fun crypto:rand_seed_s/0,
+ fun rand:export_seed_s/1,
+ fun rand:uniform_s/1,
+ fun rand:uniform_s/2}.
+
+rand_plugin_aux_floats(State0, FloatGenerator) ->
+ {FloatSamples, State1} =
+ lists:mapfoldl(
+ fun (_, StateAcc) ->
+ FloatGenerator(StateAcc)
+ end,
+ State0,
+ lists:seq(1, 10000)),
+
+ {allmap(
+ fun (V) ->
+ (V >= 0.0 andalso V < 1.0)
+ orelse {false, ct:fail({"Float sample not in interval", V, 0.0, 1.0})}
+ end,
+ FloatSamples),
+ State1}.
+
+rand_plugin_aux_integers(State0, IntegerGenerator) ->
+ MaxIntegerCeiling = 1 bsl 32,
+ {IntegerCeilings, State1} =
+ lists:mapfoldl(
+ fun (_, StateAcc) ->
+ IntegerGenerator(MaxIntegerCeiling, StateAcc)
+ end,
+ State0,
+ lists:seq(1, 100)),
+
+ foldallmap(
+ fun (Ceiling, StateAcc) ->
+ case Ceiling >= 1 andalso Ceiling =< MaxIntegerCeiling of
+ false ->
+ {{false, ct:fail({"Integer ceiling not in interval",
+ Ceiling, 1, MaxIntegerCeiling})},
+ StateAcc};
+ true ->
+ foldallmap(
+ fun (_, SubStateAcc) ->
+ {Sample, NewSubStateAcc} = IntegerGenerator(Ceiling, SubStateAcc),
+ case Sample >= 1 andalso Sample =< Ceiling of
+ false ->
+ {{false, ct:fail({"Integer sample not in interval",
+ Sample, 1, Ceiling})},
+ NewSubStateAcc};
+ true ->
+ {true, NewSubStateAcc}
+ end
+ end,
+ StateAcc,
+ lists:seq(1, 100))
+ end
+ end,
+ State1,
+ IntegerCeilings).
+
%%--------------------------------------------------------------------
%% Test data ------------------------------------------------
%%--------------------------------------------------------------------
diff --git a/lib/stdlib/doc/src/rand.xml b/lib/stdlib/doc/src/rand.xml
index 8745e16908..e7a5fb7fab 100644
--- a/lib/stdlib/doc/src/rand.xml
+++ b/lib/stdlib/doc/src/rand.xml
@@ -120,20 +120,28 @@ S0 = rand:seed_s(exsplus),
{SND0, S2} = rand:normal_s(S1),</pre>
<note>
- <p>This random number generator is not cryptographically
- strong. If a strong cryptographic random number generator is
- needed, use one of functions in the
- <seealso marker="crypto:crypto"><c>crypto</c></seealso>
- module, for example, <seealso marker="crypto:crypto">
- <c>crypto:strong_rand_bytes/1</c></seealso>.</p>
+ <p>The builtin random number generator algorithms are not
+ cryptographically strong. If a cryptographically strong
+ random number generator is needed, use something like
+ <seealso marker="crypto:crypto#rand_seed-0"><c>crypto:rand_seed/0</c></seealso>.
+ </p>
</note>
</description>
<datatypes>
<datatype>
+ <name name="builtin_alg"/>
+ </datatype>
+ <datatype>
<name name="alg"/>
</datatype>
<datatype>
+ <name name="alg_handler"/>
+ </datatype>
+ <datatype>
+ <name name="alg_seed"/>
+ </datatype>
+ <datatype>
<name name="state"/>
<desc><p>Algorithm-dependent state.</p></desc>
</datatype>
@@ -216,7 +224,7 @@ S0 = rand:seed_s(exsplus),
<desc>
<marker id="seed-1"/>
<p>Seeds random number generation with the specifed algorithm and
- time-dependent data if <anno>AlgOrExpState</anno> is an algorithm.</p>
+ time-dependent data if <anno>AlgOrStateOrExpState</anno> 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>
@@ -237,7 +245,7 @@ S0 = rand:seed_s(exsplus),
<fsummary>Seed random number generator.</fsummary>
<desc>
<p>Seeds random number generation with the specifed algorithm and
- time-dependent data if <anno>AlgOrExpState</anno> is an algorithm.</p>
+ time-dependent data if <anno>AlgOrStateOrExpState</anno> 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>
diff --git a/lib/stdlib/src/rand.erl b/lib/stdlib/src/rand.erl
index 1f457b9e0e..3ff7aaf15f 100644
--- a/lib/stdlib/src/rand.erl
+++ b/lib/stdlib/src/rand.erl
@@ -45,20 +45,22 @@
%% =====================================================================
%% This depends on the algorithm handler function
--type alg_seed() :: exs64_state() | exsplus_state() | exs1024_state().
+-type alg_seed() :: exs64_state() | exsplus_state() | exs1024_state() | term().
+
%% This is the algorithm handler function within this module
-type alg_handler() :: #{type := alg(),
- max := integer(),
- next := fun(),
- uniform := fun(),
- uniform_n := fun(),
- jump := fun()}.
-
-%% Internal state
--opaque state() :: {alg_handler(), alg_seed()}.
--type alg() :: exs64 | exsplus | exs1024.
--opaque export_state() :: {alg(), alg_seed()}.
--export_type([alg/0, state/0, export_state/0]).
+ 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())}.
+
+%% Algorithm state
+-type state() :: {alg_handler(), alg_seed()}.
+-type builtin_alg() :: exs64 | exsplus | exs1024.
+-type alg() :: builtin_alg() | term().
+-type export_state() :: {alg(), alg_seed()}.
+-export_type([builtin_alg/0, alg/0, alg_handler/0, alg_seed/0, state/0, export_state/0]).
%% =====================================================================
%% API
@@ -81,15 +83,17 @@ 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(AlgOrExpState::alg() | export_state()) -> state().
+-spec seed(AlgOrStateOrExpState::builtin_alg() | state() | export_state()) -> state().
seed(Alg) ->
seed_put(seed_s(Alg)).
--spec seed_s(AlgOrExpState::alg() | export_state()) -> state().
+-spec seed_s(AlgOrStateOrExpState::builtin_alg() | state() | export_state()) -> state().
seed_s(Alg) when is_atom(Alg) ->
seed_s(Alg, {erlang:phash2([{node(),self()}]),
erlang:system_time(),
erlang:unique_integer()});
+seed_s({AlgHandler, _Seed} = State) when is_map(AlgHandler) ->
+ State;
seed_s({Alg0, Seed}) ->
{Alg,_SeedFun} = mk_alg(Alg0),
{Alg, Seed}.
@@ -97,11 +101,11 @@ seed_s({Alg0, Seed}) ->
%% seed/2: seeds RNG with the algorithm and given values
%% and returns the NEW state.
--spec seed(Alg :: alg(), {integer(), integer(), integer()}) -> state().
+-spec seed(Alg :: builtin_alg(), {integer(), integer(), integer()}) -> state().
seed(Alg0, S0) ->
seed_put(seed_s(Alg0, S0)).
--spec seed_s(Alg :: alg(), {integer(), integer(), integer()}) -> state().
+-spec seed_s(Alg :: builtin_alg(), {integer(), integer(), integer()}) -> state().
seed_s(Alg0, S0 = {_, _, _}) ->
{Alg, Seed} = mk_alg(Alg0),
AS = Seed(S0),