aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorGuilherme Andrade <[email protected]>2017-03-12 17:20:00 +0000
committerGuilherme Andrade <[email protected]>2017-03-14 23:53:39 +0000
commitd07008a0562d1f83dcab144fdec9fd920deb2b96 (patch)
tree1e2aa60f891b6f2c47e85ded243d94d918bef9f4 /lib
parent27df945c35aa541330700d75b6844de9886361b2 (diff)
downloadotp-d07008a0562d1f83dcab144fdec9fd920deb2b96.tar.gz
otp-d07008a0562d1f83dcab144fdec9fd920deb2b96.tar.bz2
otp-d07008a0562d1f83dcab144fdec9fd920deb2b96.zip
Support generation of strong random numbers
Diffstat (limited to 'lib')
-rw-r--r--lib/crypto/c_src/crypto.c34
-rw-r--r--lib/crypto/doc/src/crypto.xml46
-rw-r--r--lib/crypto/src/crypto.erl39
-rw-r--r--lib/crypto/test/crypto_SUITE.erl50
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 @@
</type>
<desc>
<p>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 <seealso marker="#strong_rand_bytes/1">
- strong_rand_bytes/1</seealso> returns <c>low_entropy</c></p>
+ 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
+ <seealso marker="#strong_rand_bytes/1">strong_rand_bytes/1</seealso>,
+ <seealso marker="#strong_rand_uniform/0">strong_rand_uniform/0</seealso> or
+ <seealso marker="#strong_rand_uniform/1">strong_rand_uniform/1</seealso>
+ throws <c>low_entropy</c></p>
</desc>
</func>
@@ -728,6 +731,41 @@
failed due to lack of secure "randomness".</p>
</desc>
</func>
+
+ <func>
+ <name>strong_rand_uniform() -> X</name>
+ <fsummary>Generate a random floating point number between 0.0 and 1.0</fsummary>
+ <type>
+ <v>X = float()</v>
+ </type>
+ <desc>
+ <p>Generates a random floating pointer number uniformly distributed
+ in the value range <c><![CDATA[X, 0.0 < X < 1.0.]]></c>
+ Uses a cryptographically secure prng seeded and periodically mixed with operating system
+ provided entropy. By default this is the <c>BN_rand_range</c> method from OpenSSL.</p>
+ <p>May throw exception <c>low_entropy</c> in case the random generator
+ failed due to lack of secure "randomness".</p>
+ <note><p>The generated values shall present no more than 51 bits of effective entropy.</p></note>
+ </desc>
+ </func>
+
+ <func>
+ <name>strong_rand_uniform(N) -> X</name>
+ <fsummary>Generate a random positive integer between 1 and N</fsummary>
+ <type>
+ <v>N = pos_integer()</v>
+ <v>X = 1..N</v>
+ </type>
+ <desc>
+ <p>Generates a a random positive integer uniformly distributed
+ in the value range <c><![CDATA[X, 1 =< X =< N.]]></c>
+ Uses a cryptographically secure prng seeded and periodically mixed with operating system
+ provided entropy. By default this is the <c>BN_rand_range</c> method from OpenSSL.</p>
+ <p>May throw exception <c>low_entropy</c> in case the random generator
+ failed due to lack of secure "randomness".</p>
+ </desc>
+ </func>
+
<func>
<name>stream_init(Type, Key) -> State</name>
<fsummary></fsummary>
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:64/big-float>> = <<Sign:1, Exponent:11, Fraction:52>>,
+ 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);
+ <<Len:32/integer, MSB, Rest/binary>> 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
<<Len:32/integer, MSB, Rest/binary>> 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() ->
@@ -486,6 +488,44 @@ 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) ->
+ 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 ------------------------------------------------
%%--------------------------------------------------------------------
hash(_, [], []) ->
@@ -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 ------------------------------------------------
%%--------------------------------------------------------------------