Merge pull request #231 from noahshaw11/upgrade-crypto-functions-to-support-OTP-23
Upgrade crypto functions to support OTP 23
diff --git a/.travis.yml b/.travis.yml
index 271323e..b1cfa14 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,6 +3,7 @@
notifications:
email: false
otp_release:
+ - 23.0
- 22.0
- 21.3
- 21.2.3
diff --git a/rebar b/rebar
index 0d1980b..c57f7ad 100755
--- a/rebar
+++ b/rebar
Binary files differ
diff --git a/rebar.config b/rebar.config
index 2fc417e..3176eda 100644
--- a/rebar.config
+++ b/rebar.config
@@ -2,6 +2,7 @@
{erl_opts, [debug_info,
{platform_define, "^R15", 'gen_tcp_r15b_workaround'},
{platform_define, "^(R14|R15|R16B-)", 'crypto_compatibility'},
+ {platform_define, "^(R14|R15|R16|17|18|19|20|21|22)", new_crypto_unavailable},
{platform_define, "^(R14|R15|R16B|17)", 'rand_mod_unavailable'},
{platform_define, "^(R14|R15|R16B|17)", 'sni_unavailable'},
{platform_define, "^(R14|R15|R16)", 'map_unavailable'},
diff --git a/src/mochiweb_session.erl b/src/mochiweb_session.erl
index f89f19b..f288560 100644
--- a/src/mochiweb_session.erl
+++ b/src/mochiweb_session.erl
@@ -131,7 +131,7 @@
crypto:aes_cfb_128_decrypt(Key, IV, Crypt).
-spec gen_key(iolist(), iolist()) -> binary().
-gen_key(ExpirationTime, ServerKey)->
+gen_key(ExpirationTime, ServerKey) ->
crypto:md5_mac(ServerKey, [ExpirationTime]).
-spec gen_hmac(iolist(), binary(), iolist(), binary()) -> binary().
@@ -139,6 +139,7 @@
crypto:sha_mac(Key, [ExpirationTime, Data, SessionKey]).
-else.
+-ifdef(new_crypto_unavailable).
-spec encrypt_data(binary(), binary()) -> binary().
encrypt_data(Data, Key) ->
IV = crypto:strong_rand_bytes(16),
@@ -150,13 +151,33 @@
crypto:block_decrypt(aes_cfb128, Key, IV, Crypt).
-spec gen_key(iolist(), iolist()) -> binary().
-gen_key(ExpirationTime, ServerKey)->
+gen_key(ExpirationTime, ServerKey) ->
crypto:hmac(md5, ServerKey, [ExpirationTime]).
-spec gen_hmac(iolist(), binary(), iolist(), binary()) -> binary().
gen_hmac(ExpirationTime, Data, SessionKey, Key) ->
crypto:hmac(sha, Key, [ExpirationTime, Data, SessionKey]).
+-else. % new crypto available (OTP 23+)
+-spec encrypt_data(binary(), binary()) -> binary().
+encrypt_data(Data, Key) ->
+ IV = crypto:strong_rand_bytes(16),
+ Crypt = crypto:crypto_one_time(aes_128_cfb128, Key, IV, Data, true),
+ <<IV/binary, Crypt/binary>>.
+
+-spec decrypt_data(binary(), binary()) -> binary().
+decrypt_data(<<IV:16/binary, Crypt/binary>>, Key) ->
+ crypto:crypto_one_time(aes_128_cfb128, Key, IV, Crypt, false).
+
+-spec gen_key(iolist(), iolist()) -> binary().
+gen_key(ExpirationTime, ServerKey) ->
+ crypto:mac(hmac, md5, ServerKey, [ExpirationTime]).
+
+-spec gen_hmac(iolist(), binary(), iolist(), binary()) -> binary().
+gen_hmac(ExpirationTime, Data, SessionKey, Key) ->
+ crypto:mac(hmac, sha, Key, [ExpirationTime, Data, SessionKey]).
+
+-endif.
-endif.
-ifdef(TEST).
diff --git a/src/mochiweb_socket.erl b/src/mochiweb_socket.erl
index 9aeca2a..7408469 100644
--- a/src/mochiweb_socket.erl
+++ b/src/mochiweb_socket.erl
@@ -17,8 +17,8 @@
listen(Ssl, Port, Opts, SslOpts) ->
case Ssl of
true ->
- Opts1 = add_unbroken_ciphers_default(Opts ++ SslOpts),
- Opts2 = add_safe_protocol_versions(Opts1),
+ Opts1 = add_safe_protocol_versions(Opts),
+ Opts2 = add_unbroken_ciphers_default(Opts1 ++ SslOpts),
case ssl:listen(Port, Opts2) of
{ok, ListenSocket} ->
{ok, {ssl, ListenSocket}};
@@ -29,11 +29,39 @@
gen_tcp:listen(Port, Opts)
end.
+-ifdef(new_crypto_unavailable).
add_unbroken_ciphers_default(Opts) ->
Default = filter_unsecure_cipher_suites(ssl:cipher_suites()),
Ciphers = filter_broken_cipher_suites(proplists:get_value(ciphers, Opts, Default)),
[{ciphers, Ciphers} | proplists:delete(ciphers, Opts)].
+%% Filter old map style cipher suites
+filter_unsecure_cipher_suites(Ciphers) ->
+ lists:filter(fun
+ ({_,des_cbc,_}) -> false;
+ ({_,_,md5}) -> false;
+ (_) -> true
+ end,
+ Ciphers).
+
+-else.
+add_unbroken_ciphers_default(Opts) ->
+ %% add_safe_protocol_versions/1 must have been called to ensure a {versions, _} tuple is present
+ Versions = proplists:get_value(versions, Opts),
+ CipherSuites = lists:append([ssl:cipher_suites(all, Version) || Version <- Versions]),
+ Default = filter_unsecure_cipher_suites(CipherSuites),
+ Ciphers = filter_broken_cipher_suites(proplists:get_value(ciphers, Opts, Default)),
+ [{ciphers, Ciphers} | proplists:delete(ciphers, Opts)].
+
+%% Filter new map style cipher suites
+filter_unsecure_cipher_suites(Ciphers) ->
+ ssl:filter_cipher_suites(Ciphers, [
+ {key_exchange, fun(des_cbc) -> false; (_) -> true end},
+ {mac, fun(md5) -> false; (_) -> true end}
+ ]).
+
+-endif.
+
filter_broken_cipher_suites(Ciphers) ->
case proplists:get_value(ssl_app, ssl:versions()) of
"5.3" ++ _ ->
@@ -44,14 +72,6 @@
Ciphers
end.
-filter_unsecure_cipher_suites(Ciphers) ->
- lists:filter(fun
- ({_,des_cbc,_}) -> false;
- ({_,_,md5}) -> false;
- (_) -> true
- end,
- Ciphers).
-
add_safe_protocol_versions(Opts) ->
case proplists:is_defined(versions, Opts) of
true ->