From d07008a0562d1f83dcab144fdec9fd920deb2b96 Mon Sep 17 00:00:00 2001
From: Guilherme Andrade
Date: Sun, 12 Mar 2017 17:20:00 +0000
Subject: Support generation of strong random numbers
---
lib/crypto/c_src/crypto.c | 34 +++++++++++++++++++++++++++
lib/crypto/doc/src/crypto.xml | 46 ++++++++++++++++++++++++++++++++----
lib/crypto/src/crypto.erl | 39 ++++++++++++++++++++++++++++++-
lib/crypto/test/crypto_SUITE.erl | 50 +++++++++++++++++++++++++++++++++++++++-
4 files changed, 163 insertions(+), 6 deletions(-)
diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c
index b2f31870b9..0e17279e62 100644
--- a/lib/crypto/c_src/crypto.c
+++ b/lib/crypto/c_src/crypto.c
@@ -429,6 +429,7 @@ static ERL_NIF_TERM aes_ige_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TE
static ERL_NIF_TERM aes_ctr_stream_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM aes_ctr_stream_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM strong_rand_bytes_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM strong_rand_uniform_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM rand_uniform_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM mod_exp_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM dss_verify_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
@@ -501,6 +502,7 @@ static ErlNifFunc nif_funcs[] = {
{"aes_ctr_stream_encrypt", 2, aes_ctr_stream_encrypt},
{"aes_ctr_stream_decrypt", 2, aes_ctr_stream_encrypt},
{"strong_rand_bytes_nif", 1, strong_rand_bytes_nif},
+ {"strong_rand_uniform_nif", 2, strong_rand_uniform_nif},
{"rand_uniform_nif", 2, rand_uniform_nif},
{"mod_exp_nif", 4, mod_exp_nif},
{"dss_verify_nif", 4, dss_verify_nif},
@@ -2331,6 +2333,38 @@ static ERL_NIF_TERM bin_from_bn(ErlNifEnv* env, const BIGNUM *bn)
return term;
}
+static ERL_NIF_TERM strong_rand_uniform_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Lo,Hi) */
+ BIGNUM *bn_from = NULL, *bn_to, *bn_rand;
+ unsigned char* data;
+ unsigned dlen;
+ ERL_NIF_TERM ret;
+
+ if (!get_bn_from_mpint(env, argv[0], &bn_from)
+ || !get_bn_from_mpint(env, argv[1], &bn_rand)) {
+ if (bn_from) BN_free(bn_from);
+ return enif_make_badarg(env);
+ }
+
+ bn_to = BN_new();
+ BN_sub(bn_to, bn_rand, bn_from);
+ if (BN_rand_range(bn_rand, bn_to) != 1) {
+ ret = atom_false;
+ }
+ else {
+ BN_add(bn_rand, bn_rand, bn_from);
+ dlen = BN_num_bytes(bn_rand);
+ data = enif_make_new_binary(env, dlen+4, &ret);
+ put_int32(data, dlen);
+ BN_bn2bin(bn_rand, data+4);
+ ERL_VALGRIND_MAKE_MEM_DEFINED(data+4, dlen);
+ }
+ BN_free(bn_rand);
+ BN_free(bn_from);
+ BN_free(bn_to);
+ return ret;
+}
+
static ERL_NIF_TERM rand_uniform_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Lo,Hi) */
BIGNUM *bn_from = NULL, *bn_to, *bn_rand;
diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml
index d0deaceaaf..0697f6a202 100644
--- a/lib/crypto/doc/src/crypto.xml
+++ b/lib/crypto/doc/src/crypto.xml
@@ -658,10 +658,13 @@
Set the seed for PRNG to the given binary. This calls the
- RAND_seed function from openssl. Only use this if the system
- you are running on does not have enough "randomness" built in.
- Normally this is when
- strong_rand_bytes/1 returns low_entropy
+ RAND_seed function from openssl. Only use this if the system
+ you are running on does not have enough "randomness" built in.
+ Normally this is when either
+ strong_rand_bytes/1,
+ strong_rand_uniform/0 or
+ strong_rand_uniform/1
+ throws low_entropy
@@ -728,6 +731,41 @@
failed due to lack of secure "randomness".
+
+
+ strong_rand_uniform() -> X
+ Generate a random floating point number between 0.0 and 1.0
+
+ X = float()
+
+
+ Generates a random floating pointer number uniformly distributed
+ in the value range
+ Uses a cryptographically secure prng seeded and periodically mixed with operating system
+ provided entropy. By default this is the BN_rand_range method from OpenSSL.
+ May throw exception low_entropy in case the random generator
+ failed due to lack of secure "randomness".
+ The generated values shall present no more than 51 bits of effective entropy.
+
+
+
+
+ strong_rand_uniform(N) -> X
+ Generate a random positive integer between 1 and N
+
+ N = pos_integer()
+ X = 1..N
+
+
+ Generates a a random positive integer uniformly distributed
+ in the value range
+ Uses a cryptographically secure prng seeded and periodically mixed with operating system
+ provided entropy. By default this is the BN_rand_range method from OpenSSL.
+ May throw exception low_entropy in case the random generator
+ failed due to lack of secure "randomness".
+
+
+
stream_init(Type, Key) -> State
diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl
index 631af62615..4b386924cb 100644
--- a/lib/crypto/src/crypto.erl
+++ b/lib/crypto/src/crypto.erl
@@ -30,6 +30,8 @@
-export([hmac/3, hmac/4, hmac_init/2, hmac_update/2, hmac_final/1, hmac_final_n/2]).
-export([cmac/3, cmac/4]).
-export([exor/2, strong_rand_bytes/1, mod_pow/3]).
+-export([strong_rand_uniform/0]).
+-export([strong_rand_uniform/1]).
-export([rand_uniform/2]).
-export([block_encrypt/3, block_decrypt/3, block_encrypt/4, block_decrypt/4]).
-export([next_iv/2, next_iv/3]).
@@ -283,9 +285,11 @@ stream_decrypt(State, Data0) ->
stream_crypt(fun do_stream_decrypt/2, State, Data, erlang:byte_size(Data), MaxByts, []).
%%
-%% RAND - pseudo random numbers using RN_ functions in crypto lib
+%% RAND - pseudo random numbers using RN_ and BN_ functions in crypto lib
%%
-spec strong_rand_bytes(non_neg_integer()) -> binary().
+-spec strong_rand_uniform() -> float().
+-spec strong_rand_uniform(pos_integer()) -> pos_integer().
-spec rand_uniform(crypto_integer(), crypto_integer()) ->
crypto_integer().
@@ -297,6 +301,38 @@ strong_rand_bytes(Bytes) ->
strong_rand_bytes_nif(_Bytes) -> ?nif_stub.
+strong_rand_uniform() ->
+ Sign = 0, % positive
+ Exponent = 1023, % on the interval [1.0, 2.0[
+ Fraction = strong_rand_uniform(1, 1 bsl 52), % the whole interval above (except 1.0)
+ <> = <>,
+ Value - 1.0.
+
+strong_rand_uniform(N) when is_integer(N), N >= 1 ->
+ 1 + strong_rand_uniform(0, N).
+
+strong_rand_uniform(From, To) when is_binary(From), is_binary(To) ->
+ case strong_rand_uniform_nif(From,To) of
+ false ->
+ erlang:error(low_entropy);
+ <> when MSB > 127 ->
+ <<(Len + 1):32/integer, 0, MSB, Rest/binary>>;
+ Whatever ->
+ Whatever
+ end;
+strong_rand_uniform(From, To) when is_integer(From), is_integer(To), From < To ->
+ BinFrom = mpint(From),
+ BinTo = mpint(To),
+ case strong_rand_uniform(BinFrom, BinTo) of
+ Result when is_binary(Result) ->
+ erlint(Result);
+ Other ->
+ Other
+ end.
+
+strong_rand_uniform_nif(_From, _To) -> ?nif_stub.
+
+
rand_uniform(From,To) when is_binary(From), is_binary(To) ->
case rand_uniform_nif(From,To) of
<> when MSB > 127 ->
@@ -325,6 +361,7 @@ rand_uniform_pos(_,_) ->
rand_uniform_nif(_From,_To) -> ?nif_stub.
+
-spec rand_seed(binary()) -> ok.
rand_seed(Seed) ->
rand_seed_nif(Seed).
diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl
index 1d7037d003..6e3a3879c4 100644
--- a/lib/crypto/test/crypto_SUITE.erl
+++ b/lib/crypto/test/crypto_SUITE.erl
@@ -36,7 +36,9 @@ all() ->
{group, non_fips},
mod_pow,
exor,
- rand_uniform
+ rand_uniform,
+ strong_rand_uniform_float,
+ strong_rand_uniform_integer
].
groups() ->
@@ -485,6 +487,44 @@ rand_uniform(Config) when is_list(Config) ->
rand_uniform_aux_test(10),
10 = byte_size(crypto:strong_rand_bytes(10)).
+%%--------------------------------------------------------------------
+strong_rand_uniform_float() ->
+ [{doc, "strong_rand_uniform float testing"}].
+strong_rand_uniform_float(Config) when is_list(Config) ->
+ Samples = [crypto:strong_rand_uniform() || _ <- lists:seq(1, 10000)],
+ allmap(
+ fun (V) ->
+ (V >= 0.0 andalso V < 1.0)
+ orelse {false, ct:fail({"Not in interval", V, 0.0, 1.0})}
+ end,
+ Samples).
+
+strong_rand_uniform_integer() ->
+ [{doc, "strong_rand_uniform integer testing"}].
+strong_rand_uniform_integer(Config) when is_list(Config) ->
+ MaxCeiling = 1 bsl 32,
+ Ceilings = [1 | % edge case where the ceiling equals the floor
+ [crypto:strong_rand_uniform(MaxCeiling)
+ || _ <- lists:seq(1, 99)]],
+
+ allmap(
+ fun (Ceiling) ->
+ case Ceiling >= 1 andalso Ceiling =< MaxCeiling of
+ false ->
+ {false, ct:fail({"Ceiling not in interval", Ceiling, 1, MaxCeiling})};
+ true ->
+ Samples = [crypto:strong_rand_uniform(Ceiling)
+ || _ <- lists:seq(1, 100)],
+ allmap(
+ fun (V) ->
+ (V >= 1 andalso V =< Ceiling)
+ orelse {false, ct:fail({"Sample not in interval", V, 1, Ceiling})}
+ end,
+ Samples)
+ end
+ end,
+ Ceilings).
+
%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
@@ -951,6 +991,14 @@ crypto_rand_uniform(L,H) ->
ct:fail({"Not in interval", R1, L, H})
end.
+allmap(_Fun, []) ->
+ true;
+allmap(Fun, [H|T]) ->
+ case Fun(H) of
+ true -> allmap(Fun, T);
+ {false, Result} -> Result
+ end.
+
%%--------------------------------------------------------------------
%% Test data ------------------------------------------------
%%--------------------------------------------------------------------
--
cgit v1.2.3
From e50f63fbb2c974b4b8ad50095ca0b16a846fc161 Mon Sep 17 00:00:00 2001
From: Guilherme Andrade
Date: Sat, 18 Mar 2017 12:57:19 +0000
Subject: Restyle crypto strong numeric generators for usage in rand
---
lib/crypto/c_src/crypto.c | 33 +++++++++----------------
lib/crypto/doc/src/crypto.xml | 28 +++++++++++-----------
lib/crypto/src/crypto.erl | 52 +++++++++++++++++-----------------------
lib/crypto/test/crypto_SUITE.erl | 46 +++++++++++++++++------------------
4 files changed, 70 insertions(+), 89 deletions(-)
diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c
index 0e17279e62..b8ef08410c 100644
--- a/lib/crypto/c_src/crypto.c
+++ b/lib/crypto/c_src/crypto.c
@@ -429,7 +429,7 @@ static ERL_NIF_TERM aes_ige_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TE
static ERL_NIF_TERM aes_ctr_stream_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM aes_ctr_stream_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM strong_rand_bytes_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
-static ERL_NIF_TERM strong_rand_uniform_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM strong_rand_range_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM rand_uniform_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM mod_exp_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM dss_verify_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
@@ -502,7 +502,7 @@ static ErlNifFunc nif_funcs[] = {
{"aes_ctr_stream_encrypt", 2, aes_ctr_stream_encrypt},
{"aes_ctr_stream_decrypt", 2, aes_ctr_stream_encrypt},
{"strong_rand_bytes_nif", 1, strong_rand_bytes_nif},
- {"strong_rand_uniform_nif", 2, strong_rand_uniform_nif},
+ {"strong_rand_range_nif", 1, strong_rand_range_nif},
{"rand_uniform_nif", 2, rand_uniform_nif},
{"mod_exp_nif", 4, mod_exp_nif},
{"dss_verify_nif", 4, dss_verify_nif},
@@ -2333,35 +2333,24 @@ static ERL_NIF_TERM bin_from_bn(ErlNifEnv* env, const BIGNUM *bn)
return term;
}
-static ERL_NIF_TERM strong_rand_uniform_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
-{/* (Lo,Hi) */
- BIGNUM *bn_from = NULL, *bn_to, *bn_rand;
- unsigned char* data;
- unsigned dlen;
+static ERL_NIF_TERM strong_rand_range_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Range) */
+ BIGNUM *bn_range, *bn_rand;
ERL_NIF_TERM ret;
- if (!get_bn_from_mpint(env, argv[0], &bn_from)
- || !get_bn_from_mpint(env, argv[1], &bn_rand)) {
- if (bn_from) BN_free(bn_from);
- return enif_make_badarg(env);
+ if(!get_bn_from_bin(env, argv[0], &bn_range)) {
+ return enif_make_badarg(env);
}
- bn_to = BN_new();
- BN_sub(bn_to, bn_rand, bn_from);
- if (BN_rand_range(bn_rand, bn_to) != 1) {
+ bn_rand = BN_new();
+ if (BN_rand_range(bn_rand, bn_range) != 1) {
ret = atom_false;
}
else {
- BN_add(bn_rand, bn_rand, bn_from);
- dlen = BN_num_bytes(bn_rand);
- data = enif_make_new_binary(env, dlen+4, &ret);
- put_int32(data, dlen);
- BN_bn2bin(bn_rand, data+4);
- ERL_VALGRIND_MAKE_MEM_DEFINED(data+4, dlen);
+ ret = bin_from_bn(env, bn_rand);
}
BN_free(bn_rand);
- BN_free(bn_from);
- BN_free(bn_to);
+ BN_free(bn_range);
return ret;
}
diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml
index 0697f6a202..dc725a41be 100644
--- a/lib/crypto/doc/src/crypto.xml
+++ b/lib/crypto/doc/src/crypto.xml
@@ -662,8 +662,8 @@
you are running on does not have enough "randomness" built in.
Normally this is when either
strong_rand_bytes/1,
- strong_rand_uniform/0 or
- strong_rand_uniform/1
+ strong_rand_range/1 or
+ strong_rand_float/0
throws low_entropy
@@ -733,36 +733,36 @@
- strong_rand_uniform() -> X
- Generate a random floating point number between 0.0 and 1.0
+ strong_rand_range(N) -> binary()
+ Generate a random non-negative integer between 0 and N
- X = float()
+ N = pos_integer() | binary()
- Generates a random floating pointer number uniformly distributed
- in the value range
+
Generates a random non-negative integer uniformly distributed
+ in the value range
Uses a cryptographically secure prng seeded and periodically mixed with operating system
provided entropy. By default this is the BN_rand_range method from OpenSSL.
+ Returns binary representation.
May throw exception low_entropy in case the random generator
failed due to lack of secure "randomness".
- The generated values shall present no more than 51 bits of effective entropy.
- strong_rand_uniform(N) -> X
- Generate a random positive integer between 1 and N
+ strong_rand_float() -> X
+ Generate a random floating point number between 0.0 and 1.0
- N = pos_integer()
- X = 1..N
+ X = float()
- Generates a a random positive integer uniformly distributed
- in the value range
+
Generates a random floating pointer number uniformly distributed
+ in the value range
Uses a cryptographically secure prng seeded and periodically mixed with operating system
provided entropy. By default this is the BN_rand_range method from OpenSSL.
May throw exception low_entropy in case the random generator
failed due to lack of secure "randomness".
+ The generated values shall present no more than 51 bits of effective entropy.
diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl
index 4b386924cb..2c3208a3d5 100644
--- a/lib/crypto/src/crypto.erl
+++ b/lib/crypto/src/crypto.erl
@@ -30,8 +30,8 @@
-export([hmac/3, hmac/4, hmac_init/2, hmac_update/2, hmac_final/1, hmac_final_n/2]).
-export([cmac/3, cmac/4]).
-export([exor/2, strong_rand_bytes/1, mod_pow/3]).
--export([strong_rand_uniform/0]).
--export([strong_rand_uniform/1]).
+-export([strong_rand_range/1]).
+-export([strong_rand_float/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]).
@@ -288,8 +288,8 @@ stream_decrypt(State, Data0) ->
%% RAND - pseudo random numbers using RN_ and BN_ functions in crypto lib
%%
-spec strong_rand_bytes(non_neg_integer()) -> binary().
--spec strong_rand_uniform() -> float().
--spec strong_rand_uniform(pos_integer()) -> pos_integer().
+-spec strong_rand_range(pos_integer() | binary()) -> binary().
+-spec strong_rand_float() -> float().
-spec rand_uniform(crypto_integer(), crypto_integer()) ->
crypto_integer().
@@ -301,36 +301,28 @@ strong_rand_bytes(Bytes) ->
strong_rand_bytes_nif(_Bytes) -> ?nif_stub.
-strong_rand_uniform() ->
- Sign = 0, % positive
- Exponent = 1023, % on the interval [1.0, 2.0[
- Fraction = strong_rand_uniform(1, 1 bsl 52), % the whole interval above (except 1.0)
- <> = <>,
- Value - 1.0.
-
-strong_rand_uniform(N) when is_integer(N), N >= 1 ->
- 1 + strong_rand_uniform(0, N).
-
-strong_rand_uniform(From, To) when is_binary(From), is_binary(To) ->
- case strong_rand_uniform_nif(From,To) of
+strong_rand_range(Range) when is_integer(Range), Range > 0 ->
+ BinRange = int_to_bin(Range),
+ strong_rand_range(BinRange);
+strong_rand_range(BinRange) when is_binary(BinRange) ->
+ case strong_rand_range_nif(BinRange) of
false ->
erlang:error(low_entropy);
- <> when MSB > 127 ->
- <<(Len + 1):32/integer, 0, MSB, Rest/binary>>;
- Whatever ->
- Whatever
- end;
-strong_rand_uniform(From, To) when is_integer(From), is_integer(To), From < To ->
- BinFrom = mpint(From),
- BinTo = mpint(To),
- case strong_rand_uniform(BinFrom, BinTo) of
- Result when is_binary(Result) ->
- erlint(Result);
- Other ->
- Other
+ <> ->
+ BinResult
end.
-strong_rand_uniform_nif(_From, _To) -> ?nif_stub.
+strong_rand_range_nif(_BinRange) -> ?nif_stub.
+
+
+strong_rand_float() ->
+ % This could be optimized by having its own NIF
+ Sign = 0, % positive
+ Exponent = 1023, % on the interval [1.0, 2.0[
+ BinFraction = strong_rand_range(1 bsl 52), % the whole interval above
+ Fraction = bin_to_int(BinFraction),
+ <> = <>,
+ Value - 1.0.
rand_uniform(From,To) when is_binary(From), is_binary(To) ->
diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl
index 6e3a3879c4..0d80786fbc 100644
--- a/lib/crypto/test/crypto_SUITE.erl
+++ b/lib/crypto/test/crypto_SUITE.erl
@@ -37,8 +37,8 @@ all() ->
mod_pow,
exor,
rand_uniform,
- strong_rand_uniform_float,
- strong_rand_uniform_integer
+ strong_rand_range,
+ strong_rand_float
].
groups() ->
@@ -488,43 +488,43 @@ rand_uniform(Config) when is_list(Config) ->
10 = byte_size(crypto:strong_rand_bytes(10)).
%%--------------------------------------------------------------------
-strong_rand_uniform_float() ->
- [{doc, "strong_rand_uniform float testing"}].
-strong_rand_uniform_float(Config) when is_list(Config) ->
- Samples = [crypto:strong_rand_uniform() || _ <- lists:seq(1, 10000)],
- allmap(
- fun (V) ->
- (V >= 0.0 andalso V < 1.0)
- orelse {false, ct:fail({"Not in interval", V, 0.0, 1.0})}
- end,
- Samples).
-
-strong_rand_uniform_integer() ->
- [{doc, "strong_rand_uniform integer testing"}].
-strong_rand_uniform_integer(Config) when is_list(Config) ->
+strong_rand_range() ->
+ [{doc, "strong_rand_range testing"}].
+strong_rand_range(Config) when is_list(Config) ->
MaxCeiling = 1 bsl 32,
- Ceilings = [1 | % edge case where the ceiling equals the floor
- [crypto:strong_rand_uniform(MaxCeiling)
+ Ceilings = [1 | % edge case where only 0 can be generated
+ [binary:decode_unsigned(crypto:strong_rand_range(MaxCeiling), big)
|| _ <- lists:seq(1, 99)]],
allmap(
fun (Ceiling) ->
- case Ceiling >= 1 andalso Ceiling =< MaxCeiling of
+ case Ceiling >= 0 andalso Ceiling < MaxCeiling of
false ->
- {false, ct:fail({"Ceiling not in interval", Ceiling, 1, MaxCeiling})};
+ {false, ct:fail({"Ceiling not in interval", Ceiling, 0, MaxCeiling})};
true ->
- Samples = [crypto:strong_rand_uniform(Ceiling)
+ Samples = [binary:decode_unsigned(crypto:strong_rand_range(Ceiling), big)
|| _ <- lists:seq(1, 100)],
allmap(
fun (V) ->
- (V >= 1 andalso V =< Ceiling)
- orelse {false, ct:fail({"Sample not in interval", V, 1, Ceiling})}
+ (V >= 0 andalso V < Ceiling)
+ orelse {false, ct:fail({"Sample not in interval", V, 0, Ceiling})}
end,
Samples)
end
end,
Ceilings).
+strong_rand_float() ->
+ [{doc, "strong_rand_float testing"}].
+strong_rand_float(Config) when is_list(Config) ->
+ Samples = [crypto:strong_rand_float() || _ <- lists:seq(1, 10000)],
+ allmap(
+ fun (V) ->
+ (V >= 0.0 andalso V < 1.0)
+ orelse {false, ct:fail({"Not in interval", V, 0.0, 1.0})}
+ end,
+ Samples).
+
%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
--
cgit v1.2.3
From 77039e648c8a62bfc4f0242531d5fd4874b29aad Mon Sep 17 00:00:00 2001
From: Guilherme Andrade
Date: Sat, 18 Mar 2017 17:27:35 +0000
Subject: Support cryptographically strong rand plugin
---
lib/crypto/doc/src/crypto.xml | 29 +++++++++++
lib/crypto/src/crypto.erl | 30 +++++++++++-
lib/crypto/test/crypto_SUITE.erl | 102 ++++++++++++++++++++++++++++++++++++++-
lib/stdlib/doc/src/rand.xml | 24 ++++++---
lib/stdlib/src/rand.erl | 36 ++++++++------
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
@@ -766,6 +766,35 @@
+
+ rand_seed() -> rand:state()
+ Strong random number generation plugin state>
+
+ Creates state object for random number generation,
+ in order to generate cryptographically strong random numbers
+ (based on OpenSSL's BN_rand_range),
+ and saves it on process dictionary before returning it as well.
+ See also rand:seed/1
+
+ Example
+
+crypto:rand_seed(),
+_IntegerValue = rand:uniform(42), % [1; 42]
+_FloatValue = rand:uniform(). % [0.0; 1.0]
+
+
+
+
+ rand_seed_s() -> rand:state()
+ Strong random number generation plugin state>
+
+ Creates state object for random number generation,
+ in order to generate cryptographically strongly random numbers
+ (based on OpenSSL's BN_rand_range).
+ See also rand:seed_s/1
+
+
+
stream_init(Type, Key) -> State
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
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
<> 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() ->
@@ -525,6 +527,17 @@ strong_rand_float(Config) when is_list(Config) ->
end,
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 ------------------------------------------------
%%--------------------------------------------------------------------
@@ -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,19 +120,27 @@ S0 = rand:seed_s(exsplus),
{SND0, S2} = rand:normal_s(S1),
- This random number generator is not cryptographically
- strong. If a strong cryptographic random number generator is
- needed, use one of functions in the
- crypto
- module, for example,
- crypto:strong_rand_bytes/1.
+ The builtin random number generator algorithms are not
+ cryptographically strong. If a cryptographically strong
+ random number generator is needed, use something like
+ crypto:rand_seed/0.
+
+
+
+
+
+
+
+
+
+
Algorithm-dependent state.
@@ -216,7 +224,7 @@ S0 = rand:seed_s(exsplus),
Seeds random number generation with the specifed algorithm and
- time-dependent data if AlgOrExpState is an algorithm.
+ time-dependent data if AlgOrStateOrExpState is an algorithm.
Otherwise recreates the exported seed in the process dictionary,
and returns the state. See also
export_seed/0.
@@ -237,7 +245,7 @@ S0 = rand:seed_s(exsplus),
Seed random number generator.
Seeds random number generation with the specifed algorithm and
- time-dependent data if AlgOrExpState is an algorithm.
+ time-dependent data if AlgOrStateOrExpState is an algorithm.
Otherwise recreates the exported seed and returns the state.
See also
export_seed/0.
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),
--
cgit v1.2.3
From 5eae0dacf40ec60b09f0fdf761987e39320c4db0 Mon Sep 17 00:00:00 2001
From: Guilherme Andrade
Date: Sat, 18 Mar 2017 17:32:23 +0000
Subject: No longer expose strong_rand_(range|float)
---
lib/crypto/doc/src/crypto.xml | 40 ++-------------------------------
lib/crypto/src/crypto.erl | 48 ++++++++++++++++++----------------------
lib/crypto/test/crypto_SUITE.erl | 40 ---------------------------------
3 files changed, 23 insertions(+), 105 deletions(-)
diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml
index 7a5bd62c26..36a1a2c2ee 100644
--- a/lib/crypto/doc/src/crypto.xml
+++ b/lib/crypto/doc/src/crypto.xml
@@ -660,10 +660,8 @@
Set the seed for PRNG to the given binary. This calls the
RAND_seed function from openssl. Only use this if the system
you are running on does not have enough "randomness" built in.
- Normally this is when either
- strong_rand_bytes/1,
- strong_rand_range/1 or
- strong_rand_float/0
+ Normally this is when
+ strong_rand_bytes/1
throws low_entropy
@@ -732,40 +730,6 @@
-
- strong_rand_range(N) -> binary()
- Generate a random non-negative integer between 0 and N
-
- N = pos_integer() | binary()
-
-
- Generates a random non-negative integer uniformly distributed
- in the value range
- Uses a cryptographically secure prng seeded and periodically mixed with operating system
- provided entropy. By default this is the BN_rand_range method from OpenSSL.
- Returns binary representation.
- May throw exception low_entropy in case the random generator
- failed due to lack of secure "randomness".
-
-
-
-
- strong_rand_float() -> X
- Generate a random floating point number between 0.0 and 1.0
-
- X = float()
-
-
- Generates a random floating pointer number uniformly distributed
- in the value range
- Uses a cryptographically secure prng seeded and periodically mixed with operating system
- provided entropy. By default this is the BN_rand_range method from OpenSSL.
- May throw exception low_entropy in case the random generator
- failed due to lack of secure "randomness".
- The generated values shall present no more than 51 bits of effective entropy.
-
-
-
rand_seed() -> rand:state()
Strong random number generation plugin state>
diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl
index 4ae7a9cdd6..ad9245f8f2 100644
--- a/lib/crypto/src/crypto.erl
+++ b/lib/crypto/src/crypto.erl
@@ -30,8 +30,6 @@
-export([hmac/3, hmac/4, hmac_init/2, hmac_update/2, hmac_final/1, hmac_final_n/2]).
-export([cmac/3, cmac/4]).
-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]).
@@ -290,8 +288,6 @@ stream_decrypt(State, Data0) ->
%% RAND - pseudo random numbers using RN_ and BN_ functions in crypto lib
%%
-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()) ->
@@ -305,29 +301,6 @@ strong_rand_bytes(Bytes) ->
strong_rand_bytes_nif(_Bytes) -> ?nif_stub.
-strong_rand_range(Range) when is_integer(Range), Range > 0 ->
- BinRange = int_to_bin(Range),
- strong_rand_range(BinRange);
-strong_rand_range(BinRange) when is_binary(BinRange) ->
- case strong_rand_range_nif(BinRange) of
- false ->
- erlang:error(low_entropy);
- <> ->
- BinResult
- end.
-strong_rand_range_nif(_BinRange) -> ?nif_stub.
-
-
-strong_rand_float() ->
- % This could be optimized by having its own NIF
- Sign = 0, % positive
- Exponent = 1023, % on the interval [1.0, 2.0[
- BinFraction = strong_rand_range(1 bsl 52), % the whole interval above
- Fraction = bin_to_int(BinFraction),
- <> = <>,
- Value - 1.0.
-
-
rand_seed() ->
rand:seed(rand_seed_s()).
@@ -352,6 +325,27 @@ rand_plugin_uniform(Max, State) ->
rand_plugin_jump(State) ->
State.
+strong_rand_range(Range) when is_integer(Range), Range > 0 ->
+ BinRange = int_to_bin(Range),
+ strong_rand_range(BinRange);
+strong_rand_range(BinRange) when is_binary(BinRange) ->
+ case strong_rand_range_nif(BinRange) of
+ false ->
+ erlang:error(low_entropy);
+ <> ->
+ BinResult
+ end.
+strong_rand_range_nif(_BinRange) -> ?nif_stub.
+
+strong_rand_float() ->
+ % This could be optimized by having its own NIF
+ Sign = 0, % positive
+ Exponent = 1023, % on the interval [1.0, 2.0[
+ BinFraction = strong_rand_range(1 bsl 52), % the whole interval above
+ Fraction = bin_to_int(BinFraction),
+ <> = <>,
+ Value - 1.0.
+
rand_uniform(From,To) when is_binary(From), is_binary(To) ->
case rand_uniform_nif(From,To) of
diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl
index 482a07d634..1b7456af18 100644
--- a/lib/crypto/test/crypto_SUITE.erl
+++ b/lib/crypto/test/crypto_SUITE.erl
@@ -37,8 +37,6 @@ all() ->
mod_pow,
exor,
rand_uniform,
- strong_rand_range,
- strong_rand_float,
rand_plugin,
rand_plugin_s
].
@@ -489,44 +487,6 @@ rand_uniform(Config) when is_list(Config) ->
rand_uniform_aux_test(10),
10 = byte_size(crypto:strong_rand_bytes(10)).
-%%--------------------------------------------------------------------
-strong_rand_range() ->
- [{doc, "strong_rand_range testing"}].
-strong_rand_range(Config) when is_list(Config) ->
- MaxCeiling = 1 bsl 32,
- Ceilings = [1 | % edge case where only 0 can be generated
- [binary:decode_unsigned(crypto:strong_rand_range(MaxCeiling), big)
- || _ <- lists:seq(1, 99)]],
-
- allmap(
- fun (Ceiling) ->
- case Ceiling >= 0 andalso Ceiling < MaxCeiling of
- false ->
- {false, ct:fail({"Ceiling not in interval", Ceiling, 0, MaxCeiling})};
- true ->
- Samples = [binary:decode_unsigned(crypto:strong_rand_range(Ceiling), big)
- || _ <- lists:seq(1, 100)],
- allmap(
- fun (V) ->
- (V >= 0 andalso V < Ceiling)
- orelse {false, ct:fail({"Sample not in interval", V, 0, Ceiling})}
- end,
- Samples)
- end
- end,
- Ceilings).
-
-strong_rand_float() ->
- [{doc, "strong_rand_float testing"}].
-strong_rand_float(Config) when is_list(Config) ->
- Samples = [crypto:strong_rand_float() || _ <- lists:seq(1, 10000)],
- allmap(
- fun (V) ->
- (V >= 0.0 andalso V < 1.0)
- orelse {false, ct:fail({"Not in interval", V, 0.0, 1.0})}
- end,
- Samples).
-
%%--------------------------------------------------------------------
rand_plugin() ->
[{doc, "crypto rand plugin testing (implicit state / process dictionary)"}].
--
cgit v1.2.3
From 1f236ff5a8aaedc720b45420c3d53092435ecd52 Mon Sep 17 00:00:00 2001
From: Guilherme Andrade
Date: Wed, 22 Mar 2017 21:48:54 +0000
Subject: fixup! Support cryptographically strong rand plugin
Be friendlier to Dialyzer
---
lib/crypto/doc/src/crypto.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml
index 36a1a2c2ee..3eee24769d 100644
--- a/lib/crypto/doc/src/crypto.xml
+++ b/lib/crypto/doc/src/crypto.xml
@@ -742,7 +742,7 @@
Example
-crypto:rand_seed(),
+_ = crypto:rand_seed(),
_IntegerValue = rand:uniform(42), % [1; 42]
_FloatValue = rand:uniform(). % [0.0; 1.0]
--
cgit v1.2.3
From 6f6c478401eefee1c8d7b200eb02d288739a7648 Mon Sep 17 00:00:00 2001
From: Guilherme Andrade
Date: Wed, 22 Mar 2017 21:49:38 +0000
Subject: fixup! Support cryptographically strong rand plugin
Fix documented range (interval is half-open.)
---
lib/crypto/doc/src/crypto.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml
index 3eee24769d..30835a8447 100644
--- a/lib/crypto/doc/src/crypto.xml
+++ b/lib/crypto/doc/src/crypto.xml
@@ -744,7 +744,7 @@
_ = crypto:rand_seed(),
_IntegerValue = rand:uniform(42), % [1; 42]
-_FloatValue = rand:uniform(). % [0.0; 1.0]
+_FloatValue = rand:uniform(). % [0.0; 1.0[
--
cgit v1.2.3
From 54b89c8750bed431275f1f376fb10ec7899ca335 Mon Sep 17 00:00:00 2001
From: Guilherme Andrade
Date: Wed, 22 Mar 2017 21:50:20 +0000
Subject: fixup! Support cryptographically strong rand plugin
Fix plugin alg type
---
lib/stdlib/src/rand.erl | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/stdlib/src/rand.erl b/lib/stdlib/src/rand.erl
index 3ff7aaf15f..ddd22914e2 100644
--- a/lib/stdlib/src/rand.erl
+++ b/lib/stdlib/src/rand.erl
@@ -58,7 +58,7 @@
%% Algorithm state
-type state() :: {alg_handler(), alg_seed()}.
-type builtin_alg() :: exs64 | exsplus | exs1024.
--type alg() :: builtin_alg() | term().
+-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]).
--
cgit v1.2.3
From 195edd9076c865f8af6a9aeeb02bc032b94c0fb3 Mon Sep 17 00:00:00 2001
From: Guilherme Andrade
Date: Wed, 22 Mar 2017 21:51:17 +0000
Subject: fixup! Support cryptographically strong rand plugin
Minimize use of guards.
---
lib/stdlib/src/rand.erl | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/lib/stdlib/src/rand.erl b/lib/stdlib/src/rand.erl
index ddd22914e2..60da53cd2b 100644
--- a/lib/stdlib/src/rand.erl
+++ b/lib/stdlib/src/rand.erl
@@ -88,15 +88,15 @@ seed(Alg) ->
seed_put(seed_s(Alg)).
-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}.
+ {Alg, Seed};
+seed_s(Alg) ->
+ seed_s(Alg, {erlang:phash2([{node(),self()}]),
+ erlang:system_time(),
+ erlang:unique_integer()}).
%% seed/2: seeds RNG with the algorithm and given values
%% and returns the NEW state.
--
cgit v1.2.3
From ec1e5bc7ff8101b7db4fa5d67bbbed652a9238c8 Mon Sep 17 00:00:00 2001
From: Guilherme Andrade
Date: Wed, 22 Mar 2017 21:56:28 +0000
Subject: Allow for crypto upgrades when using rand plugin
---
lib/crypto/src/crypto.erl | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl
index ad9245f8f2..777b5cd1c6 100644
--- a/lib/crypto/src/crypto.erl
+++ b/lib/crypto/src/crypto.erl
@@ -32,6 +32,10 @@
-export([exor/2, strong_rand_bytes/1, mod_pow/3]).
-export([rand_seed/0]).
-export([rand_seed_s/0]).
+-export([rand_plugin_next/1]).
+-export([rand_plugin_uniform/1]).
+-export([rand_plugin_uniform/2]).
+-export([rand_plugin_jump/1]).
-export([rand_uniform/2]).
-export([block_encrypt/3, block_decrypt/3, block_encrypt/4, block_decrypt/4]).
-export([next_iv/2, next_iv/3]).
@@ -305,12 +309,12 @@ rand_seed() ->
rand:seed(rand_seed_s()).
rand_seed_s() ->
- {#{ type => crypto,
+ {#{ type => ?MODULE,
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},
+ next => fun ?MODULE:rand_plugin_next/1,
+ uniform => fun ?MODULE:rand_plugin_uniform/1,
+ uniform_n => fun ?MODULE:rand_plugin_uniform/2,
+ jump => fun ?MODULE:rand_plugin_jump/1},
no_seed}.
rand_plugin_next(Seed) ->
--
cgit v1.2.3
From c84e541b78cb9ee63a02db2240903ddd6131793a Mon Sep 17 00:00:00 2001
From: Guilherme Andrade
Date: Wed, 22 Mar 2017 22:15:15 +0000
Subject: Attempt faster approach to strong random floats
---
lib/crypto/src/crypto.erl | 13 +++++--------
1 file changed, 5 insertions(+), 8 deletions(-)
diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl
index 777b5cd1c6..d8bc13b537 100644
--- a/lib/crypto/src/crypto.erl
+++ b/lib/crypto/src/crypto.erl
@@ -49,6 +49,9 @@
%% This should correspond to the similar macro in crypto.c
-define(MAX_BYTES_TO_NIF, 20000). %% Current value is: erlang:system_info(context_reductions) * 10
+%% Used by strong_rand_float/0
+-define(HALF_DBL_EPSILON, 1.1102230246251565e-16). % math:pow(2, -53)
+
%%-type ecdsa_digest_type() :: 'md5' | 'sha' | 'sha256' | 'sha384' | 'sha512'.
-type crypto_integer() :: binary() | integer().
%%-type ec_named_curve() :: atom().
@@ -342,14 +345,8 @@ strong_rand_range(BinRange) when is_binary(BinRange) ->
strong_rand_range_nif(_BinRange) -> ?nif_stub.
strong_rand_float() ->
- % This could be optimized by having its own NIF
- Sign = 0, % positive
- Exponent = 1023, % on the interval [1.0, 2.0[
- BinFraction = strong_rand_range(1 bsl 52), % the whole interval above
- Fraction = bin_to_int(BinFraction),
- <> = <>,
- Value - 1.0.
-
+ WholeRange = strong_rand_range(1 bsl 53),
+ ?HALF_DBL_EPSILON * bytes_to_integer(WholeRange).
rand_uniform(From,To) when is_binary(From), is_binary(To) ->
case rand_uniform_nif(From,To) of
--
cgit v1.2.3
From e1a74e3077ca870520a748f29dd7c4b9115ce090 Mon Sep 17 00:00:00 2001
From: Raimo Niskanen
Date: Mon, 3 Apr 2017 12:29:23 +0200
Subject: Clean up documentation and test cases
---
lib/crypto/doc/src/crypto.xml | 35 +++++++++++++---------
lib/stdlib/doc/src/rand.xml | 43 +++++++++++++++++++-------
lib/stdlib/src/rand.erl | 68 ++++++++++++++++++++++++++----------------
lib/stdlib/test/rand_SUITE.erl | 33 ++++++++++++++------
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 @@
rand_seed() -> rand:state()
- Strong random number generation plugin state>
-
- Creates state object for random number generation,
- in order to generate cryptographically strong random numbers
- (based on OpenSSL's BN_rand_range),
- and saves it on process dictionary before returning it as well.
- See also rand:seed/1
-
+ Strong random number generation plugin state
+
+
+ Creates state object for
+ random number generation,
+ in order to generate cryptographically strong random numbers
+ (based on OpenSSL's BN_rand_range),
+ and saves it on process dictionary before returning it as well.
+ See also
+ rand:seed/1.
+
Example
_ = crypto:rand_seed(),
@@ -750,12 +753,16 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[
rand_seed_s() -> rand:state()
- Strong random number generation plugin state>
-
- Creates state object for random number generation,
- in order to generate cryptographically strongly random numbers
- (based on OpenSSL's BN_rand_range).
- See also rand:seed_s/1
+ Strong random number generation plugin state
+
+
+ Creates state object for
+ random number generation,
+ in order to generate cryptographically strongly random numbers
+ (based on OpenSSL's BN_rand_range).
+ See also
+ rand:seed_s/1.
+
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),
-
+
+
+
+
+ Algorithm specific internal state
+
+
+
+ Algorithm specific internal state
+
+
+
+ Algorithm specific internal state
@@ -147,8 +159,11 @@ S0 = rand:seed_s(exsplus),
- Algorithm-dependent state that can be printed or saved to
- file.
+
+
+ Algorithm-dependent state that can be printed or saved to file.
+
+
@@ -223,8 +238,11 @@ S0 = rand:seed_s(exsplus),
Seed random number generator.
- Seeds random number generation with the specifed algorithm and
- time-dependent data if AlgOrStateOrExpState is an algorithm.
+
+ Seeds random number generation with the specifed algorithm and
+ time-dependent data if AlgOrStateOrExpState
+ is an algorithm.
+
Otherwise recreates the exported seed in the process dictionary,
and returns the state. See also
export_seed/0.
@@ -244,8 +262,11 @@ S0 = rand:seed_s(exsplus),
Seed random number generator.
- Seeds random number generation with the specifed algorithm and
- time-dependent data if AlgOrStateOrExpState is an algorithm.
+
+ Seeds random number generation with the specifed algorithm and
+ time-dependent data if AlgOrStateOrExpState
+ is an algorithm.
+
Otherwise recreates the exported seed and returns the state.
See also
export_seed/0.
@@ -266,7 +287,7 @@ S0 = rand:seed_s(exsplus),
Return a random float.
Returns a random float uniformly distributed in the value
- range 0.0 < X < 1.0 and
+ range 0.0 =< X < 1.0 and
updates the state in the process dictionary.
@@ -277,7 +298,7 @@ S0 = rand:seed_s(exsplus),
Returns, for a specified integer N >= 1,
a random integer uniformly distributed in the value range
- 1 <= X <= N and
+ 1 =< X =< N and
updates the state in the process dictionary.
@@ -287,7 +308,7 @@ S0 = rand:seed_s(exsplus),
Return a random float.
Returns, for a specified state, random float
- uniformly distributed in the value range 0.0 <
+ uniformly distributed in the value range 0.0 =<
X < 1.0 and a new state.
@@ -298,7 +319,7 @@ S0 = rand:seed_s(exsplus),
Returns, for a specified integer N >= 1
and a state, a random integer uniformly distributed in the value
- range 1 <= X <= N and a
+ range 1 =< X =< N and a
new state.
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],
--
cgit v1.2.3