From 2181428bcad2abb3265e796e0b84b10830c74a76 Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Wed, 6 Sep 2017 21:56:22 +0200 Subject: Implement crypto rand cache --- lib/crypto/src/crypto.erl | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) (limited to 'lib/crypto') diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl index d111525214..69c98c651f 100644 --- a/lib/crypto/src/crypto.erl +++ b/lib/crypto/src/crypto.erl @@ -30,11 +30,12 @@ -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([rand_seed/0]). --export([rand_seed_s/0]). +-export([rand_seed/0, rand_seed_alg/1]). +-export([rand_seed_s/0, rand_seed_alg_s/1]). -export([rand_plugin_next/1]). -export([rand_plugin_uniform/1]). -export([rand_plugin_uniform/2]). +-export([rand_cache_plugin_next/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]). @@ -299,6 +300,8 @@ stream_decrypt(State, Data0) -> -spec strong_rand_bytes(non_neg_integer()) -> binary(). -spec rand_seed() -> rand:state(). -spec rand_seed_s() -> rand:state(). +-spec rand_seed_alg(Alg :: atom()) -> rand:state(). +-spec rand_seed_alg_s(Alg :: atom()) -> rand:state(). -spec rand_uniform(crypto_integer(), crypto_integer()) -> crypto_integer(). @@ -314,12 +317,24 @@ rand_seed() -> rand:seed(rand_seed_s()). rand_seed_s() -> + rand_seed_alg_s(?MODULE). + +rand_seed_alg(Alg) -> + rand:seed(rand_seed_alg_s(Alg)). + +-define(CRYPTO_CACHE_BITS, 56). +rand_seed_alg_s(?MODULE) -> {#{ type => ?MODULE, bits => 64, next => fun ?MODULE:rand_plugin_next/1, uniform => fun ?MODULE:rand_plugin_uniform/1, uniform_n => fun ?MODULE:rand_plugin_uniform/2}, - no_seed}. + no_seed}; +rand_seed_alg_s(crypto_cache) -> + {#{ type => crypto_cache, + bits => ?CRYPTO_CACHE_BITS, + next => fun ?MODULE:rand_cache_plugin_next/1}, + <<>>}. rand_plugin_next(Seed) -> {bytes_to_integer(strong_rand_range(1 bsl 64)), Seed}. @@ -330,6 +345,11 @@ rand_plugin_uniform(State) -> rand_plugin_uniform(Max, State) -> {bytes_to_integer(strong_rand_range(Max)) + 1, State}. +rand_cache_plugin_next(<<>>) -> + rand_cache_plugin_next( + strong_rand_bytes(?CRYPTO_CACHE_BITS * 16)); % Cache 16 * 8 words +rand_cache_plugin_next(<>) -> + {I, Cache}. strong_rand_range(Range) when is_integer(Range), Range > 0 -> BinRange = int_to_bin(Range), @@ -377,7 +397,7 @@ rand_uniform_nif(_From,_To) -> ?nif_stub. -spec rand_seed(binary()) -> ok. -rand_seed(Seed) -> +rand_seed(Seed) when is_binary(Seed) -> rand_seed_nif(Seed). rand_seed_nif(_Seed) -> ?nif_stub. -- cgit v1.2.3 From 29222f06f38e321e5a2ac8dae67ced92b6544bde Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Tue, 19 Sep 2017 15:33:53 +0200 Subject: Document crypto rand cache --- lib/crypto/doc/src/crypto.xml | 111 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 109 insertions(+), 2 deletions(-) (limited to 'lib/crypto') diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml index 89ef529c5d..5afab632cd 100644 --- a/lib/crypto/doc/src/crypto.xml +++ b/lib/crypto/doc/src/crypto.xml @@ -739,9 +739,16 @@ random number generation, in order to generate cryptographically strong random numbers (based on OpenSSL's BN_rand_range), - and saves it on process dictionary before returning it as well. + and saves it in the process dictionary before returning it as well. See also - rand:seed/1. + rand:seed/1 and + rand_seed_s/0. +

+

+ When using the state object from this function the + rand functions using it + may throw exception low_entropy in case the random generator + failed due to lack of secure "randomness".

Example

@@ -763,6 +770,106 @@ _FloatValue = rand:uniform().     % [0.0; 1.0[
See also rand:seed_s/1.

+

+ When using the state object from this function the + rand functions using it + may throw exception low_entropy in case the random generator + failed due to lack of secure "randomness". +

+ +

+ The state returned from this function can not be used + to get a reproducable random sequence as from + the other + rand + functions, + since reproducability does not match cryptographically safe. +

+

+ The only supported usage is to generate one distinct + random sequence from this start state. +

+
+ + + + + rand_seed_alg(Alg) -> rand:state() + Strong random number generation plugin state + + Alg = crypto | crypto_cache + + +

+ Creates state object for + random number generation, + in order to generate cryptographically strong random numbers. + See also + rand:seed/1 and + rand_seed_alg_s/1. +

+

+ When using the state object from this function the + rand functions using it + may throw exception low_entropy in case the random generator + failed due to lack of secure "randomness". +

+

Example

+
+_ = crypto:rand_seed_alg(crypto_cache),
+_IntegerValue = rand:uniform(42), % [1; 42]
+_FloatValue = rand:uniform().     % [0.0; 1.0[
+
+
+ + + rand_seed_alg_s(Alg) -> rand:state() + Strong random number generation plugin state + + Alg = crypto | crypto_cache + + +

+ Creates state object for + random number generation, + in order to generate cryptographically strongly random numbers. + See also + rand:seed_s/1. +

+

+ If Alg is crypto this function behaves exactly like + rand_seed_s/0. +

+

+ If Alg is crypto_cache this function + fetches random data with OpenSSL's RAND_bytes + and caches it for speed using an internal word size + of 56 bits that makes calculations fast on 64 bit machines. +

+

+ When using the state object from this function the + rand functions using it + may throw exception low_entropy in case the random generator + failed due to lack of secure "randomness". +

+ +

+ The state returned from this function can not be used + to get a reproducable random sequence as from + the other + rand + functions, + since reproducability does not match cryptographically safe. +

+

+ In fact since random data is cached some numbers may + get reproduced if you try, but this is unpredictable. +

+

+ The only supported usage is to generate one distinct + random sequence from this start state. +

+
-- cgit v1.2.3 From 7710c0d681a4b5f17253945dde0726de0e27cdcf Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Thu, 28 Sep 2017 14:48:44 +0200 Subject: Make cache size configurable --- lib/crypto/doc/src/crypto.xml | 14 ++++++++++++++ lib/crypto/doc/src/crypto_app.xml | 18 ++++++++++++++++++ lib/crypto/src/crypto.app.src | 2 +- lib/crypto/src/crypto.erl | 38 +++++++++++++++++++++++++++++--------- 4 files changed, 62 insertions(+), 10 deletions(-) (limited to 'lib/crypto') diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml index 5afab632cd..c32e3430ab 100644 --- a/lib/crypto/doc/src/crypto.xml +++ b/lib/crypto/doc/src/crypto.xml @@ -800,6 +800,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[ Alg = crypto | crypto_cache +

Creates state object for random number generation, @@ -814,6 +815,12 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[ may throw exception low_entropy in case the random generator failed due to lack of secure "randomness".

+

+ The cache size can be changed from its default value using the + + crypto app's + configuration parameter rand_cache_size. +

Example

 _ = crypto:rand_seed_alg(crypto_cache),
@@ -829,6 +836,7 @@ _FloatValue = rand:uniform().     % [0.0; 1.0[
Alg = crypto | crypto_cache +

Creates state object for random number generation, @@ -852,6 +860,12 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[ may throw exception low_entropy in case the random generator failed due to lack of secure "randomness".

+

+ The cache size can be changed from its default value using the + + crypto app's + configuration parameter rand_cache_size. +

The state returned from this function can not be used diff --git a/lib/crypto/doc/src/crypto_app.xml b/lib/crypto/doc/src/crypto_app.xml index ba22557480..8296b1bc77 100644 --- a/lib/crypto/doc/src/crypto_app.xml +++ b/lib/crypto/doc/src/crypto_app.xml @@ -68,6 +68,24 @@ thus the crypto module will fail to load. This mechanism prevents the accidental use of non-validated algorithms.

+ rand_cache_size = integer() + +

+ Sets the cache size in bytes to use by + + crypto:rand_seed_alg(crypto_cache) + and + + crypto:rand_seed_alg_s(crypto_cache) + . + This parameter is read when a seed function is called, + and then kept in generators state object. It has a rather + small default value that causes reads of strong random bytes + about once per hundred calls for a random value. + The set value is rounded up to an integral number of words + of the size these seed functions use. +

+
diff --git a/lib/crypto/src/crypto.app.src b/lib/crypto/src/crypto.app.src index 1d3f35e465..492aa10e51 100644 --- a/lib/crypto/src/crypto.app.src +++ b/lib/crypto/src/crypto.app.src @@ -24,7 +24,7 @@ crypto_ec_curves]}, {registered, []}, {applications, [kernel, stdlib]}, - {env, [{fips_mode, false}]}, + {env, [{fips_mode, false}, {rand_cache_size, 896}]}, {runtime_dependencies, ["erts-9.0","stdlib-3.4","kernel-5.3"]}]}. diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl index 69c98c651f..69ca884471 100644 --- a/lib/crypto/src/crypto.erl +++ b/lib/crypto/src/crypto.erl @@ -297,11 +297,17 @@ stream_decrypt(State, Data0) -> %% %% RAND - pseudo random numbers using RN_ and BN_ functions in crypto lib %% +-type rand_cache_seed() :: + nonempty_improper_list(non_neg_integer(), binary()). -spec strong_rand_bytes(non_neg_integer()) -> binary(). -spec rand_seed() -> rand:state(). -spec rand_seed_s() -> rand:state(). --spec rand_seed_alg(Alg :: atom()) -> rand:state(). --spec rand_seed_alg_s(Alg :: atom()) -> rand:state(). +-spec rand_seed_alg(Alg :: atom()) -> + {rand:alg_handler(), + atom() | rand_cache_seed()}. +-spec rand_seed_alg_s(Alg :: atom()) -> + {rand:alg_handler(), + atom() | rand_cache_seed()}. -spec rand_uniform(crypto_integer(), crypto_integer()) -> crypto_integer(). @@ -319,10 +325,12 @@ rand_seed() -> rand_seed_s() -> rand_seed_alg_s(?MODULE). +-dialyzer({no_improper_lists, rand_seed_alg/1}). rand_seed_alg(Alg) -> rand:seed(rand_seed_alg_s(Alg)). - + -define(CRYPTO_CACHE_BITS, 56). +-dialyzer({no_improper_lists, rand_seed_alg_s/1}). rand_seed_alg_s(?MODULE) -> {#{ type => ?MODULE, bits => 64, @@ -331,10 +339,22 @@ rand_seed_alg_s(?MODULE) -> uniform_n => fun ?MODULE:rand_plugin_uniform/2}, no_seed}; rand_seed_alg_s(crypto_cache) -> + EnvCacheSize = + application:get_env( + crypto, rand_cache_size, + ?CRYPTO_CACHE_BITS * 16), % Cache 16 * 8 words + Bytes = (?CRYPTO_CACHE_BITS + 7) div 8, + CacheSize = + case ((EnvCacheSize + (Bytes - 1)) div Bytes) * Bytes of + Sz when is_integer(Sz), Bytes =< Sz -> + Sz; + _ -> + Bytes + end, {#{ type => crypto_cache, bits => ?CRYPTO_CACHE_BITS, next => fun ?MODULE:rand_cache_plugin_next/1}, - <<>>}. + [CacheSize|<<>>]}. rand_plugin_next(Seed) -> {bytes_to_integer(strong_rand_range(1 bsl 64)), Seed}. @@ -345,11 +365,11 @@ rand_plugin_uniform(State) -> rand_plugin_uniform(Max, State) -> {bytes_to_integer(strong_rand_range(Max)) + 1, State}. -rand_cache_plugin_next(<<>>) -> - rand_cache_plugin_next( - strong_rand_bytes(?CRYPTO_CACHE_BITS * 16)); % Cache 16 * 8 words -rand_cache_plugin_next(<>) -> - {I, Cache}. +-dialyzer({no_improper_lists, rand_cache_plugin_next/1}). +rand_cache_plugin_next([CacheSize|<<>>]) -> + rand_cache_plugin_next([CacheSize|strong_rand_bytes(CacheSize)]); +rand_cache_plugin_next([CacheSize|<>]) -> + {I, [CacheSize|Cache]}. strong_rand_range(Range) when is_integer(Range), Range > 0 -> BinRange = int_to_bin(Range), -- cgit v1.2.3 From a89405725c623d489643c2bd4a07ce626dccdcc3 Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Mon, 2 Oct 2017 16:32:14 +0200 Subject: Future proof cache word size --- lib/crypto/src/crypto.erl | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) (limited to 'lib/crypto') diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl index 69ca884471..2ca5e4db9e 100644 --- a/lib/crypto/src/crypto.erl +++ b/lib/crypto/src/crypto.erl @@ -325,12 +325,10 @@ rand_seed() -> rand_seed_s() -> rand_seed_alg_s(?MODULE). --dialyzer({no_improper_lists, rand_seed_alg/1}). rand_seed_alg(Alg) -> rand:seed(rand_seed_alg_s(Alg)). -define(CRYPTO_CACHE_BITS, 56). --dialyzer({no_improper_lists, rand_seed_alg_s/1}). rand_seed_alg_s(?MODULE) -> {#{ type => ?MODULE, bits => 64, @@ -339,11 +337,11 @@ rand_seed_alg_s(?MODULE) -> uniform_n => fun ?MODULE:rand_plugin_uniform/2}, no_seed}; rand_seed_alg_s(crypto_cache) -> + CacheBits = ?CRYPTO_CACHE_BITS, EnvCacheSize = application:get_env( - crypto, rand_cache_size, - ?CRYPTO_CACHE_BITS * 16), % Cache 16 * 8 words - Bytes = (?CRYPTO_CACHE_BITS + 7) div 8, + crypto, rand_cache_size, CacheBits * 16), % Cache 16 * 8 words + Bytes = (CacheBits + 7) div 8, CacheSize = case ((EnvCacheSize + (Bytes - 1)) div Bytes) * Bytes of Sz when is_integer(Sz), Bytes =< Sz -> @@ -352,9 +350,9 @@ rand_seed_alg_s(crypto_cache) -> Bytes end, {#{ type => crypto_cache, - bits => ?CRYPTO_CACHE_BITS, + bits => CacheBits, next => fun ?MODULE:rand_cache_plugin_next/1}, - [CacheSize|<<>>]}. + {CacheBits, CacheSize, <<>>}}. rand_plugin_next(Seed) -> {bytes_to_integer(strong_rand_range(1 bsl 64)), Seed}. @@ -365,11 +363,12 @@ rand_plugin_uniform(State) -> rand_plugin_uniform(Max, State) -> {bytes_to_integer(strong_rand_range(Max)) + 1, State}. --dialyzer({no_improper_lists, rand_cache_plugin_next/1}). -rand_cache_plugin_next([CacheSize|<<>>]) -> - rand_cache_plugin_next([CacheSize|strong_rand_bytes(CacheSize)]); -rand_cache_plugin_next([CacheSize|<>]) -> - {I, [CacheSize|Cache]}. +rand_cache_plugin_next({CacheBits, CacheSize, <<>>}) -> + rand_cache_plugin_next( + {CacheBits, CacheSize, strong_rand_bytes(CacheSize)}); +rand_cache_plugin_next({CacheBits, CacheSize, Cache}) -> + <> = Cache, + {I, {CacheBits, CacheSize, NewCache}}. strong_rand_range(Range) when is_integer(Range), Range > 0 -> BinRange = int_to_bin(Range), -- cgit v1.2.3