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(-)
(limited to 'lib/crypto')
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