diff --git a/.travis.yml b/.travis.yml index 271323e7..b1cfa148 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ language: erlang notifications: email: false otp_release: + - 23.0 - 22.0 - 21.3 - 21.2.3 diff --git a/rebar b/rebar index 0d1980bf..c57f7add 100755 Binary files a/rebar and b/rebar differ diff --git a/rebar.config b/rebar.config index 2fc417e2..3176edab 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 f89f19b7..f288560a 100644 --- a/src/mochiweb_session.erl +++ b/src/mochiweb_session.erl @@ -131,7 +131,7 @@ decrypt_data(<>, Key) -> 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 @@ gen_hmac(ExpirationTime, Data, SessionKey, Key) -> 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 @@ decrypt_data(<>, Key) -> 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), + <>. + +-spec decrypt_data(binary(), binary()) -> binary(). +decrypt_data(<>, 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 9aeca2a2..74084693 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 @@ listen(Ssl, Port, Opts, SslOpts) -> 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 @@ filter_broken_cipher_suites(Ciphers) -> 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 ->