diff options
author | Guilherme Andrade <[email protected]> | 2017-03-12 17:20:00 +0000 |
---|---|---|
committer | Guilherme Andrade <[email protected]> | 2017-03-14 23:53:39 +0000 |
commit | d07008a0562d1f83dcab144fdec9fd920deb2b96 (patch) | |
tree | 1e2aa60f891b6f2c47e85ded243d94d918bef9f4 | |
parent | 27df945c35aa541330700d75b6844de9886361b2 (diff) | |
download | otp-d07008a0562d1f83dcab144fdec9fd920deb2b96.tar.gz otp-d07008a0562d1f83dcab144fdec9fd920deb2b96.tar.bz2 otp-d07008a0562d1f83dcab144fdec9fd920deb2b96.zip |
Support generation of strong random numbers
-rw-r--r-- | lib/crypto/c_src/crypto.c | 34 | ||||
-rw-r--r-- | lib/crypto/doc/src/crypto.xml | 46 | ||||
-rw-r--r-- | lib/crypto/src/crypto.erl | 39 | ||||
-rw-r--r-- | 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 @@ </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 ------------------------------------------------ %%-------------------------------------------------------------------- |