aboutsummaryrefslogtreecommitdiffstats
path: root/lib/crypto
diff options
context:
space:
mode:
Diffstat (limited to 'lib/crypto')
-rw-r--r--lib/crypto/c_src/crypto.c64
-rw-r--r--lib/crypto/doc/src/crypto.xml46
-rw-r--r--lib/crypto/doc/src/notes.xml16
-rw-r--r--lib/crypto/src/crypto.erl55
-rw-r--r--lib/crypto/test/crypto_SUITE.erl110
-rw-r--r--lib/crypto/vsn.mk2
6 files changed, 280 insertions, 13 deletions
diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c
index b2f31870b9..1f4ce9a3da 100644
--- a/lib/crypto/c_src/crypto.c
+++ b/lib/crypto/c_src/crypto.c
@@ -425,10 +425,12 @@ static ERL_NIF_TERM hmac_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM
static ERL_NIF_TERM cmac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM block_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM aes_cfb_8_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM aes_cfb_128_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM aes_ige_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
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_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[]);
@@ -501,6 +503,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_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},
@@ -1736,17 +1739,20 @@ static ERL_NIF_TERM block_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM
return enif_raise_exception(env, atom_notsup);
}
- if ((argv[0] == atom_aes_cfb8 || argv[0] == atom_aes_cfb128)
- && (key.size == 24 || key.size == 32)
-#ifdef FIPS_SUPPORT
- && !FIPS_mode()
-#endif
- ) {
+ if (argv[0] == atom_aes_cfb8
+ && (key.size == 24 || key.size == 32)) {
/* Why do EVP_CIPHER_CTX_set_key_length() fail on these key sizes?
* Fall back on low level API
*/
return aes_cfb_8_crypt(env, argc-1, argv+1);
}
+ else if (argv[0] == atom_aes_cfb128
+ && (key.size == 24 || key.size == 32)) {
+ /* Why do EVP_CIPHER_CTX_set_key_length() fail on these key sizes?
+ * Fall back on low level API
+ */
+ return aes_cfb_128_crypt_nif(env, argc-1, argv+1);
+ }
ivec_size = EVP_CIPHER_iv_length(cipher);
@@ -1822,6 +1828,31 @@ static ERL_NIF_TERM aes_cfb_8_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM
return ret;
}
+static ERL_NIF_TERM aes_cfb_128_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Key, IVec, Data, IsEncrypt) */
+ ErlNifBinary key, ivec, text;
+ AES_KEY aes_key;
+ unsigned char ivec_clone[16]; /* writable copy */
+ int new_ivlen = 0;
+ ERL_NIF_TERM ret;
+
+ if (!enif_inspect_iolist_as_binary(env, argv[0], &key)
+ || !(key.size == 16 || key.size == 24 || key.size == 32)
+ || !enif_inspect_binary(env, argv[1], &ivec) || ivec.size != 16
+ || !enif_inspect_iolist_as_binary(env, argv[2], &text)) {
+ return enif_make_badarg(env);
+ }
+
+ memcpy(ivec_clone, ivec.data, 16);
+ AES_set_encrypt_key(key.data, key.size * 8, &aes_key);
+ AES_cfb128_encrypt((unsigned char *) text.data,
+ enif_make_new_binary(env, text.size, &ret),
+ text.size, &aes_key, ivec_clone, &new_ivlen,
+ (argv[3] != atom_true));
+ CONSUME_REDS(env,text);
+ return ret;
+}
+
static ERL_NIF_TERM aes_ige_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Key, IVec, Data, IsEncrypt) */
#ifdef HAVE_AES_IGE
@@ -2331,6 +2362,27 @@ static ERL_NIF_TERM bin_from_bn(ErlNifEnv* env, const BIGNUM *bn)
return term;
}
+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_bin(env, argv[0], &bn_range)) {
+ return enif_make_badarg(env);
+ }
+
+ bn_rand = BN_new();
+ if (BN_rand_range(bn_rand, bn_range) != 1) {
+ ret = atom_false;
+ }
+ else {
+ ret = bin_from_bn(env, bn_rand);
+ }
+ BN_free(bn_rand);
+ BN_free(bn_range);
+ 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..552d95d7dc 100644
--- a/lib/crypto/doc/src/crypto.xml
+++ b/lib/crypto/doc/src/crypto.xml
@@ -658,10 +658,11 @@
</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
+ <seealso marker="#strong_rand_bytes/1">strong_rand_bytes/1</seealso>
+ throws <c>low_entropy</c></p>
</desc>
</func>
@@ -728,6 +729,43 @@
failed due to lack of secure "randomness".</p>
</desc>
</func>
+
+ <func>
+ <name>rand_seed() -> rand:state()</name>
+ <fsummary>Strong random number generation plugin state</fsummary>
+ <desc>
+ <p>
+ Creates state object for
+ <seealso marker="stdlib:rand">random number generation</seealso>,
+ in order to generate cryptographically strong random numbers
+ (based on OpenSSL's <c>BN_rand_range</c>),
+ and saves it on process dictionary before returning it as well.
+ See also
+ <seealso marker="stdlib:rand#seed-1">rand:seed/1</seealso>.
+ </p>
+ <p><em>Example</em></p>
+ <pre>
+_ = crypto:rand_seed(),
+_IntegerValue = rand:uniform(42), % [1; 42]
+_FloatValue = rand:uniform(). % [0.0; 1.0[</pre>
+ </desc>
+ </func>
+
+ <func>
+ <name>rand_seed_s() -> rand:state()</name>
+ <fsummary>Strong random number generation plugin state</fsummary>
+ <desc>
+ <p>
+ Creates state object for
+ <seealso marker="stdlib:rand">random number generation</seealso>,
+ in order to generate cryptographically strongly random numbers
+ (based on OpenSSL's <c>BN_rand_range</c>).
+ See also
+ <seealso marker="stdlib:rand#seed_s-1">rand:seed_s/1</seealso>.
+ </p>
+ </desc>
+ </func>
+
<func>
<name>stream_init(Type, Key) -> State</name>
<fsummary></fsummary>
diff --git a/lib/crypto/doc/src/notes.xml b/lib/crypto/doc/src/notes.xml
index 37997b649b..887aeca680 100644
--- a/lib/crypto/doc/src/notes.xml
+++ b/lib/crypto/doc/src/notes.xml
@@ -31,6 +31,22 @@
</header>
<p>This document describes the changes made to the Crypto application.</p>
+<section><title>Crypto 3.7.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix a bug with AES CFB 128 for 192 and 256 bit keys.
+ Thanks to kellymclaughlin !</p>
+ <p>
+ Own Id: OTP-14313 Aux Id: PR-1393 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Crypto 3.7.3</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl
index ce8add6559..1287ec6176 100644
--- a/lib/crypto/src/crypto.erl
+++ b/lib/crypto/src/crypto.erl
@@ -30,6 +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_plugin_next/1]).
+-export([rand_plugin_uniform/1]).
+-export([rand_plugin_uniform/2]).
+-export([rand_plugin_jump/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]).
@@ -45,6 +51,9 @@
%% This should correspond to the similar macro in crypto.c
-define(MAX_BYTES_TO_NIF, 20000). %% Current value is: erlang:system_info(context_reductions) * 10
+%% Used by strong_rand_float/0
+-define(HALF_DBL_EPSILON, 1.1102230246251565e-16). % math:pow(2, -53)
+
%%-type ecdsa_digest_type() :: 'md5' | 'sha' | 'sha256' | 'sha384' | 'sha512'.
-type crypto_integer() :: binary() | integer().
%%-type ec_named_curve() :: atom().
@@ -56,6 +65,7 @@
%%-type ec_curve() :: ec_named_curve() | ec_curve_spec().
%%-type ec_key() :: {Curve :: ec_curve(), PrivKey :: binary() | undefined, PubKey :: ec_point() | undefined}.
+-compile(no_native).
-on_load(on_load/0).
-define(CRYPTO_NIF_VSN,302).
@@ -285,9 +295,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 rand_seed() -> rand:state().
+-spec rand_seed_s() -> rand:state().
-spec rand_uniform(crypto_integer(), crypto_integer()) ->
crypto_integer().
@@ -299,6 +311,46 @@ strong_rand_bytes(Bytes) ->
strong_rand_bytes_nif(_Bytes) -> ?nif_stub.
+rand_seed() ->
+ rand:seed(rand_seed_s()).
+
+rand_seed_s() ->
+ {#{ type => ?MODULE,
+ max => infinity,
+ next => fun ?MODULE:rand_plugin_next/1,
+ uniform => fun ?MODULE:rand_plugin_uniform/1,
+ uniform_n => fun ?MODULE:rand_plugin_uniform/2,
+ jump => fun ?MODULE:rand_plugin_jump/1},
+ no_seed}.
+
+rand_plugin_next(Seed) ->
+ {bytes_to_integer(strong_rand_range(1 bsl 64)), Seed}.
+
+rand_plugin_uniform(State) ->
+ {strong_rand_float(), State}.
+
+rand_plugin_uniform(Max, State) ->
+ {bytes_to_integer(strong_rand_range(Max)) + 1, State}.
+
+rand_plugin_jump(State) ->
+ State.
+
+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);
+ <<BinResult/binary>> ->
+ BinResult
+ end.
+strong_rand_range_nif(_BinRange) -> ?nif_stub.
+
+strong_rand_float() ->
+ WholeRange = strong_rand_range(1 bsl 53),
+ ?HALF_DBL_EPSILON * bytes_to_integer(WholeRange).
+
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 ->
@@ -327,6 +379,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..1b7456af18 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,
+ rand_plugin,
+ rand_plugin_s
].
groups() ->
@@ -486,6 +488,17 @@ rand_uniform(Config) when is_list(Config) ->
10 = byte_size(crypto:strong_rand_bytes(10)).
%%--------------------------------------------------------------------
+rand_plugin() ->
+ [{doc, "crypto rand plugin testing (implicit state / process dictionary)"}].
+rand_plugin(Config) when is_list(Config) ->
+ rand_plugin_aux(implicit_state).
+
+rand_plugin_s() ->
+ [{doc, "crypto rand plugin testing (explicit state)"}].
+rand_plugin_s(Config) when is_list(Config) ->
+ rand_plugin_aux(explicit_state).
+
+%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
hash(_, [], []) ->
@@ -951,6 +964,101 @@ crypto_rand_uniform(L,H) ->
ct:fail({"Not in interval", R1, L, H})
end.
+foldallmap(_Fun, AccN, []) ->
+ {true, AccN};
+foldallmap(Fun, AccN, [H|T]) ->
+ case Fun(H, AccN) of
+ {true, AccM} -> foldallmap(Fun, AccM, T);
+ {{false, Result}, AccM} -> {Result, AccM}
+ end.
+
+allmap(_Fun, []) ->
+ true;
+allmap(Fun, [H|T]) ->
+ case Fun(H) of
+ true -> allmap(Fun, T);
+ {false, Result} -> Result
+ end.
+
+rand_plugin_aux(StateType) ->
+ {Seeder, SeedExporter, FloatGenerator, IntegerGenerator} = rand_plugin_functions(StateType),
+ State0 = Seeder(),
+ {crypto, no_seed} = SeedExporter(State0),
+ {FloatTestResult, State1} = rand_plugin_aux_floats(State0, FloatGenerator),
+ case FloatTestResult of
+ true ->
+ {IntegerTestResult, _State2} = rand_plugin_aux_integers(State1, IntegerGenerator),
+ IntegerTestResult;
+ {false, _} ->
+ FloatTestResult
+ end.
+
+% returns {Seeder, SeedExporter, FloatGenerator, IntegerGenerator} with consistent signatures
+rand_plugin_functions(implicit_state) ->
+ {fun () -> crypto:rand_seed(), implicit_state end,
+ fun (implicit_state) -> rand:export_seed() end,
+ fun (implicit_state) -> {rand:uniform(), implicit_state} end,
+ fun (N, implicit_state) -> {rand:uniform(N), implicit_state} end};
+rand_plugin_functions(explicit_state) ->
+ {fun crypto:rand_seed_s/0,
+ fun rand:export_seed_s/1,
+ fun rand:uniform_s/1,
+ fun rand:uniform_s/2}.
+
+rand_plugin_aux_floats(State0, FloatGenerator) ->
+ {FloatSamples, State1} =
+ lists:mapfoldl(
+ fun (_, StateAcc) ->
+ FloatGenerator(StateAcc)
+ end,
+ State0,
+ lists:seq(1, 10000)),
+
+ {allmap(
+ fun (V) ->
+ (V >= 0.0 andalso V < 1.0)
+ orelse {false, ct:fail({"Float sample not in interval", V, 0.0, 1.0})}
+ end,
+ FloatSamples),
+ State1}.
+
+rand_plugin_aux_integers(State0, IntegerGenerator) ->
+ MaxIntegerCeiling = 1 bsl 32,
+ {IntegerCeilings, State1} =
+ lists:mapfoldl(
+ fun (_, StateAcc) ->
+ IntegerGenerator(MaxIntegerCeiling, StateAcc)
+ end,
+ State0,
+ lists:seq(1, 100)),
+
+ foldallmap(
+ fun (Ceiling, StateAcc) ->
+ case Ceiling >= 1 andalso Ceiling =< MaxIntegerCeiling of
+ false ->
+ {{false, ct:fail({"Integer ceiling not in interval",
+ Ceiling, 1, MaxIntegerCeiling})},
+ StateAcc};
+ true ->
+ foldallmap(
+ fun (_, SubStateAcc) ->
+ {Sample, NewSubStateAcc} = IntegerGenerator(Ceiling, SubStateAcc),
+ case Sample >= 1 andalso Sample =< Ceiling of
+ false ->
+ {{false, ct:fail({"Integer sample not in interval",
+ Sample, 1, Ceiling})},
+ NewSubStateAcc};
+ true ->
+ {true, NewSubStateAcc}
+ end
+ end,
+ StateAcc,
+ lists:seq(1, 100))
+ end
+ end,
+ State1,
+ IntegerCeilings).
+
%%--------------------------------------------------------------------
%% Test data ------------------------------------------------
%%--------------------------------------------------------------------
diff --git a/lib/crypto/vsn.mk b/lib/crypto/vsn.mk
index 81cb2f8130..f3e0623ac9 100644
--- a/lib/crypto/vsn.mk
+++ b/lib/crypto/vsn.mk
@@ -1 +1 @@
-CRYPTO_VSN = 3.7.3
+CRYPTO_VSN = 3.7.4