diff options
Diffstat (limited to 'lib/crypto')
-rw-r--r-- | lib/crypto/doc/src/crypto.xml | 29 | ||||
-rw-r--r-- | lib/crypto/src/crypto.erl | 30 | ||||
-rw-r--r-- | lib/crypto/test/crypto_SUITE.erl | 102 |
3 files changed, 159 insertions, 2 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 ------------------------------------------------ %%-------------------------------------------------------------------- |