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