diff options
-rw-r--r-- | lib/crypto/c_src/crypto.c | 33 | ||||
-rw-r--r-- | lib/crypto/doc/src/crypto.xml | 28 | ||||
-rw-r--r-- | lib/crypto/src/crypto.erl | 52 | ||||
-rw-r--r-- | 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 <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> + <seealso marker="#strong_rand_range/1">strong_rand_range/1</seealso> or + <seealso marker="#strong_rand_float/0">strong_rand_float/0</seealso> throws <c>low_entropy</c></p> </desc> </func> @@ -733,36 +733,36 @@ </func> <func> - <name>strong_rand_uniform() -> X</name> - <fsummary>Generate a random floating point number between 0.0 and 1.0</fsummary> + <name>strong_rand_range(N) -> binary()</name> + <fsummary>Generate a random non-negative integer between 0 and N</fsummary> <type> - <v>X = float()</v> + <v>N = pos_integer() | binary()</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> + <p>Generates a random non-negative integer uniformly distributed + in the value range <c><![CDATA[X, 0 =< 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>Returns binary representation.</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> + <name>strong_rand_float() -> X</name> + <fsummary>Generate a random floating point number between 0.0 and 1.0</fsummary> <type> - <v>N = pos_integer()</v> - <v>X = 1..N</v> + <v>X = float()</v> </type> <desc> - <p>Generates a a random positive integer uniformly distributed - in the value range <c><![CDATA[X, 1 =< X =< N.]]></c> + <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> 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: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 +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); - <<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 + <<BinResult/binary>> -> + 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:64/big-float>> = <<Sign:1, Exponent:11, Fraction:52>>, + 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 ------------------------------------------------ %%-------------------------------------------------------------------- |