diff options
Diffstat (limited to 'lib/ssl')
35 files changed, 2483 insertions, 530 deletions
diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml index c54acfcf53..d117641496 100644 --- a/lib/ssl/doc/src/notes.xml +++ b/lib/ssl/doc/src/notes.xml @@ -27,6 +27,35 @@ </header> <p>This document describes the changes made to the SSL application.</p> +<section><title>SSL 9.0.3</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Correct alert handling with new TLS sender process, from + ssl-9.0.2. CLOSE ALERTS could under some circumstances be + encoded using an incorrect cipher state. This would cause + the peer to regard them as unknown messages.</p> + <p> + Own Id: OTP-15337 Aux Id: ERL-738 </p> + </item> + <item> + <p> + Correct handling of socket packet option with new TLS + sender process, from ssl-9.0.2. When changing the socket + option {packet, 1|2|3|4} with ssl:setopts/2 the option + must internally be propagated to the sender process as + well as the reader process as this particular option also + affects the data to be sent.</p> + <p> + Own Id: OTP-15348 Aux Id: ERL-747 </p> + </item> + </list> + </section> + +</section> + <section><title>SSL 9.0.2</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml index 6efa022a79..ef99ace351 100644 --- a/lib/ssl/doc/src/ssl.xml +++ b/lib/ssl/doc/src/ssl.xml @@ -209,7 +209,24 @@ elliptic_curves => [oid] | undefined, sni => string() | undefined} }</c></p></item> - + + <tag><c>signature_scheme() =</c></tag> + <item> + <p><c>rsa_pkcs1_sha256</c></p> + <p><c>| rsa_pkcs1_sha384</c></p> + <p><c>| rsa_pkcs1_sha512</c></p> + <p><c>| ecdsa_secp256r1_sha256</c></p> + <p><c>| ecdsa_secp384r1_sha384</c></p> + <p><c>| ecdsa_secp521r1_sha512</c></p> + <p><c>| rsa_pss_rsae_sha256</c></p> + <p><c>| rsa_pss_rsae_sha384</c></p> + <p><c>| rsa_pss_rsae_sha512</c></p> + <p><c>| rsa_pss_pss_sha256</c></p> + <p><c>| rsa_pss_pss_sha384</c></p> + <p><c>| rsa_pss_pss_sha512</c></p> + <p><c>| rsa_pkcs1_sha1</c></p> + <p><c>| ecdsa_sha1</c></p> + </item> </taglist> </section> @@ -709,6 +726,26 @@ fun(srp, Username :: string(), UserState :: term()) -> that may be selected. Default support for {md5, rsa} removed in ssl-8.0 </p> </item> + <tag><marker id="signature_algs_cert"/><c>{signature_algs_cert, [signature_scheme()]}</c></tag> + <item> + <p> + In addition to the signature_algorithms extension from TLS 1.2, + <url href="http://www.ietf.org/rfc/rfc8446.txt#section-4.2.3">TLS 1.3 + (RFC 5246 Section 4.2.3)</url>adds the signature_algorithms_cert extension + which enables having special requirements on the signatures used in the + certificates that differs from the requirements on digital signatures as a whole. + If this is not required this extension is not needed. + </p> + <p> + The client will send a signature_algorithms_cert extension (ClientHello), + if TLS version 1.3 or later is used, and the signature_algs_cert option is + explicitly specified. By default, only the signature_algs extension is sent. + </p> + <p> + The signature schemes shall be ordered according to the client's preference + (favorite choice first). + </p> + </item> </taglist> </section> @@ -860,7 +897,6 @@ fun(srp, Username :: string(), UserState :: term()) -> negotiation, introduced in TLS-1.2. The algorithms will also be offered to the client if a client certificate is requested. For more details see the <seealso marker="#client_signature_algs">corresponding client option</seealso>. </p> </item> - </taglist> </section> diff --git a/lib/ssl/src/Makefile b/lib/ssl/src/Makefile index af36d04a99..c7dee81c71 100644 --- a/lib/ssl/src/Makefile +++ b/lib/ssl/src/Makefile @@ -65,6 +65,7 @@ MODULES= \ ssl_cipher_format \ ssl_srp_primes \ tls_connection \ + tls_connection_1_3 \ dtls_connection \ tls_sender\ ssl_config \ @@ -83,19 +84,21 @@ MODULES= \ tls_socket \ dtls_socket \ tls_record \ + tls_record_1_3\ dtls_record \ ssl_record \ ssl_v3 \ tls_v1 \ dtls_v1 \ - ssl_logger + ssl_logger \ + ssl_dh_groups INTERNAL_HRL_FILES = \ ssl_alert.hrl ssl_cipher.hrl \ tls_connection.hrl dtls_connection.hrl ssl_connection.hrl \ ssl_handshake.hrl tls_handshake.hrl tls_handshake_1_3.hrl dtls_handshake.hrl \ ssl_api.hrl ssl_internal.hrl \ - ssl_record.hrl tls_record.hrl dtls_record.hrl ssl_srp.hrl + ssl_record.hrl tls_record.hrl tls_record_1_3.hrl dtls_record.hrl ssl_srp.hrl ERL_FILES= \ $(MODULES:%=%.erl) \ @@ -169,6 +172,7 @@ $(EBIN)/ssl_certificate.$(EMULATOR): ssl_internal.hrl ssl_alert.hrl ssl_handshak $(EBIN)/ssl_certificate_db.$(EMULATOR): ssl_internal.hrl ../../public_key/include/public_key.hrl ../../kernel/include/file.hrl $(EBIN)/ssl_cipher.$(EMULATOR): ssl_internal.hrl ssl_record.hrl ssl_cipher.hrl ssl_handshake.hrl ssl_alert.hrl ../../public_key/include/public_key.hrl $(EBIN)/tls_connection.$(EMULATOR): ssl_internal.hrl tls_connection.hrl tls_record.hrl ssl_cipher.hrl tls_handshake.hrl ssl_alert.hrl ../../public_key/include/public_key.hrl +$(EBIN)/tls_connection_1_3.$(EMULATOR): ssl_internal.hrl tls_connection.hrl $(EBIN)/dtls_connection.$(EMULATOR): ssl_internal.hrl dtls_connection.hrl dtls_record.hrl ssl_cipher.hrl dtls_handshake.hrl ssl_alert.hrl ../../public_key/include/public_key.hrl $(EBIN)/tls_handshake.$(EMULATOR): ssl_internal.hrl tls_record.hrl ssl_cipher.hrl tls_handshake.hrl ssl_alert.hrl ../../public_key/include/public_key.hrl $(EBIN)/tls_handshake.$(EMULATOR): ssl_internal.hrl ssl_connection.hrl ssl_record.hrl ssl_cipher.hrl ssl_handshake.hrl ssl_alert.hrl ../../public_key/include/public_key.hrl diff --git a/lib/ssl/src/dtls_handshake.erl b/lib/ssl/src/dtls_handshake.erl index c8daa11433..55aa8174a3 100644 --- a/lib/ssl/src/dtls_handshake.erl +++ b/lib/ssl/src/dtls_handshake.erl @@ -194,7 +194,7 @@ handle_client_hello(Version, ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY); _ -> #{key_exchange := KeyExAlg} = ssl_cipher_format:suite_definition(CipherSuite), - case ssl_handshake:select_hashsign(ClientHashSigns, Cert, KeyExAlg, + case ssl_handshake:select_hashsign({ClientHashSigns, undefined}, Cert, KeyExAlg, SupportedHashSigns, TLSVersion) of #alert{} = Alert -> Alert; diff --git a/lib/ssl/src/dtls_record.erl b/lib/ssl/src/dtls_record.erl index 9eb0d8e2d7..b7346d3ec8 100644 --- a/lib/ssl/src/dtls_record.erl +++ b/lib/ssl/src/dtls_record.erl @@ -499,23 +499,22 @@ encode_dtls_cipher_text(Type, {MajVer, MinVer}, Fragment, WriteState#{sequence_number => Seq + 1}}. encode_plain_text(Type, Version, Data, #{compression_state := CompS0, + cipher_state := CipherS0, epoch := Epoch, sequence_number := Seq, - cipher_state := CipherS0, security_parameters := #security_parameters{ cipher_type = ?AEAD, - bulk_cipher_algorithm = - BulkCipherAlgo, + bulk_cipher_algorithm = BCAlg, compression_algorithm = CompAlg} } = WriteState0) -> {Comp, CompS1} = ssl_record:compress(CompAlg, Data, CompS0), - AAD = calc_aad(Type, Version, Epoch, Seq), + AAD = start_additional_data(Type, Version, Epoch, Seq), + CipherS = ssl_record:nonce_seed(BCAlg, <<?UINT16(Epoch), ?UINT48(Seq)>>, CipherS0), + WriteState = WriteState0#{compression_state => CompS1, + cipher_state => CipherS}, TLSVersion = dtls_v1:corresponding_tls_version(Version), - {CipherFragment, CipherS1} = - ssl_cipher:cipher_aead(BulkCipherAlgo, CipherS0, Seq, AAD, Comp, TLSVersion), - {CipherFragment, WriteState0#{compression_state => CompS1, - cipher_state => CipherS1}}; + ssl_record:cipher_aead(TLSVersion, Comp, WriteState, AAD); encode_plain_text(Type, Version, Fragment, #{compression_state := CompS0, epoch := Epoch, sequence_number := Seq, @@ -547,9 +546,10 @@ decode_cipher_text(#ssl_tls{type = Type, version = Version, BulkCipherAlgo, compression_algorithm = CompAlg}} = ReadState0, ConnnectionStates0) -> - AAD = calc_aad(Type, Version, Epoch, Seq), + AAD = start_additional_data(Type, Version, Epoch, Seq), + CipherS1 = ssl_record:nonce_seed(BulkCipherAlgo, <<?UINT16(Epoch), ?UINT48(Seq)>>, CipherS0), TLSVersion = dtls_v1:corresponding_tls_version(Version), - case ssl_cipher:decipher_aead(BulkCipherAlgo, CipherS0, Seq, AAD, CipherFragment, TLSVersion) of + case ssl_record:decipher_aead(BulkCipherAlgo, CipherS1, AAD, CipherFragment, TLSVersion) of {PlainFragment, CipherState} -> {Plain, CompressionS1} = ssl_record:uncompress(CompAlg, PlainFragment, CompressionS0), @@ -600,7 +600,7 @@ mac_hash({Major, Minor}, MacAlg, MacSecret, Epoch, SeqNo, Type, Length, Fragment Fragment], dtls_v1:hmac_hash(MacAlg, MacSecret, Value). -calc_aad(Type, {MajVer, MinVer}, Epoch, SeqNo) -> +start_additional_data(Type, {MajVer, MinVer}, Epoch, SeqNo) -> <<?UINT16(Epoch), ?UINT48(SeqNo), ?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer)>>. %%-------------------------------------------------------------------- diff --git a/lib/ssl/src/inet_tls_dist.erl b/lib/ssl/src/inet_tls_dist.erl index 7f1a77f601..ce771343fe 100644 --- a/lib/ssl/src/inet_tls_dist.erl +++ b/lib/ssl/src/inet_tls_dist.erl @@ -568,7 +568,7 @@ gen_close(Driver, Socket) -> get_address_resolver(EpmdModule, Driver) -> case erlang:function_exported(EpmdModule, address_please, 3) of true -> {EpmdModule, address_please}; - _ -> {Driver, getaddr} + _ -> {erl_epmd, address_please} end. %% ------------------------------------------------------------ diff --git a/lib/ssl/src/ssl.app.src b/lib/ssl/src/ssl.app.src index 17173d7c79..e7a4d73ec4 100644 --- a/lib/ssl/src/ssl.app.src +++ b/lib/ssl/src/ssl.app.src @@ -4,14 +4,17 @@ {modules, [ %% TLS/SSL tls_connection, + tls_connection_1_3, tls_handshake, tls_handshake_1_3, tls_record, + tls_record_1_3, tls_socket, tls_v1, ssl_v3, tls_connection_sup, tls_sender, + ssl_dh_groups, %% DTLS dtls_connection, dtls_handshake, diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index ef9aac34bf..524f06d52e 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -51,7 +51,7 @@ %% SSL/TLS protocol handling -export([cipher_suites/0, cipher_suites/1, cipher_suites/2, filter_cipher_suites/2, prepend_cipher_suites/2, append_cipher_suites/2, - eccs/0, eccs/1, versions/0, + eccs/0, eccs/1, versions/0, groups/0, format_error/1, renegotiate/1, prf/5, negotiated_protocol/1, connection_information/1, connection_information/2]). %% Misc @@ -578,6 +578,13 @@ eccs_filter_supported(Curves) -> Curves). %%-------------------------------------------------------------------- +-spec groups() -> tls_v1:supported_groups(). +%% Description: returns all supported groups (TLS 1.3 and later) +%%-------------------------------------------------------------------- +groups() -> + tls_v1:groups(4). + +%%-------------------------------------------------------------------- -spec getopts(#sslsocket{}, [gen_tcp:option_name()]) -> {ok, [gen_tcp:option()]} | {error, reason()}. %% @@ -614,6 +621,25 @@ getopts(#sslsocket{}, OptionTags) -> %% %% Description: Sets options %%-------------------------------------------------------------------- +setopts(#sslsocket{pid = [Pid, Sender]}, Options0) when is_pid(Pid), is_list(Options0) -> + try proplists:expand([{binary, [{mode, binary}]}, + {list, [{mode, list}]}], Options0) of + Options -> + case proplists:get_value(packet, Options, undefined) of + undefined -> + ssl_connection:set_opts(Pid, Options); + PacketOpt -> + case tls_sender:setopts(Sender, [{packet, PacketOpt}]) of + ok -> + ssl_connection:set_opts(Pid, Options); + Error -> + Error + end + end + catch + _:_ -> + {error, {options, {not_a_proplist, Options0}}} + end; setopts(#sslsocket{pid = [Pid|_]}, Options0) when is_pid(Pid), is_list(Options0) -> try proplists:expand([{binary, [{mode, binary}]}, {list, [{mode, list}]}], Options0) of @@ -961,6 +987,9 @@ handle_options(Opts0, Role, Host) -> HighestVersion), eccs = handle_eccs_option(proplists:get_value(eccs, Opts, eccs()), HighestVersion), + supported_groups = handle_supported_groups_option( + proplists:get_value(supported_groups, Opts, groups()), + HighestVersion), signature_algs = handle_hashsigns_option( proplists:get_value( @@ -975,10 +1004,7 @@ handle_options(Opts0, Role, Host) -> proplists:get_value( signature_algs_cert, Opts, - default_option_role(server, - tls_v1:default_signature_schemes(HighestVersion), - Role - )), + undefined), %% Do not send by default tls_version(HighestVersion)), %% Server side option reuse_session = handle_option(reuse_session, Opts, ReuseSessionFun), @@ -1041,8 +1067,9 @@ handle_options(Opts0, Role, Host) -> alpn_preferred_protocols, next_protocols_advertised, client_preferred_next_protocols, log_alert, log_level, server_name_indication, honor_cipher_order, padding_check, crl_check, crl_cache, - fallback, signature_algs, eccs, honor_ecc_order, beast_mitigation, - max_handshake_size, handshake, customize_hostname_check], + fallback, signature_algs, signature_algs_cert, eccs, honor_ecc_order, + beast_mitigation, max_handshake_size, handshake, customize_hostname_check, + supported_groups], SockOpts = lists:foldl(fun(Key, PropList) -> proplists:delete(Key, PropList) end, Opts, SslOptions), @@ -1326,8 +1353,6 @@ handle_signature_algorithms_option(Value, Version) when is_list(Value) _ -> Value end; -handle_signature_algorithms_option(_, Version) when Version >= {3, 4} -> - handle_signature_algorithms_option(tls_v1:default_signature_schemes(Version), Version); handle_signature_algorithms_option(_, _Version) -> undefined. @@ -1478,6 +1503,16 @@ handle_eccs_option(Value, Version) when is_list(Value) -> error:_ -> throw({error, {options, {eccs, Value}}}) end. +handle_supported_groups_option(Value, Version) when is_list(Value) -> + {_Major, Minor} = tls_version(Version), + try tls_v1:groups(Minor, Value) of + Groups -> #supported_groups{supported_groups = Groups} + catch + exit:_ -> throw({error, {options, {supported_groups, Value}}}); + error:_ -> throw({error, {options, {supported_groups, Value}}}) + end. + + unexpected_format(Error) -> lists:flatten(io_lib:format("Unexpected error: ~p", [Error])). @@ -1639,12 +1674,26 @@ new_ssl_options([{eccs, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> handle_eccs_option(Value, RecordCB:highest_protocol_version()) }, RecordCB); +new_ssl_options([{supported_groups, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> + new_ssl_options(Rest, + Opts#ssl_options{supported_groups = + handle_supported_groups_option(Value, RecordCB:highest_protocol_version()) + }, + RecordCB); new_ssl_options([{signature_algs, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> new_ssl_options(Rest, Opts#ssl_options{signature_algs = handle_hashsigns_option(Value, tls_version(RecordCB:highest_protocol_version()))}, RecordCB); +new_ssl_options([{signature_algs_cert, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> + new_ssl_options( + Rest, + Opts#ssl_options{signature_algs_cert = + handle_signature_algorithms_option( + Value, + tls_version(RecordCB:highest_protocol_version()))}, + RecordCB); new_ssl_options([{protocol, dtls = Value} | Rest], #ssl_options{} = Opts, dtls_record = RecordCB) -> new_ssl_options(Rest, Opts#ssl_options{protocol = Value}, RecordCB); new_ssl_options([{protocol, tls = Value} | Rest], #ssl_options{} = Opts, tls_record = RecordCB) -> diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl index 9bb2beaebd..e12faba824 100644 --- a/lib/ssl/src/ssl_cipher.erl +++ b/lib/ssl/src/ssl_cipher.erl @@ -35,7 +35,7 @@ -include_lib("public_key/include/public_key.hrl"). -export([security_parameters/2, security_parameters/3, - cipher_init/3, decipher/6, cipher/5, decipher_aead/6, cipher_aead/6, + cipher_init/3, nonce_seed/2, decipher/6, cipher/5, aead_encrypt/5, aead_decrypt/6, suites/1, all_suites/1, crypto_support_filters/0, chacha_suites/1, anonymous_suites/1, psk_suites/1, psk_suites_anon/1, srp_suites/0, srp_suites_anon/0, @@ -50,6 +50,8 @@ -type cipher_enum() :: integer(). +-export_type([cipher_enum/0]). + %%-------------------------------------------------------------------- -spec security_parameters(ssl_cipher_format:cipher_suite(), #security_parameters{}) -> #security_parameters{}. @@ -93,10 +95,15 @@ cipher_init(?RC4, IV, Key) -> #cipher_state{iv = IV, key = Key, state = State}; cipher_init(?AES_GCM, IV, Key) -> <<Nonce:64>> = random_bytes(8), - #cipher_state{iv = IV, key = Key, nonce = Nonce}; + #cipher_state{iv = IV, key = Key, nonce = Nonce, tag_len = 16}; +cipher_init(?CHACHA20_POLY1305, IV, Key) -> + #cipher_state{iv = IV, key = Key, tag_len = 16}; cipher_init(_BCA, IV, Key) -> #cipher_state{iv = IV, key = Key}. +nonce_seed(Seed, CipherState) -> + CipherState#cipher_state{nonce = Seed}. + %%-------------------------------------------------------------------- -spec cipher(cipher_enum(), #cipher_state{}, binary(), iodata(), ssl_record:ssl_version()) -> {binary(), #cipher_state{}}. @@ -128,32 +135,16 @@ cipher(?AES_CBC, CipherState, Mac, Fragment, Version) -> crypto:block_encrypt(aes_cbc256, Key, IV, T) end, block_size(aes_128_cbc), CipherState, Mac, Fragment, Version). -%%-------------------------------------------------------------------- --spec cipher_aead(cipher_enum(), #cipher_state{}, integer(), binary(), iodata(), ssl_record:ssl_version()) -> - {binary(), #cipher_state{}}. -%% -%% Description: Encrypts the data and protects associated data (AAD) using chipher -%% described by cipher_enum() and updating the cipher state -%% Use for suites that use authenticated encryption with associated data (AEAD) -%%------------------------------------------------------------------- -cipher_aead(?AES_GCM, CipherState, SeqNo, AAD, Fragment, Version) -> - aead_cipher(aes_gcm, CipherState, SeqNo, AAD, Fragment, Version); -cipher_aead(?CHACHA20_POLY1305, CipherState, SeqNo, AAD, Fragment, Version) -> - aead_cipher(chacha20_poly1305, CipherState, SeqNo, AAD, Fragment, Version). - -aead_cipher(chacha20_poly1305, #cipher_state{key=Key} = CipherState, SeqNo, AAD0, Fragment, _Version) -> - CipherLen = erlang:iolist_size(Fragment), - AAD = <<AAD0/binary, ?UINT16(CipherLen)>>, - Nonce = ?uint64(SeqNo), - {Content, CipherTag} = crypto:block_encrypt(chacha20_poly1305, Key, Nonce, {AAD, Fragment}), - {<<Content/binary, CipherTag/binary>>, CipherState}; -aead_cipher(Type, #cipher_state{key=Key, iv = IV0, nonce = Nonce} = CipherState, _SeqNo, AAD0, Fragment, _Version) -> - CipherLen = erlang:iolist_size(Fragment), - AAD = <<AAD0/binary, ?UINT16(CipherLen)>>, - <<Salt:4/bytes, _/binary>> = IV0, - IV = <<Salt/binary, Nonce:64/integer>>, - {Content, CipherTag} = crypto:block_encrypt(Type, Key, IV, {AAD, Fragment}), - {<<Nonce:64/integer, Content/binary, CipherTag/binary>>, CipherState#cipher_state{nonce = Nonce + 1}}. +aead_encrypt(Type, Key, Nonce, Fragment, AdditionalData) -> + crypto:block_encrypt(aead_type(Type), Key, Nonce, {AdditionalData, Fragment}). + +aead_decrypt(Type, Key, Nonce, CipherText, CipherTag, AdditionalData) -> + crypto:block_decrypt(aead_type(Type), Key, Nonce, {AdditionalData, CipherText, CipherTag}). + +aead_type(?AES_GCM) -> + aes_gcm; +aead_type(?CHACHA20_POLY1305) -> + chacha20_poly1305. build_cipher_block(BlockSz, Mac, Fragment) -> TotSz = byte_size(Mac) + erlang:iolist_size(Fragment) + 1, @@ -220,19 +211,6 @@ decipher(?AES_CBC, HashSz, CipherState, Fragment, Version, PaddingCheck) -> crypto:block_decrypt(aes_cbc256, Key, IV, T) end, CipherState, HashSz, Fragment, Version, PaddingCheck). -%%-------------------------------------------------------------------- --spec decipher_aead(cipher_enum(), #cipher_state{}, integer(), binary(), binary(), ssl_record:ssl_version()) -> - {binary(), #cipher_state{}} | #alert{}. -%% -%% Description: Decrypts the data and checks the associated data (AAD) MAC using -%% cipher described by cipher_enum() and updating the cipher state. -%% Use for suites that use authenticated encryption with associated data (AEAD) -%%------------------------------------------------------------------- -decipher_aead(?AES_GCM, CipherState, SeqNo, AAD, Fragment, Version) -> - aead_decipher(aes_gcm, CipherState, SeqNo, AAD, Fragment, Version); -decipher_aead(?CHACHA20_POLY1305, CipherState, SeqNo, AAD, Fragment, Version) -> - aead_decipher(chacha20_poly1305, CipherState, SeqNo, AAD, Fragment, Version). - block_decipher(Fun, #cipher_state{key=Key, iv=IV} = CipherState0, HashSz, Fragment, Version, PaddingCheck) -> try @@ -263,34 +241,6 @@ block_decipher(Fun, #cipher_state{key=Key, iv=IV} = CipherState0, ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC, decryption_failed) end. -aead_ciphertext_to_state(chacha20_poly1305, SeqNo, _IV, AAD0, Fragment, _Version) -> - CipherLen = size(Fragment) - 16, - <<CipherText:CipherLen/bytes, CipherTag:16/bytes>> = Fragment, - AAD = <<AAD0/binary, ?UINT16(CipherLen)>>, - Nonce = ?uint64(SeqNo), - {Nonce, AAD, CipherText, CipherTag}; -aead_ciphertext_to_state(_, _SeqNo, <<Salt:4/bytes, _/binary>>, AAD0, Fragment, _Version) -> - CipherLen = size(Fragment) - 24, - <<ExplicitNonce:8/bytes, CipherText:CipherLen/bytes, CipherTag:16/bytes>> = Fragment, - AAD = <<AAD0/binary, ?UINT16(CipherLen)>>, - Nonce = <<Salt/binary, ExplicitNonce/binary>>, - {Nonce, AAD, CipherText, CipherTag}. - -aead_decipher(Type, #cipher_state{key = Key, iv = IV} = CipherState, - SeqNo, AAD0, Fragment, Version) -> - try - {Nonce, AAD, CipherText, CipherTag} = aead_ciphertext_to_state(Type, SeqNo, IV, AAD0, Fragment, Version), - case crypto:block_decrypt(Type, Key, Nonce, {AAD, CipherText, CipherTag}) of - Content when is_binary(Content) -> - {Content, CipherState}; - _ -> - ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC, decryption_failed) - end - catch - _:_ -> - ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC, decryption_failed) - end. - %%-------------------------------------------------------------------- -spec suites(ssl_record:ssl_version()) -> [ssl_cipher_format:cipher_suite()]. %% @@ -1044,7 +994,7 @@ filter_suites_pubkey(ec, Ciphers, _, OtpCert) -> ec_ecdhe_suites(Ciphers)), filter_keyuse_suites(keyAgreement, Uses, CiphersSuites, ec_ecdh_suites(Ciphers)). -filter_suites_signature(rsa, Ciphers, {3, N}) when N >= 3 -> +filter_suites_signature(_, Ciphers, {3, N}) when N >= 3 -> Ciphers; filter_suites_signature(rsa, Ciphers, Version) -> (Ciphers -- ecdsa_signed_suites(Ciphers, Version)) -- dsa_signed_suites(Ciphers, Version); diff --git a/lib/ssl/src/ssl_cipher.hrl b/lib/ssl/src/ssl_cipher.hrl index 1febc52e43..5891f3a7cc 100644 --- a/lib/ssl/src/ssl_cipher.hrl +++ b/lib/ssl/src/ssl_cipher.hrl @@ -48,7 +48,8 @@ iv, key, state, - nonce + nonce, + tag_len }). %%% TLS_NULL_WITH_NULL_NULL is specified and is the initial state of a diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index 3eb3b0a980..2abc678ed9 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -627,8 +627,10 @@ init({call, From}, {start, {Opts, EmOpts}, Timeout}, catch throw:Error -> stop_and_reply(normal, {reply, From, {error, Error}}, State0) end; -init({call, From}, Msg, State, Connection) -> +init({call, From}, {new_user, _} = Msg, State, Connection) -> handle_call(Msg, From, ?FUNCTION_NAME, State, Connection); +init({call, From}, _Msg, _State, _Connection) -> + {keep_state_and_data, [{reply, From, {error, notsup_on_transport_accept_socket}}]}; init(_Type, _Event, _State, _Connection) -> {keep_state_and_data, [postpone]}. @@ -848,7 +850,9 @@ certify(internal, #certificate_request{} = CertRequest, role = client, ssl_options = #ssl_options{signature_algs = SupportedHashSigns}, negotiated_version = Version} = State0, Connection) -> - case ssl_handshake:select_hashsign(CertRequest, Cert, SupportedHashSigns, ssl:tls_version(Version)) of + case ssl_handshake:select_hashsign(CertRequest, Cert, + SupportedHashSigns, + ssl:tls_version(Version)) of #alert {} = Alert -> handle_own_alert(Alert, Version, ?FUNCTION_NAME, State0); NegotiatedHashSign -> diff --git a/lib/ssl/src/ssl_dh_groups.erl b/lib/ssl/src/ssl_dh_groups.erl new file mode 100644 index 0000000000..36c97ed13e --- /dev/null +++ b/lib/ssl/src/ssl_dh_groups.erl @@ -0,0 +1,443 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2007-2018. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +-module(ssl_dh_groups). + +-export([modp2048_generator/0, modp2048_prime/0, + ffdhe2048_generator/0, ffdhe2048_prime/0, + ffdhe3072_generator/0, ffdhe3072_prime/0, + ffdhe4096_generator/0, ffdhe4096_prime/0, + ffdhe6144_generator/0, ffdhe6144_prime/0, + ffdhe8192_generator/0, ffdhe8192_prime/0]). + +%% RFC3526 - 2048-bit MODP Group +%% This group is assigned id 14. +%% +%% This prime is: 2^2048 - 2^1984 - 1 + 2^64 * { [2^1918 pi] + 124476 } +%% +%% Its hexadecimal value is: +%% +%% FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 +%% 29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD +%% EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245 +%% E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED +%% EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D +%% C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F +%% 83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D +%% 670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B +%% E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9 +%% DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510 +%% 15728E5A 8AACAA68 FFFFFFFF FFFFFFFF +%% +%% The generator is: 2. +modp2048_generator() -> + 2. + +modp2048_prime() -> + P = "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1" + "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD" + "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245" + "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED" + "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D" + "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F" + "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D" + "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B" + "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9" + "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510" + "15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF", + list_to_integer(P, 16). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% RFC8446 - TLS 1.3 +%%% RFC7919 - Negotiated FFDHE for TLS +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% ffdhe2048 +%% --------- +%% The 2048-bit group has registry value 256 and is calculated from the +%% following formula: +%% +%% The modulus is: +%% +%% p = 2^2048 - 2^1984 + {[2^1918 * e] + 560316 } * 2^64 - 1 +%% +%% The hexadecimal representation of p is: +%% +%% FFFFFFFF FFFFFFFF ADF85458 A2BB4A9A AFDC5620 273D3CF1 +%% D8B9C583 CE2D3695 A9E13641 146433FB CC939DCE 249B3EF9 +%% 7D2FE363 630C75D8 F681B202 AEC4617A D3DF1ED5 D5FD6561 +%% 2433F51F 5F066ED0 85636555 3DED1AF3 B557135E 7F57C935 +%% 984F0C70 E0E68B77 E2A689DA F3EFE872 1DF158A1 36ADE735 +%% 30ACCA4F 483A797A BC0AB182 B324FB61 D108A94B B2C8E3FB +%% B96ADAB7 60D7F468 1D4F42A3 DE394DF4 AE56EDE7 6372BB19 +%% 0B07A7C8 EE0A6D70 9E02FCE1 CDF7E2EC C03404CD 28342F61 +%% 9172FE9C E98583FF 8E4F1232 EEF28183 C3FE3B1B 4C6FAD73 +%% 3BB5FCBC 2EC22005 C58EF183 7D1683B2 C6F34A26 C1B2EFFA +%% 886B4238 61285C97 FFFFFFFF FFFFFFFF +%% +%% The generator is: g = 2 +%% +%% The group size is: q = (p-1)/2 +%% +%% The estimated symmetric-equivalent strength of this group is 103 +%% bits. +ffdhe2048_generator() -> + 2. + +ffdhe2048_prime() -> + P = "FFFFFFFF" "FFFFFFFF" "ADF85458" "A2BB4A9A" "AFDC5620" "273D3CF1" + "D8B9C583" "CE2D3695" "A9E13641" "146433FB" "CC939DCE" "249B3EF9" + "7D2FE363" "630C75D8" "F681B202" "AEC4617A" "D3DF1ED5" "D5FD6561" + "2433F51F" "5F066ED0" "85636555" "3DED1AF3" "B557135E" "7F57C935" + "984F0C70" "E0E68B77" "E2A689DA" "F3EFE872" "1DF158A1" "36ADE735" + "30ACCA4F" "483A797A" "BC0AB182" "B324FB61" "D108A94B" "B2C8E3FB" + "B96ADAB7" "60D7F468" "1D4F42A3" "DE394DF4" "AE56EDE7" "6372BB19" + "0B07A7C8" "EE0A6D70" "9E02FCE1" "CDF7E2EC" "C03404CD" "28342F61" + "9172FE9C" "E98583FF" "8E4F1232" "EEF28183" "C3FE3B1B" "4C6FAD73" + "3BB5FCBC" "2EC22005" "C58EF183" "7D1683B2" "C6F34A26" "C1B2EFFA" + "886B4238" "61285C97" "FFFFFFFF" "FFFFFFFF", + list_to_integer(P, 16). + + +%% ffdhe3072 +%% --------- +%% The 3072-bit prime has registry value 257 and is calculated from the +%% following formula: +%% +%% The modulus is: +%% +%% p = 2^3072 - 2^3008 + {[2^2942 * e] + 2625351} * 2^64 - 1 +%% +%% The hexadecimal representation of p is: +%% +%% FFFFFFFF FFFFFFFF ADF85458 A2BB4A9A AFDC5620 273D3CF1 +%% D8B9C583 CE2D3695 A9E13641 146433FB CC939DCE 249B3EF9 +%% 7D2FE363 630C75D8 F681B202 AEC4617A D3DF1ED5 D5FD6561 +%% 2433F51F 5F066ED0 85636555 3DED1AF3 B557135E 7F57C935 +%% 984F0C70 E0E68B77 E2A689DA F3EFE872 1DF158A1 36ADE735 +%% 30ACCA4F 483A797A BC0AB182 B324FB61 D108A94B B2C8E3FB +%% B96ADAB7 60D7F468 1D4F42A3 DE394DF4 AE56EDE7 6372BB19 +%% 0B07A7C8 EE0A6D70 9E02FCE1 CDF7E2EC C03404CD 28342F61 +%% 9172FE9C E98583FF 8E4F1232 EEF28183 C3FE3B1B 4C6FAD73 +%% 3BB5FCBC 2EC22005 C58EF183 7D1683B2 C6F34A26 C1B2EFFA +%% 886B4238 611FCFDC DE355B3B 6519035B BC34F4DE F99C0238 +%% 61B46FC9 D6E6C907 7AD91D26 91F7F7EE 598CB0FA C186D91C +%% AEFE1309 85139270 B4130C93 BC437944 F4FD4452 E2D74DD3 +%% 64F2E21E 71F54BFF 5CAE82AB 9C9DF69E E86D2BC5 22363A0D +%% ABC52197 9B0DEADA 1DBF9A42 D5C4484E 0ABCD06B FA53DDEF +%% 3C1B20EE 3FD59D7C 25E41D2B 66C62E37 FFFFFFFF FFFFFFFF +%% +%% The generator is: g = 2 +%% +%% The group size is: q = (p-1)/2 +%% +%% The estimated symmetric-equivalent strength of this group is 125 +%% bits. +ffdhe3072_generator() -> + 2. + +ffdhe3072_prime() -> + P = "FFFFFFFF" "FFFFFFFF" "ADF85458" "A2BB4A9A" "AFDC5620" "273D3CF1" + "D8B9C583" "CE2D3695" "A9E13641" "146433FB" "CC939DCE" "249B3EF9" + "7D2FE363" "630C75D8" "F681B202" "AEC4617A" "D3DF1ED5" "D5FD6561" + "2433F51F" "5F066ED0" "85636555" "3DED1AF3" "B557135E" "7F57C935" + "984F0C70" "E0E68B77" "E2A689DA" "F3EFE872" "1DF158A1" "36ADE735" + "30ACCA4F" "483A797A" "BC0AB182" "B324FB61" "D108A94B" "B2C8E3FB" + "B96ADAB7" "60D7F468" "1D4F42A3" "DE394DF4" "AE56EDE7" "6372BB19" + "0B07A7C8" "EE0A6D70" "9E02FCE1" "CDF7E2EC" "C03404CD" "28342F61" + "9172FE9C" "E98583FF" "8E4F1232" "EEF28183" "C3FE3B1B" "4C6FAD73" + "3BB5FCBC" "2EC22005" "C58EF183" "7D1683B2" "C6F34A26" "C1B2EFFA" + "886B4238" "611FCFDC" "DE355B3B" "6519035B" "BC34F4DE" "F99C0238" + "61B46FC9" "D6E6C907" "7AD91D26" "91F7F7EE" "598CB0FA" "C186D91C" + "AEFE1309" "85139270" "B4130C93" "BC437944" "F4FD4452" "E2D74DD3" + "64F2E21E" "71F54BFF" "5CAE82AB" "9C9DF69E" "E86D2BC5" "22363A0D" + "ABC52197" "9B0DEADA" "1DBF9A42" "D5C4484E" "0ABCD06B" "FA53DDEF" + "3C1B20EE" "3FD59D7C" "25E41D2B" "66C62E37" "FFFFFFFF" "FFFFFFFF", + list_to_integer(P, 16). + + +%% ffdhe4096 +%% --------- +%% The 4096-bit group has registry value 258 and is calculated from the +%% following formula: +%% +%% The modulus is: +%% +%% p = 2^4096 - 2^4032 + {[2^3966 * e] + 5736041} * 2^64 - 1 +%% +%% The hexadecimal representation of p is: +%% +%% FFFFFFFF FFFFFFFF ADF85458 A2BB4A9A AFDC5620 273D3CF1 +%% D8B9C583 CE2D3695 A9E13641 146433FB CC939DCE 249B3EF9 +%% 7D2FE363 630C75D8 F681B202 AEC4617A D3DF1ED5 D5FD6561 +%% 2433F51F 5F066ED0 85636555 3DED1AF3 B557135E 7F57C935 +%% 984F0C70 E0E68B77 E2A689DA F3EFE872 1DF158A1 36ADE735 +%% 30ACCA4F 483A797A BC0AB182 B324FB61 D108A94B B2C8E3FB +%% B96ADAB7 60D7F468 1D4F42A3 DE394DF4 AE56EDE7 6372BB19 +%% 0B07A7C8 EE0A6D70 9E02FCE1 CDF7E2EC C03404CD 28342F61 +%% 9172FE9C E98583FF 8E4F1232 EEF28183 C3FE3B1B 4C6FAD73 +%% 3BB5FCBC 2EC22005 C58EF183 7D1683B2 C6F34A26 C1B2EFFA +%% 886B4238 611FCFDC DE355B3B 6519035B BC34F4DE F99C0238 +%% 61B46FC9 D6E6C907 7AD91D26 91F7F7EE 598CB0FA C186D91C +%% AEFE1309 85139270 B4130C93 BC437944 F4FD4452 E2D74DD3 +%% 64F2E21E 71F54BFF 5CAE82AB 9C9DF69E E86D2BC5 22363A0D +%% ABC52197 9B0DEADA 1DBF9A42 D5C4484E 0ABCD06B FA53DDEF +%% 3C1B20EE 3FD59D7C 25E41D2B 669E1EF1 6E6F52C3 164DF4FB +%% 7930E9E4 E58857B6 AC7D5F42 D69F6D18 7763CF1D 55034004 +%% 87F55BA5 7E31CC7A 7135C886 EFB4318A ED6A1E01 2D9E6832 +%% A907600A 918130C4 6DC778F9 71AD0038 092999A3 33CB8B7A +%% 1A1DB93D 7140003C 2A4ECEA9 F98D0ACC 0A8291CD CEC97DCF +%% 8EC9B55A 7F88A46B 4DB5A851 F44182E1 C68A007E 5E655F6A +%% FFFFFFFF FFFFFFFF +%% +%% The generator is: g = 2 +%% +%% The group size is: q = (p-1)/2 +%% +%% The estimated symmetric-equivalent strength of this group is 150 +%% bits. +ffdhe4096_generator() -> + 2. + +ffdhe4096_prime() -> + P = "FFFFFFFF" "FFFFFFFF" "ADF85458" "A2BB4A9A" "AFDC5620" "273D3CF1" + "D8B9C583" "CE2D3695" "A9E13641" "146433FB" "CC939DCE" "249B3EF9" + "7D2FE363" "630C75D8" "F681B202" "AEC4617A" "D3DF1ED5" "D5FD6561" + "2433F51F" "5F066ED0" "85636555" "3DED1AF3" "B557135E" "7F57C935" + "984F0C70" "E0E68B77" "E2A689DA" "F3EFE872" "1DF158A1" "36ADE735" + "30ACCA4F" "483A797A" "BC0AB182" "B324FB61" "D108A94B" "B2C8E3FB" + "B96ADAB7" "60D7F468" "1D4F42A3" "DE394DF4" "AE56EDE7" "6372BB19" + "0B07A7C8" "EE0A6D70" "9E02FCE1" "CDF7E2EC" "C03404CD" "28342F61" + "9172FE9C" "E98583FF" "8E4F1232" "EEF28183" "C3FE3B1B" "4C6FAD73" + "3BB5FCBC" "2EC22005" "C58EF183" "7D1683B2" "C6F34A26" "C1B2EFFA" + "886B4238" "611FCFDC" "DE355B3B" "6519035B" "BC34F4DE" "F99C0238" + "61B46FC9" "D6E6C907" "7AD91D26" "91F7F7EE" "598CB0FA" "C186D91C" + "AEFE1309" "85139270" "B4130C93" "BC437944" "F4FD4452" "E2D74DD3" + "64F2E21E" "71F54BFF" "5CAE82AB" "9C9DF69E" "E86D2BC5" "22363A0D" + "ABC52197" "9B0DEADA" "1DBF9A42" "D5C4484E" "0ABCD06B" "FA53DDEF" + "3C1B20EE" "3FD59D7C" "25E41D2B" "669E1EF1" "6E6F52C3" "164DF4FB" + "7930E9E4" "E58857B6" "AC7D5F42" "D69F6D18" "7763CF1D" "55034004" + "87F55BA5" "7E31CC7A" "7135C886" "EFB4318A" "ED6A1E01" "2D9E6832" + "A907600A" "918130C4" "6DC778F9" "71AD0038" "092999A3" "33CB8B7A" + "1A1DB93D" "7140003C" "2A4ECEA9" "F98D0ACC" "0A8291CD" "CEC97DCF" + "8EC9B55A" "7F88A46B" "4DB5A851" "F44182E1" "C68A007E" "5E655F6A" + "FFFFFFFF" "FFFFFFFF", + list_to_integer(P, 16). + + +%% ffdhe6144 +%% --------- +%% The 6144-bit group has registry value 259 and is calculated from the +%% following formula: +%% +%% The modulus is: +%% +%% p = 2^6144 - 2^6080 + {[2^6014 * e] + 15705020} * 2^64 - 1 +%% +%% The hexadecimal representation of p is: +%% +%% FFFFFFFF FFFFFFFF ADF85458 A2BB4A9A AFDC5620 273D3CF1 +%% D8B9C583 CE2D3695 A9E13641 146433FB CC939DCE 249B3EF9 +%% 7D2FE363 630C75D8 F681B202 AEC4617A D3DF1ED5 D5FD6561 +%% 2433F51F 5F066ED0 85636555 3DED1AF3 B557135E 7F57C935 +%% 984F0C70 E0E68B77 E2A689DA F3EFE872 1DF158A1 36ADE735 +%% 30ACCA4F 483A797A BC0AB182 B324FB61 D108A94B B2C8E3FB +%% B96ADAB7 60D7F468 1D4F42A3 DE394DF4 AE56EDE7 6372BB19 +%% 0B07A7C8 EE0A6D70 9E02FCE1 CDF7E2EC C03404CD 28342F61 +%% 9172FE9C E98583FF 8E4F1232 EEF28183 C3FE3B1B 4C6FAD73 +%% 3BB5FCBC 2EC22005 C58EF183 7D1683B2 C6F34A26 C1B2EFFA +%% 886B4238 611FCFDC DE355B3B 6519035B BC34F4DE F99C0238 +%% 61B46FC9 D6E6C907 7AD91D26 91F7F7EE 598CB0FA C186D91C +%% AEFE1309 85139270 B4130C93 BC437944 F4FD4452 E2D74DD3 +%% 64F2E21E 71F54BFF 5CAE82AB 9C9DF69E E86D2BC5 22363A0D +%% ABC52197 9B0DEADA 1DBF9A42 D5C4484E 0ABCD06B FA53DDEF +%% 3C1B20EE 3FD59D7C 25E41D2B 669E1EF1 6E6F52C3 164DF4FB +%% 7930E9E4 E58857B6 AC7D5F42 D69F6D18 7763CF1D 55034004 +%% 87F55BA5 7E31CC7A 7135C886 EFB4318A ED6A1E01 2D9E6832 +%% A907600A 918130C4 6DC778F9 71AD0038 092999A3 33CB8B7A +%% 1A1DB93D 7140003C 2A4ECEA9 F98D0ACC 0A8291CD CEC97DCF +%% 8EC9B55A 7F88A46B 4DB5A851 F44182E1 C68A007E 5E0DD902 +%% 0BFD64B6 45036C7A 4E677D2C 38532A3A 23BA4442 CAF53EA6 +%% 3BB45432 9B7624C8 917BDD64 B1C0FD4C B38E8C33 4C701C3A +%% CDAD0657 FCCFEC71 9B1F5C3E 4E46041F 388147FB 4CFDB477 +%% A52471F7 A9A96910 B855322E DB6340D8 A00EF092 350511E3 +%% 0ABEC1FF F9E3A26E 7FB29F8C 183023C3 587E38DA 0077D9B4 +%% 763E4E4B 94B2BBC1 94C6651E 77CAF992 EEAAC023 2A281BF6 +%% B3A739C1 22611682 0AE8DB58 47A67CBE F9C9091B 462D538C +%% D72B0374 6AE77F5E 62292C31 1562A846 505DC82D B854338A +%% E49F5235 C95B9117 8CCF2DD5 CACEF403 EC9D1810 C6272B04 +%% 5B3B71F9 DC6B80D6 3FDD4A8E 9ADB1E69 62A69526 D43161C1 +%% A41D570D 7938DAD4 A40E329C D0E40E65 FFFFFFFF FFFFFFFF +%% +%% The generator is: g = 2 +%% +%% The group size is: q = (p-1)/2 +%% +%% The estimated symmetric-equivalent strength of this group is 175 +%% bits. +ffdhe6144_generator() -> + 2. + +ffdhe6144_prime() -> + P = "FFFFFFFF" "FFFFFFFF" "ADF85458" "A2BB4A9A" "AFDC5620" "273D3CF1" + "D8B9C583" "CE2D3695" "A9E13641" "146433FB" "CC939DCE" "249B3EF9" + "7D2FE363" "630C75D8" "F681B202" "AEC4617A" "D3DF1ED5" "D5FD6561" + "2433F51F" "5F066ED0" "85636555" "3DED1AF3" "B557135E" "7F57C935" + "984F0C70" "E0E68B77" "E2A689DA" "F3EFE872" "1DF158A1" "36ADE735" + "30ACCA4F" "483A797A" "BC0AB182" "B324FB61" "D108A94B" "B2C8E3FB" + "B96ADAB7" "60D7F468" "1D4F42A3" "DE394DF4" "AE56EDE7" "6372BB19" + "0B07A7C8" "EE0A6D70" "9E02FCE1" "CDF7E2EC" "C03404CD" "28342F61" + "9172FE9C" "E98583FF" "8E4F1232" "EEF28183" "C3FE3B1B" "4C6FAD73" + "3BB5FCBC" "2EC22005" "C58EF183" "7D1683B2" "C6F34A26" "C1B2EFFA" + "886B4238" "611FCFDC" "DE355B3B" "6519035B" "BC34F4DE" "F99C0238" + "61B46FC9" "D6E6C907" "7AD91D26" "91F7F7EE" "598CB0FA" "C186D91C" + "AEFE1309" "85139270" "B4130C93" "BC437944" "F4FD4452" "E2D74DD3" + "64F2E21E" "71F54BFF" "5CAE82AB" "9C9DF69E" "E86D2BC5" "22363A0D" + "ABC52197" "9B0DEADA" "1DBF9A42" "D5C4484E" "0ABCD06B" "FA53DDEF" + "3C1B20EE" "3FD59D7C" "25E41D2B" "669E1EF1" "6E6F52C3" "164DF4FB" + "7930E9E4" "E58857B6" "AC7D5F42" "D69F6D18" "7763CF1D" "55034004" + "87F55BA5" "7E31CC7A" "7135C886" "EFB4318A" "ED6A1E01" "2D9E6832" + "A907600A" "918130C4" "6DC778F9" "71AD0038" "092999A3" "33CB8B7A" + "1A1DB93D" "7140003C" "2A4ECEA9" "F98D0ACC" "0A8291CD" "CEC97DCF" + "8EC9B55A" "7F88A46B" "4DB5A851" "F44182E1" "C68A007E" "5E0DD902" + "0BFD64B6" "45036C7A" "4E677D2C" "38532A3A" "23BA4442" "CAF53EA6" + "3BB45432" "9B7624C8" "917BDD64" "B1C0FD4C" "B38E8C33" "4C701C3A" + "CDAD0657" "FCCFEC71" "9B1F5C3E" "4E46041F" "388147FB" "4CFDB477" + "A52471F7" "A9A96910" "B855322E" "DB6340D8" "A00EF092" "350511E3" + "0ABEC1FF" "F9E3A26E" "7FB29F8C" "183023C3" "587E38DA" "0077D9B4" + "763E4E4B" "94B2BBC1" "94C6651E" "77CAF992" "EEAAC023" "2A281BF6" + "B3A739C1" "22611682" "0AE8DB58" "47A67CBE" "F9C9091B" "462D538C" + "D72B0374" "6AE77F5E" "62292C31" "1562A846" "505DC82D" "B854338A" + "E49F5235" "C95B9117" "8CCF2DD5" "CACEF403" "EC9D1810" "C6272B04" + "5B3B71F9" "DC6B80D6" "3FDD4A8E" "9ADB1E69" "62A69526" "D43161C1" + "A41D570D" "7938DAD4" "A40E329C" "D0E40E65" "FFFFFFFF" "FFFFFFFF", + list_to_integer(P, 16). + + +%% ffdhe8192 +%% --------- +%% The 8192-bit group has registry value 260 and is calculated from the +%% following formula: +%% +%% The modulus is: +%% +%% p = 2^8192 - 2^8128 + {[2^8062 * e] + 10965728} * 2^64 - 1 +%% +%% The hexadecimal representation of p is: +%% +%% FFFFFFFF FFFFFFFF ADF85458 A2BB4A9A AFDC5620 273D3CF1 +%% D8B9C583 CE2D3695 A9E13641 146433FB CC939DCE 249B3EF9 +%% 7D2FE363 630C75D8 F681B202 AEC4617A D3DF1ED5 D5FD6561 +%% 2433F51F 5F066ED0 85636555 3DED1AF3 B557135E 7F57C935 +%% 984F0C70 E0E68B77 E2A689DA F3EFE872 1DF158A1 36ADE735 +%% 30ACCA4F 483A797A BC0AB182 B324FB61 D108A94B B2C8E3FB +%% B96ADAB7 60D7F468 1D4F42A3 DE394DF4 AE56EDE7 6372BB19 +%% 0B07A7C8 EE0A6D70 9E02FCE1 CDF7E2EC C03404CD 28342F61 +%% 9172FE9C E98583FF 8E4F1232 EEF28183 C3FE3B1B 4C6FAD73 +%% 3BB5FCBC 2EC22005 C58EF183 7D1683B2 C6F34A26 C1B2EFFA +%% 886B4238 611FCFDC DE355B3B 6519035B BC34F4DE F99C0238 +%% 61B46FC9 D6E6C907 7AD91D26 91F7F7EE 598CB0FA C186D91C +%% AEFE1309 85139270 B4130C93 BC437944 F4FD4452 E2D74DD3 +%% 64F2E21E 71F54BFF 5CAE82AB 9C9DF69E E86D2BC5 22363A0D +%% ABC52197 9B0DEADA 1DBF9A42 D5C4484E 0ABCD06B FA53DDEF +%% 3C1B20EE 3FD59D7C 25E41D2B 669E1EF1 6E6F52C3 164DF4FB +%% 7930E9E4 E58857B6 AC7D5F42 D69F6D18 7763CF1D 55034004 +%% 87F55BA5 7E31CC7A 7135C886 EFB4318A ED6A1E01 2D9E6832 +%% A907600A 918130C4 6DC778F9 71AD0038 092999A3 33CB8B7A +%% 1A1DB93D 7140003C 2A4ECEA9 F98D0ACC 0A8291CD CEC97DCF +%% 8EC9B55A 7F88A46B 4DB5A851 F44182E1 C68A007E 5E0DD902 +%% 0BFD64B6 45036C7A 4E677D2C 38532A3A 23BA4442 CAF53EA6 +%% 3BB45432 9B7624C8 917BDD64 B1C0FD4C B38E8C33 4C701C3A +%% CDAD0657 FCCFEC71 9B1F5C3E 4E46041F 388147FB 4CFDB477 +%% A52471F7 A9A96910 B855322E DB6340D8 A00EF092 350511E3 +%% 0ABEC1FF F9E3A26E 7FB29F8C 183023C3 587E38DA 0077D9B4 +%% 763E4E4B 94B2BBC1 94C6651E 77CAF992 EEAAC023 2A281BF6 +%% B3A739C1 22611682 0AE8DB58 47A67CBE F9C9091B 462D538C +%% D72B0374 6AE77F5E 62292C31 1562A846 505DC82D B854338A +%% E49F5235 C95B9117 8CCF2DD5 CACEF403 EC9D1810 C6272B04 +%% 5B3B71F9 DC6B80D6 3FDD4A8E 9ADB1E69 62A69526 D43161C1 +%% A41D570D 7938DAD4 A40E329C CFF46AAA 36AD004C F600C838 +%% 1E425A31 D951AE64 FDB23FCE C9509D43 687FEB69 EDD1CC5E +%% 0B8CC3BD F64B10EF 86B63142 A3AB8829 555B2F74 7C932665 +%% CB2C0F1C C01BD702 29388839 D2AF05E4 54504AC7 8B758282 +%% 2846C0BA 35C35F5C 59160CC0 46FD8251 541FC68C 9C86B022 +%% BB709987 6A460E74 51A8A931 09703FEE 1C217E6C 3826E52C +%% 51AA691E 0E423CFC 99E9E316 50C1217B 624816CD AD9A95F9 +%% D5B80194 88D9C0A0 A1FE3075 A577E231 83F81D4A 3F2FA457 +%% 1EFC8CE0 BA8A4FE8 B6855DFE 72B0A66E DED2FBAB FBE58A30 +%% FAFABE1C 5D71A87E 2F741EF8 C1FE86FE A6BBFDE5 30677F0D +%% 97D11D49 F7A8443D 0822E506 A9F4614E 011E2A94 838FF88C +%% D68C8BB7 C5C6424C FFFFFFFF FFFFFFFF +%% +%% The generator is: g = 2 +%% +%% The group size is: q = (p-1)/2 +%% +%% The estimated symmetric-equivalent strength of this group is 192 +%% bits. +ffdhe8192_generator() -> + 2. + +ffdhe8192_prime() -> + P = "FFFFFFFF" "FFFFFFFF" "ADF85458" "A2BB4A9A" "AFDC5620" "273D3CF1" + "D8B9C583" "CE2D3695" "A9E13641" "146433FB" "CC939DCE" "249B3EF9" + "7D2FE363" "630C75D8" "F681B202" "AEC4617A" "D3DF1ED5" "D5FD6561" + "2433F51F" "5F066ED0" "85636555" "3DED1AF3" "B557135E" "7F57C935" + "984F0C70" "E0E68B77" "E2A689DA" "F3EFE872" "1DF158A1" "36ADE735" + "30ACCA4F" "483A797A" "BC0AB182" "B324FB61" "D108A94B" "B2C8E3FB" + "B96ADAB7" "60D7F468" "1D4F42A3" "DE394DF4" "AE56EDE7" "6372BB19" + "0B07A7C8" "EE0A6D70" "9E02FCE1" "CDF7E2EC" "C03404CD" "28342F61" + "9172FE9C" "E98583FF" "8E4F1232" "EEF28183" "C3FE3B1B" "4C6FAD73" + "3BB5FCBC" "2EC22005" "C58EF183" "7D1683B2" "C6F34A26" "C1B2EFFA" + "886B4238" "611FCFDC" "DE355B3B" "6519035B" "BC34F4DE" "F99C0238" + "61B46FC9" "D6E6C907" "7AD91D26" "91F7F7EE" "598CB0FA" "C186D91C" + "AEFE1309" "85139270" "B4130C93" "BC437944" "F4FD4452" "E2D74DD3" + "64F2E21E" "71F54BFF" "5CAE82AB" "9C9DF69E" "E86D2BC5" "22363A0D" + "ABC52197" "9B0DEADA" "1DBF9A42" "D5C4484E" "0ABCD06B" "FA53DDEF" + "3C1B20EE" "3FD59D7C" "25E41D2B" "669E1EF1" "6E6F52C3" "164DF4FB" + "7930E9E4" "E58857B6" "AC7D5F42" "D69F6D18" "7763CF1D" "55034004" + "87F55BA5" "7E31CC7A" "7135C886" "EFB4318A" "ED6A1E01" "2D9E6832" + "A907600A" "918130C4" "6DC778F9" "71AD0038" "092999A3" "33CB8B7A" + "1A1DB93D" "7140003C" "2A4ECEA9" "F98D0ACC" "0A8291CD" "CEC97DCF" + "8EC9B55A" "7F88A46B" "4DB5A851" "F44182E1" "C68A007E" "5E0DD902" + "0BFD64B6" "45036C7A" "4E677D2C" "38532A3A" "23BA4442" "CAF53EA6" + "3BB45432" "9B7624C8" "917BDD64" "B1C0FD4C" "B38E8C33" "4C701C3A" + "CDAD0657" "FCCFEC71" "9B1F5C3E" "4E46041F" "388147FB" "4CFDB477" + "A52471F7" "A9A96910" "B855322E" "DB6340D8" "A00EF092" "350511E3" + "0ABEC1FF" "F9E3A26E" "7FB29F8C" "183023C3" "587E38DA" "0077D9B4" + "763E4E4B" "94B2BBC1" "94C6651E" "77CAF992" "EEAAC023" "2A281BF6" + "B3A739C1" "22611682" "0AE8DB58" "47A67CBE" "F9C9091B" "462D538C" + "D72B0374" "6AE77F5E" "62292C31" "1562A846" "505DC82D" "B854338A" + "E49F5235" "C95B9117" "8CCF2DD5" "CACEF403" "EC9D1810" "C6272B04" + "5B3B71F9" "DC6B80D6" "3FDD4A8E" "9ADB1E69" "62A69526" "D43161C1" + "A41D570D" "7938DAD4" "A40E329C" "CFF46AAA" "36AD004C" "F600C838" + "1E425A31" "D951AE64" "FDB23FCE" "C9509D43" "687FEB69" "EDD1CC5E" + "0B8CC3BD" "F64B10EF" "86B63142" "A3AB8829" "555B2F74" "7C932665" + "CB2C0F1C" "C01BD702" "29388839" "D2AF05E4" "54504AC7" "8B758282" + "2846C0BA" "35C35F5C" "59160CC0" "46FD8251" "541FC68C" "9C86B022" + "BB709987" "6A460E74" "51A8A931" "09703FEE" "1C217E6C" "3826E52C" + "51AA691E" "0E423CFC" "99E9E316" "50C1217B" "624816CD" "AD9A95F9" + "D5B80194" "88D9C0A0" "A1FE3075" "A577E231" "83F81D4A" "3F2FA457" + "1EFC8CE0" "BA8A4FE8" "B6855DFE" "72B0A66E" "DED2FBAB" "FBE58A30" + "FAFABE1C" "5D71A87E" "2F741EF8" "C1FE86FE" "A6BBFDE5" "30677F0D" + "97D11D49" "F7A8443D" "0822E506" "A9F4614E" "011E2A94" "838FF88C" + "D68C8BB7" "C5C6424C" "FFFFFFFF" "FFFFFFFF", + list_to_integer(P, 16). diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index ba0b670091..da2e92a76b 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -60,7 +60,7 @@ -export([encode_handshake/2, encode_hello_extensions/1, encode_extensions/1, encode_extensions/2, encode_client_protocol_negotiation/2, encode_protocols_advertised_on_server/1]). %% Decode --export([decode_handshake/3, decode_vector/1, decode_hello_extensions/3, decode_extensions/1, +-export([decode_handshake/3, decode_vector/1, decode_hello_extensions/3, decode_extensions/2, decode_server_key/3, decode_client_key/3, decode_suites/2 ]). @@ -75,7 +75,7 @@ handle_client_hello_extensions/9, %% Returns server hello extensions handle_server_hello_extensions/9, select_curve/2, select_curve/3, select_hashsign/4, select_hashsign/5, - select_hashsign_algs/3 + select_hashsign_algs/3, empty_extensions/2 ]). %%==================================================================== @@ -620,6 +620,14 @@ encode_extensions([#elliptic_curves{elliptic_curve_list = EllipticCurves} | Rest Len = ListLen + 2, encode_extensions(Rest, <<?UINT16(?ELLIPTIC_CURVES_EXT), ?UINT16(Len), ?UINT16(ListLen), EllipticCurveList/binary, Acc/binary>>); +encode_extensions([#supported_groups{supported_groups = SupportedGroups} | Rest], Acc) -> + + SupportedGroupList = << <<(tls_v1:group_to_enum(X)):16>> || X <- SupportedGroups>>, + ListLen = byte_size(SupportedGroupList), + Len = ListLen + 2, + encode_extensions(Rest, <<?UINT16(?ELLIPTIC_CURVES_EXT), + ?UINT16(Len), ?UINT16(ListLen), + SupportedGroupList/binary, Acc/binary>>); encode_extensions([#ec_point_formats{ec_point_format_list = ECPointFormats} | Rest], Acc) -> ECPointFormatList = list_to_binary(ECPointFormats), ListLen = byte_size(ECPointFormatList), @@ -638,7 +646,15 @@ encode_extensions([#hash_sign_algos{hash_sign_algos = HashSignAlgos} | Rest], Ac Len = ListLen + 2, encode_extensions(Rest, <<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len), ?UINT16(ListLen), SignAlgoList/binary, Acc/binary>>); -encode_extensions([#signature_scheme_list{ +encode_extensions([#signature_algorithms{ + signature_scheme_list = SignatureSchemes} | Rest], Acc) -> + SignSchemeList = << <<(ssl_cipher:signature_scheme(SignatureScheme)):16 >> || + SignatureScheme <- SignatureSchemes >>, + ListLen = byte_size(SignSchemeList), + Len = ListLen + 2, + encode_extensions(Rest, <<?UINT16(?SIGNATURE_ALGORITHMS_EXT), + ?UINT16(Len), ?UINT16(ListLen), SignSchemeList/binary, Acc/binary>>); +encode_extensions([#signature_algorithms_cert{ signature_scheme_list = SignatureSchemes} | Rest], Acc) -> SignSchemeList = << <<(ssl_cipher:signature_scheme(SignatureScheme)):16 >> || SignatureScheme <- SignatureSchemes >>, @@ -703,7 +719,7 @@ decode_handshake(Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32 session_id = Session_ID, cipher_suite = Cipher_suite, compression_method = Comp_method, - extensions = empty_hello_extensions(Version, server)}; + extensions = empty_extensions(Version, server_hello)}; decode_handshake(Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, ?BYTE(SID_length), Session_ID:SID_length/binary, @@ -772,15 +788,20 @@ decode_vector(<<?UINT16(Len), Vector:Len/binary>>) -> %% Description: Decodes TLS hello extensions %%-------------------------------------------------------------------- decode_hello_extensions(Extensions, Version, Role) -> - decode_extensions(Extensions, empty_hello_extensions(Version, Role)). + MessageType = + case Role of + client -> client_hello; + server -> server_hello + end, + decode_extensions(Extensions, Version, empty_extensions(Version, MessageType)). %%-------------------------------------------------------------------- --spec decode_extensions(binary()) -> map(). +-spec decode_extensions(binary(),tuple()) -> map(). %% %% Description: Decodes TLS hello extensions %%-------------------------------------------------------------------- -decode_extensions(Extensions) -> - decode_extensions(Extensions, empty_extensions()). +decode_extensions(Extensions, Version) -> + decode_extensions(Extensions, Version, empty_extensions()). %%-------------------------------------------------------------------- -spec decode_server_key(binary(), ssl_cipher_format:key_algo(), ssl_record:ssl_version()) -> @@ -983,43 +1004,72 @@ premaster_secret(EncSecret, #'RSAPrivateKey'{} = RSAPrivateKey) -> %%==================================================================== %% Extensions handling %%==================================================================== -client_hello_extensions(Version, CipherSuites, - #ssl_options{signature_algs = SupportedHashSigns, - signature_algs_cert = SignatureSchemes, - eccs = SupportedECCs, - versions = Versions} = SslOpts, ConnectionStates, Renegotiation) -> - {EcPointFormats, EllipticCurves} = - case advertises_ec_ciphers(lists:map(fun ssl_cipher_format:suite_definition/1, CipherSuites)) of - true -> - client_ecc_extensions(SupportedECCs); - false -> - {undefined, undefined} - end, +client_hello_extensions(Version, CipherSuites, SslOpts, ConnectionStates, Renegotiation) -> + HelloExtensions0 = add_tls12_extensions(Version, SslOpts, ConnectionStates, Renegotiation), + HelloExtensions1 = add_common_extensions(Version, HelloExtensions0, CipherSuites, SslOpts), + maybe_add_tls13_extensions(Version, HelloExtensions1, SslOpts). + + +add_tls12_extensions(Version, + #ssl_options{signature_algs = SupportedHashSigns} = SslOpts, + ConnectionStates, + Renegotiation) -> SRP = srp_user(SslOpts), + #{renegotiation_info => renegotiation_info(tls_record, client, + ConnectionStates, Renegotiation), + srp => SRP, + signature_algs => available_signature_algs(SupportedHashSigns, Version), + alpn => encode_alpn(SslOpts#ssl_options.alpn_advertised_protocols, Renegotiation), + next_protocol_negotiation => + encode_client_protocol_negotiation(SslOpts#ssl_options.next_protocol_selector, + Renegotiation), + sni => sni(SslOpts#ssl_options.server_name_indication) + }. - HelloExtensions = #{renegotiation_info => renegotiation_info(tls_record, client, - ConnectionStates, Renegotiation), - srp => SRP, - signature_algs => available_signature_algs(SupportedHashSigns, Version), - ec_point_formats => EcPointFormats, - elliptic_curves => EllipticCurves, - alpn => encode_alpn(SslOpts#ssl_options.alpn_advertised_protocols, Renegotiation), - next_protocol_negotiation => - encode_client_protocol_negotiation(SslOpts#ssl_options.next_protocol_selector, - Renegotiation), - sni => sni(SslOpts#ssl_options.server_name_indication) - }, - - %% Add "supported_versions" extension if TLS 1.3 - case Version of - {3,4} -> - HelloExtensions#{client_hello_versions => - #client_hello_versions{versions = Versions}, - signature_algs_cert => - #signature_scheme_list{signature_scheme_list = SignatureSchemes}}; - _Else -> - HelloExtensions - end. + +add_common_extensions({3,4}, + HelloExtensions, + _CipherSuites, + #ssl_options{eccs = SupportedECCs, + supported_groups = Groups}) -> + {EcPointFormats, _} = + client_ecc_extensions(SupportedECCs), + HelloExtensions#{ec_point_formats => EcPointFormats, + elliptic_curves => Groups}; + +add_common_extensions(_Version, + HelloExtensions, + CipherSuites, + #ssl_options{eccs = SupportedECCs}) -> + + {EcPointFormats, EllipticCurves} = + case advertises_ec_ciphers( + lists:map(fun ssl_cipher_format:suite_definition/1, + CipherSuites)) of + true -> + client_ecc_extensions(SupportedECCs); + false -> + {undefined, undefined} + end, + HelloExtensions#{ec_point_formats => EcPointFormats, + elliptic_curves => EllipticCurves}. + + +maybe_add_tls13_extensions({3,4}, + HelloExtensions, + #ssl_options{signature_algs_cert = SignatureSchemes, + versions = SupportedVersions}) -> + HelloExtensions#{client_hello_versions => + #client_hello_versions{versions = SupportedVersions}, + signature_algs_cert => + signature_algs_cert(SignatureSchemes)}; +maybe_add_tls13_extensions(_, HelloExtensions, _) -> + HelloExtensions. + +signature_algs_cert(undefined) -> + undefined; +signature_algs_cert(SignatureSchemes) -> + #signature_algorithms_cert{signature_scheme_list = SignatureSchemes}. handle_client_hello_extensions(RecordCB, Random, ClientCipherSuites, Exts, Version, @@ -1034,7 +1084,7 @@ handle_client_hello_extensions(RecordCB, Random, ClientCipherSuites, ClientCipherSuites, Compression, ConnectionStates0, Renegotiation, SecureRenegotation), - Empty = empty_hello_extensions(Version, client), + Empty = empty_extensions(Version, server_hello), ServerHelloExtensions = Empty#{renegotiation_info => renegotiation_info(RecordCB, server, ConnectionStates, Renegotiation), ec_point_formats => server_ecc_extension(Version, maps:get(ec_point_formats, Exts, undefined)) @@ -1128,26 +1178,50 @@ select_hashsign(_, _, KeyExAlgo, _, _Version) when KeyExAlgo == dh_anon; {null, anon}; %% The signature_algorithms extension was introduced with TLS 1.2. Ignore it if we have %% negotiated a lower version. -select_hashsign(HashSigns, Cert, KeyExAlgo, - undefined, {Major, Minor} = Version) when Major >= 3 andalso Minor >= 3-> - select_hashsign(HashSigns, Cert, KeyExAlgo, tls_v1:default_signature_algs(Version), Version); -select_hashsign(#hash_sign_algos{hash_sign_algos = HashSigns}, Cert, KeyExAlgo, SupportedHashSigns, - {Major, Minor}) when Major >= 3 andalso Minor >= 3 -> - #'OTPCertificate'{tbsCertificate = TBSCert} = public_key:pkix_decode_cert(Cert, otp), - #'OTPSubjectPublicKeyInfo'{algorithm = {_, SubjAlgo, _}} = - TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo, - - SubSign = sign_algo(SubjAlgo), - - case lists:filter(fun({_, S} = Algos) when S == SubSign -> - is_acceptable_hash_sign(Algos, KeyExAlgo, SupportedHashSigns); - (_) -> - false - end, HashSigns) of - [] -> - ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_signature_algorithm); - [HashSign | _] -> - HashSign +select_hashsign({ClientHashSigns, ClientSignatureSchemes}, + Cert, KeyExAlgo, undefined, {Major, Minor} = Version) + when Major >= 3 andalso Minor >= 3-> + select_hashsign({ClientHashSigns, ClientSignatureSchemes}, Cert, KeyExAlgo, + tls_v1:default_signature_algs(Version), Version); +select_hashsign({#hash_sign_algos{hash_sign_algos = ClientHashSigns}, + ClientSignatureSchemes0}, + Cert, KeyExAlgo, SupportedHashSigns, {Major, Minor}) + when Major >= 3 andalso Minor >= 3 -> + ClientSignatureSchemes = get_signature_scheme(ClientSignatureSchemes0), + {SignAlgo0, Param, PublicKeyAlgo0} = get_cert_params(Cert), + SignAlgo = sign_algo(SignAlgo0), + PublicKeyAlgo = public_key_algo(PublicKeyAlgo0), + + %% RFC 5246 (TLS 1.2) + %% If the client provided a "signature_algorithms" extension, then all + %% certificates provided by the server MUST be signed by a + %% hash/signature algorithm pair that appears in that extension. + %% + %% RFC 8446 (TLS 1.3) + %% TLS 1.3 provides two extensions for indicating which signature + %% algorithms may be used in digital signatures. The + %% "signature_algorithms_cert" extension applies to signatures in + %% certificates and the "signature_algorithms" extension, which + %% originally appeared in TLS 1.2, applies to signatures in + %% CertificateVerify messages. + %% + %% If no "signature_algorithms_cert" extension is + %% present, then the "signature_algorithms" extension also applies to + %% signatures appearing in certificates. + case is_supported_sign(SignAlgo, Param, ClientHashSigns, ClientSignatureSchemes) of + true -> + case lists:filter(fun({_, S} = Algos) when S == PublicKeyAlgo -> + is_acceptable_hash_sign(Algos, KeyExAlgo, SupportedHashSigns); + (_) -> + false + end, ClientHashSigns) of + [] -> + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_signature_algorithm); + [HashSign | _] -> + HashSign + end; + false -> + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_signature_algorithm) end; select_hashsign(_, Cert, _, _, Version) -> #'OTPCertificate'{tbsCertificate = TBSCert} = public_key:pkix_decode_cert(Cert, otp), @@ -1161,21 +1235,23 @@ select_hashsign(_, Cert, _, _, Version) -> %% %% Description: Handles signature algorithms selection for certificate requests (client) %%-------------------------------------------------------------------- -select_hashsign(#certificate_request{hashsign_algorithms = #hash_sign_algos{hash_sign_algos = HashSigns}, - certificate_types = Types}, Cert, SupportedHashSigns, +select_hashsign(#certificate_request{ + hashsign_algorithms = #hash_sign_algos{ + hash_sign_algos = HashSigns}, + certificate_types = Types}, + Cert, + SupportedHashSigns, {Major, Minor}) when Major >= 3 andalso Minor >= 3-> - #'OTPCertificate'{tbsCertificate = TBSCert} = public_key:pkix_decode_cert(Cert, otp), - #'OTPCertificate'{tbsCertificate = TBSCert, - signatureAlgorithm = {_,SignAlgo, _}} = public_key:pkix_decode_cert(Cert, otp), - #'OTPSubjectPublicKeyInfo'{algorithm = {_, SubjAlgo, _}} = - TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo, - - Sign = sign_algo(SignAlgo), - SubSign = sign_algo(SubjAlgo), - - case is_acceptable_cert_type(SubSign, HashSigns, Types) andalso is_supported_sign(Sign, HashSigns) of + {SignAlgo0, Param, PublicKeyAlgo0} = get_cert_params(Cert), + SignAlgo = sign_algo(SignAlgo0), + PublicKeyAlgo = public_key_algo(PublicKeyAlgo0), + + case is_acceptable_cert_type(PublicKeyAlgo, Types) andalso + %% certificate_request has no "signature_algorithms_cert" + %% extension in TLS 1.2. + is_supported_sign(SignAlgo, Param, HashSigns, undefined) of true -> - case lists:filter(fun({_, S} = Algos) when S == SubSign -> + case lists:filter(fun({_, S} = Algos) when S == PublicKeyAlgo -> is_acceptable_hash_sign(Algos, SupportedHashSigns); (_) -> false @@ -1188,8 +1264,38 @@ select_hashsign(#certificate_request{hashsign_algorithms = #hash_sign_algos{hash false -> ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_signature_algorithm) end; -select_hashsign(#certificate_request{}, Cert, _, Version) -> - select_hashsign(undefined, Cert, undefined, [], Version). +select_hashsign(#certificate_request{certificate_types = Types}, Cert, _, Version) -> + {_, _, PublicKeyAlgo0} = get_cert_params(Cert), + PublicKeyAlgo = public_key_algo(PublicKeyAlgo0), + + %% Check cert even for TLS 1.0/1.1 + case is_acceptable_cert_type(PublicKeyAlgo, Types) of + true -> + select_hashsign(undefined, Cert, undefined, [], Version); + false -> + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_signature_algorithm) + end. + + +%% Gets the relevant parameters of a certificate: +%% - signature algorithm +%% - parameters of the signature algorithm +%% - public key algorithm (key type) +get_cert_params(Cert) -> + #'OTPCertificate'{tbsCertificate = TBSCert, + signatureAlgorithm = + {_,SignAlgo, Param}} = public_key:pkix_decode_cert(Cert, otp), + #'OTPSubjectPublicKeyInfo'{algorithm = {_, PublicKeyAlgo, _}} = + TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo, + {SignAlgo, Param, PublicKeyAlgo}. + + +get_signature_scheme(undefined) -> + undefined; +get_signature_scheme(#signature_algorithms_cert{ + signature_scheme_list = ClientSignatureSchemes}) -> + ClientSignatureSchemes. + %%-------------------------------------------------------------------- -spec select_hashsign_algs({atom(), atom()}| undefined, oid(), ssl_record:ssl_version()) -> @@ -1238,6 +1344,8 @@ extension_value(#ec_point_formats{ec_point_format_list = List}) -> List; extension_value(#elliptic_curves{elliptic_curve_list = List}) -> List; +extension_value(#supported_groups{supported_groups = SupportedGroups}) -> + SupportedGroups; extension_value(#hash_sign_algos{hash_sign_algos = Algos}) -> Algos; extension_value(#alpn{extension_data = Data}) -> @@ -1258,33 +1366,30 @@ int_to_bin(I) -> L = (length(integer_to_list(I, 16)) + 1) div 2, <<I:(L*8)>>. -certificate_types(_, {N, M}) when N >= 3 andalso M >= 3 -> - case proplists:get_bool(ecdsa, - proplists:get_value(public_keys, crypto:supports())) of - true -> - <<?BYTE(?ECDSA_SIGN), ?BYTE(?RSA_SIGN), ?BYTE(?DSS_SIGN)>>; - false -> - <<?BYTE(?RSA_SIGN), ?BYTE(?DSS_SIGN)>> - end; - -certificate_types(#{key_exchange := KeyExchange}, _) when KeyExchange == rsa; - KeyExchange == dh_rsa; - KeyExchange == dhe_rsa; - KeyExchange == ecdhe_rsa -> - <<?BYTE(?RSA_SIGN)>>; - -certificate_types(#{key_exchange := KeyExchange}, _) when KeyExchange == dh_dss; - KeyExchange == dhe_dss; - KeyExchange == srp_dss -> - <<?BYTE(?DSS_SIGN)>>; - -certificate_types(#{key_exchange := KeyExchange}, _) when KeyExchange == dh_ecdsa; - KeyExchange == dhe_ecdsa; - KeyExchange == ecdh_ecdsa; - KeyExchange == ecdhe_ecdsa -> - <<?BYTE(?ECDSA_SIGN)>>; +%% TLS 1.0+ +%% The end-entity certificate provided by the client MUST contain a +%% key that is compatible with certificate_types. +certificate_types(_, {N, M}) when N >= 3 andalso M >= 1 -> + ECDSA = supported_cert_type_or_empty(ecdsa, ?ECDSA_SIGN), + RSA = supported_cert_type_or_empty(rsa, ?RSA_SIGN), + DSS = supported_cert_type_or_empty(dss, ?DSS_SIGN), + <<ECDSA/binary,RSA/binary,DSS/binary>>; +%% SSL 3.0 certificate_types(_, _) -> - <<?BYTE(?RSA_SIGN)>>. + RSA = supported_cert_type_or_empty(rsa, ?RSA_SIGN), + DSS = supported_cert_type_or_empty(dss, ?DSS_SIGN), + <<RSA/binary,DSS/binary>>. + +%% Returns encoded certificate_type if algorithm is supported +supported_cert_type_or_empty(Algo, Type) -> + case proplists:get_bool( + Algo, + proplists:get_value(public_keys, crypto:supports())) of + true -> + <<?BYTE(Type)>>; + false -> + <<>> + end. certificate_authorities(CertDbHandle, CertDbRef) -> Authorities = certificate_authorities_from_db(CertDbHandle, CertDbRef), @@ -1978,16 +2083,19 @@ dec_server_key_signature(Params, <<?UINT16(Len), Signature:Len/binary>>, _) -> dec_server_key_signature(_, _, _) -> throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, failed_to_decrypt_server_key_sign)). -decode_extensions(<<>>, Acc) -> +decode_extensions(<<>>, _Version, Acc) -> Acc; -decode_extensions(<<?UINT16(?ALPN_EXT), ?UINT16(ExtLen), ?UINT16(Len), ExtensionData:Len/binary, Rest/binary>>, Acc) - when Len + 2 =:= ExtLen -> +decode_extensions(<<?UINT16(?ALPN_EXT), ?UINT16(ExtLen), ?UINT16(Len), + ExtensionData:Len/binary, Rest/binary>>, Version, Acc) + when Len + 2 =:= ExtLen -> ALPN = #alpn{extension_data = ExtensionData}, - decode_extensions(Rest, Acc#{alpn => ALPN}); -decode_extensions(<<?UINT16(?NEXTPROTONEG_EXT), ?UINT16(Len), ExtensionData:Len/binary, Rest/binary>>, Acc) -> + decode_extensions(Rest, Version, Acc#{alpn => ALPN}); +decode_extensions(<<?UINT16(?NEXTPROTONEG_EXT), ?UINT16(Len), + ExtensionData:Len/binary, Rest/binary>>, Version, Acc) -> NextP = #next_protocol_negotiation{extension_data = ExtensionData}, - decode_extensions(Rest, Acc#{next_protocol_negotiation => NextP}); -decode_extensions(<<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), Info:Len/binary, Rest/binary>>, Acc) -> + decode_extensions(Rest, Version, Acc#{next_protocol_negotiation => NextP}); +decode_extensions(<<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), + Info:Len/binary, Rest/binary>>, Version, Acc) -> RenegotiateInfo = case Len of 1 -> % Initial handshake Info; % should be <<0>> will be matched in handle_renegotiation_info @@ -1996,35 +2104,50 @@ decode_extensions(<<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), Info:Len/binary, <<?BYTE(VerifyLen), VerifyInfo/binary>> = Info, VerifyInfo end, - decode_extensions(Rest, Acc#{renegotiation_info => - #renegotiation_info{renegotiated_connection = - RenegotiateInfo}}); + decode_extensions(Rest, Version, Acc#{renegotiation_info => + #renegotiation_info{renegotiated_connection = + RenegotiateInfo}}); -decode_extensions(<<?UINT16(?SRP_EXT), ?UINT16(Len), ?BYTE(SRPLen), SRP:SRPLen/binary, Rest/binary>>, Acc) +decode_extensions(<<?UINT16(?SRP_EXT), ?UINT16(Len), ?BYTE(SRPLen), + SRP:SRPLen/binary, Rest/binary>>, Version, Acc) when Len == SRPLen + 2 -> - decode_extensions(Rest, Acc#{srp => #srp{username = SRP}}); + decode_extensions(Rest, Version, Acc#{srp => #srp{username = SRP}}); decode_extensions(<<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len), - ExtData:Len/binary, Rest/binary>>, Acc) -> + ExtData:Len/binary, Rest/binary>>, Version, Acc) + when Version < {3,4} -> SignAlgoListLen = Len - 2, <<?UINT16(SignAlgoListLen), SignAlgoList/binary>> = ExtData, HashSignAlgos = [{ssl_cipher:hash_algorithm(Hash), ssl_cipher:sign_algorithm(Sign)} || <<?BYTE(Hash), ?BYTE(Sign)>> <= SignAlgoList], - decode_extensions(Rest, Acc#{signature_algs => - #hash_sign_algos{hash_sign_algos = HashSignAlgos}}); + decode_extensions(Rest, Version, Acc#{signature_algs => + #hash_sign_algos{hash_sign_algos = + HashSignAlgos}}); + +decode_extensions(<<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len), + ExtData:Len/binary, Rest/binary>>, Version, Acc) + when Version =:= {3,4} -> + SignSchemeListLen = Len - 2, + <<?UINT16(SignSchemeListLen), SignSchemeList/binary>> = ExtData, + SignSchemes = [ssl_cipher:signature_scheme(SignScheme) || + <<?UINT16(SignScheme)>> <= SignSchemeList], + decode_extensions(Rest, Version, Acc#{signature_algs => + #signature_algorithms{ + signature_scheme_list = SignSchemes}}); decode_extensions(<<?UINT16(?SIGNATURE_ALGORITHMS_CERT_EXT), ?UINT16(Len), - ExtData:Len/binary, Rest/binary>>, Acc) -> + ExtData:Len/binary, Rest/binary>>, Version, Acc) -> SignSchemeListLen = Len - 2, <<?UINT16(SignSchemeListLen), SignSchemeList/binary>> = ExtData, SignSchemes = [ssl_cipher:signature_scheme(SignScheme) || <<?UINT16(SignScheme)>> <= SignSchemeList], - decode_extensions(Rest, Acc#{signature_algs_cert => - #signature_scheme_list{ - signature_scheme_list = SignSchemes}}); + decode_extensions(Rest, Version, Acc#{signature_algs_cert => + #signature_algorithms_cert{ + signature_scheme_list = SignSchemes}}); decode_extensions(<<?UINT16(?ELLIPTIC_CURVES_EXT), ?UINT16(Len), - ExtData:Len/binary, Rest/binary>>, Acc) -> + ExtData:Len/binary, Rest/binary>>, Version, Acc) + when Version < {3,4} -> <<?UINT16(_), EllipticCurveList/binary>> = ExtData, %% Ignore unknown curves Pick = fun(Enum) -> @@ -2036,42 +2159,66 @@ decode_extensions(<<?UINT16(?ELLIPTIC_CURVES_EXT), ?UINT16(Len), end end, EllipticCurves = lists:filtermap(Pick, [ECC || <<ECC:16>> <= EllipticCurveList]), - decode_extensions(Rest, Acc#{elliptic_curves => - #elliptic_curves{elliptic_curve_list = - EllipticCurves}}); + decode_extensions(Rest, Version, Acc#{elliptic_curves => + #elliptic_curves{elliptic_curve_list = + EllipticCurves}}); + + +decode_extensions(<<?UINT16(?ELLIPTIC_CURVES_EXT), ?UINT16(Len), + ExtData:Len/binary, Rest/binary>>, Version, Acc) + when Version =:= {3,4} -> + <<?UINT16(_), GroupList/binary>> = ExtData, + %% Ignore unknown curves + Pick = fun(Enum) -> + case tls_v1:enum_to_group(Enum) of + undefined -> + false; + Group -> + {true, Group} + end + end, + SupportedGroups = lists:filtermap(Pick, [Group || <<Group:16>> <= GroupList]), + decode_extensions(Rest, Version, Acc#{elliptic_curves => + #supported_groups{supported_groups = + SupportedGroups}}); + decode_extensions(<<?UINT16(?EC_POINT_FORMATS_EXT), ?UINT16(Len), - ExtData:Len/binary, Rest/binary>>, Acc) -> + ExtData:Len/binary, Rest/binary>>, Version, Acc) -> <<?BYTE(_), ECPointFormatList/binary>> = ExtData, ECPointFormats = binary_to_list(ECPointFormatList), - decode_extensions(Rest, Acc#{ec_point_formats => - #ec_point_formats{ec_point_format_list = - ECPointFormats}}); + decode_extensions(Rest, Version, Acc#{ec_point_formats => + #ec_point_formats{ec_point_format_list = + ECPointFormats}}); -decode_extensions(<<?UINT16(?SNI_EXT), ?UINT16(Len), Rest/binary>>, Acc) when Len == 0 -> - decode_extensions(Rest, Acc#{sni => #sni{hostname = ""}}); %% Server may send an empy SNI +decode_extensions(<<?UINT16(?SNI_EXT), ?UINT16(Len), + Rest/binary>>, Version, Acc) when Len == 0 -> + decode_extensions(Rest, Version, Acc#{sni => #sni{hostname = ""}}); %% Server may send an empy SNI decode_extensions(<<?UINT16(?SNI_EXT), ?UINT16(Len), - ExtData:Len/binary, Rest/binary>>, Acc) -> + ExtData:Len/binary, Rest/binary>>, Version, Acc) -> <<?UINT16(_), NameList/binary>> = ExtData, - decode_extensions(Rest, Acc#{sni => dec_sni(NameList)}); + decode_extensions(Rest, Version, Acc#{sni => dec_sni(NameList)}); decode_extensions(<<?UINT16(?SUPPORTED_VERSIONS_EXT), ?UINT16(Len), - ExtData:Len/binary, Rest/binary>>, Acc) when Len > 2 -> + ExtData:Len/binary, Rest/binary>>, Version, Acc) when Len > 2 -> <<?UINT16(_),Versions/binary>> = ExtData, - decode_extensions(Rest, Acc#{client_hello_versions => - #client_hello_versions{versions = decode_versions(Versions)}}); + decode_extensions(Rest, Version, Acc#{client_hello_versions => + #client_hello_versions{ + versions = decode_versions(Versions)}}); decode_extensions(<<?UINT16(?SUPPORTED_VERSIONS_EXT), ?UINT16(Len), - ?UINT16(Version), Rest/binary>>, Acc) when Len =:= 2, Version =:= 16#0304 -> - decode_extensions(Rest, Acc#{server_hello_selected_version => - #server_hello_selected_version{selected_version = {3,4}}}); + ?UINT16(SelectedVersion), Rest/binary>>, Version, Acc) + when Len =:= 2, SelectedVersion =:= 16#0304 -> + decode_extensions(Rest, Version, Acc#{server_hello_selected_version => + #server_hello_selected_version{selected_version = + {3,4}}}); %% Ignore data following the ClientHello (i.e., %% extensions) if not understood. -decode_extensions(<<?UINT16(_), ?UINT16(Len), _Unknown:Len/binary, Rest/binary>>, Acc) -> - decode_extensions(Rest, Acc); +decode_extensions(<<?UINT16(_), ?UINT16(Len), _Unknown:Len/binary, Rest/binary>>, Version, Acc) -> + decode_extensions(Rest, Version, Acc); %% This theoretically should not happen if the protocol is followed, but if it does it is ignored. -decode_extensions(_, Acc) -> +decode_extensions(_, _, Acc) -> Acc. dec_hashsign(<<?BYTE(HashAlgo), ?BYTE(SignAlgo)>>) -> @@ -2355,17 +2502,6 @@ handle_srp_extension(undefined, Session) -> handle_srp_extension(#srp{username = Username}, Session) -> Session#session{srp_username = Username}. - -sign_algo(?rsaEncryption) -> - rsa; -sign_algo(?'id-ecPublicKey') -> - ecdsa; -sign_algo(?'id-dsa') -> - dsa; -sign_algo(Alg) -> - {_, Sign} =public_key:pkix_sign_types(Alg), - Sign. - is_acceptable_hash_sign( _, KeyExAlgo, _) when KeyExAlgo == psk; KeyExAlgo == dhe_psk; @@ -2381,15 +2517,80 @@ is_acceptable_hash_sign(Algos,_, SupportedHashSigns) -> is_acceptable_hash_sign(Algos, SupportedHashSigns) -> lists:member(Algos, SupportedHashSigns). -is_acceptable_cert_type(Sign, _HashSigns, Types) -> +is_acceptable_cert_type(Sign, Types) -> lists:member(sign_type(Sign), binary_to_list(Types)). -is_supported_sign(Sign, HashSigns) -> - [] =/= lists:dropwhile(fun({_, S}) when S =/= Sign -> - true; - (_)-> - false - end, HashSigns). +%% signature_algorithms_cert = undefined +is_supported_sign(SignAlgo, _, HashSigns, undefined) -> + lists:member(SignAlgo, HashSigns); + +%% {'SignatureAlgorithm',{1,2,840,113549,1,1,11},'NULL'} +is_supported_sign({Hash, Sign}, 'NULL', _, SignatureSchemes) -> + Fun = fun (Scheme, Acc) -> + {H0, S0, _} = ssl_cipher:scheme_to_components(Scheme), + S1 = case S0 of + rsa_pkcs1 -> rsa; + S -> S + end, + H1 = case H0 of + sha1 -> sha; + H -> H + end, + Acc orelse (Sign =:= S1 andalso + Hash =:= H1) + end, + lists:foldl(Fun, false, SignatureSchemes); + +%% TODO: Implement validation for the curve used in the signature +%% RFC 3279 - 2.2.3 ECDSA Signature Algorithm +%% When the ecdsa-with-SHA1 algorithm identifier appears as the +%% algorithm field in an AlgorithmIdentifier, the encoding MUST omit the +%% parameters field. That is, the AlgorithmIdentifier SHALL be a +%% SEQUENCE of one component: the OBJECT IDENTIFIER ecdsa-with-SHA1. +%% +%% The elliptic curve parameters in the subjectPublicKeyInfo field of +%% the certificate of the issuer SHALL apply to the verification of the +%% signature. +is_supported_sign({Hash, Sign}, _Param, _, SignatureSchemes) -> + Fun = fun (Scheme, Acc) -> + {H0, S0, _} = ssl_cipher:scheme_to_components(Scheme), + S1 = case S0 of + rsa_pkcs1 -> rsa; + S -> S + end, + H1 = case H0 of + sha1 -> sha; + H -> H + end, + Acc orelse (Sign =:= S1 andalso + Hash =:= H1) + end, + lists:foldl(Fun, false, SignatureSchemes). + +%% SupportedPublicKeyAlgorithms PUBLIC-KEY-ALGORITHM-CLASS ::= { +%% dsa | rsa-encryption | dh | kea | ec-public-key } +public_key_algo(?rsaEncryption) -> + rsa; +public_key_algo(?'id-ecPublicKey') -> + ecdsa; +public_key_algo(?'id-dsa') -> + dsa. + +%% SupportedSignatureAlgorithms SIGNATURE-ALGORITHM-CLASS ::= { +%% dsa-with-sha1 | dsaWithSHA1 | md2-with-rsa-encryption | +%% md5-with-rsa-encryption | sha1-with-rsa-encryption | sha-1with-rsa-encryption | +%% sha224-with-rsa-encryption | +%% sha256-with-rsa-encryption | +%% sha384-with-rsa-encryption | +%% sha512-with-rsa-encryption | +%% ecdsa-with-sha1 | +%% ecdsa-with-sha224 | +%% ecdsa-with-sha256 | +%% ecdsa-with-sha384 | +%% ecdsa-with-sha512 } +sign_algo(Alg) -> + public_key:pkix_sign_types(Alg). + sign_type(rsa) -> ?RSA_SIGN; sign_type(dsa) -> @@ -2408,6 +2609,11 @@ client_ecc_extensions(SupportedECCs) -> CryptoSupport = proplists:get_value(public_keys, crypto:supports()), case proplists:get_bool(ecdh, CryptoSupport) of true -> + %% RFC 8422 - 5.1. Client Hello Extensions + %% Clients SHOULD send both the Supported Elliptic Curves Extension and the + %% Supported Point Formats Extension. If the Supported Point Formats + %% Extension is indeed sent, it MUST contain the value 0 (uncompressed) + %% as one of the items in the list of point formats. EcPointFormats = #ec_point_formats{ec_point_format_list = [?ECPOINT_UNCOMPRESSED]}, EllipticCurves = SupportedECCs, {EcPointFormats, EllipticCurves}; @@ -2575,27 +2781,37 @@ cert_curve(Cert, ECCCurve0, CipherSuite) -> {ECCCurve0, CipherSuite} end. -empty_hello_extensions({3, 4}, server) -> - #{server_hello_selected_version => undefined, - key_share => undefined, - pre_shared_key => undefined, - sni => undefined - }; -empty_hello_extensions({3, 4}, client) -> - #{client_hello_versions => undefined, - signature_algs => undefined, - signature_algs_cert => undefined, +empty_extensions() -> + #{}. + +empty_extensions({3,4}, client_hello) -> + #{ sni => undefined, + %% max_fragment_length => undefined, + %% status_request => undefined, + elliptic_curves => undefined, + signature_algs => undefined, + %% use_srtp => undefined, + %% heartbeat => undefined, alpn => undefined, + %% signed_cert_timestamp => undefined, + %% client_cert_type => undefined, + %% server_cert_type => undefined, + %% padding => undefined, key_share => undefined, - pre_shared_key => undefined + pre_shared_key => undefined, + %% psk_key_exhange_modes => undefined, + %% early_data => undefined, + %% cookie => undefined, + client_hello_versions => undefined, + %% cert_authorities => undefined, + %% post_handshake_auth => undefined, + signature_algs_cert => undefined }; -empty_hello_extensions({3, 3}, client) -> - Ext = empty_hello_extensions({3,2}, client), - Ext#{client_hello_versions => undefined, - signature_algs => undefined, - signature_algs_cert => undefined}; -empty_hello_extensions(_, client) -> +empty_extensions({3, 3}, client_hello) -> + Ext = empty_extensions({3,2}, client_hello), + Ext#{signature_algs => undefined}; +empty_extensions(_, client_hello) -> #{renegotiation_info => undefined, alpn => undefined, next_protocol_negotiation => undefined, @@ -2603,11 +2819,13 @@ empty_hello_extensions(_, client) -> ec_point_formats => undefined, elliptic_curves => undefined, sni => undefined}; -empty_hello_extensions(_, server) -> +empty_extensions({3,4}, server_hello) -> + #{server_hello_selected_version => undefined, + key_share => undefined, + pre_shared_key => undefined + }; +empty_extensions(_, server_hello) -> #{renegotiation_info => undefined, alpn => undefined, next_protocol_negotiation => undefined, - ec_point_formats => undefined, - sni => undefined}. -empty_extensions() -> - #{}. + ec_point_formats => undefined}. diff --git a/lib/ssl/src/ssl_handshake.hrl b/lib/ssl/src/ssl_handshake.hrl index 36aefd5e22..1fd143a641 100644 --- a/lib/ssl/src/ssl_handshake.hrl +++ b/lib/ssl/src/ssl_handshake.hrl @@ -52,9 +52,8 @@ -define(NUM_OF_SESSION_ID_BYTES, 32). % TSL 1.1 & SSL 3 -define(NUM_OF_PREMASTERSECRET_BYTES, 48). --define(DEFAULT_DIFFIE_HELLMAN_GENERATOR, 2). --define(DEFAULT_DIFFIE_HELLMAN_PRIME, - 16#FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF). +-define(DEFAULT_DIFFIE_HELLMAN_GENERATOR, ssl_dh_groups:modp2048_generator()). +-define(DEFAULT_DIFFIE_HELLMAN_PRIME, ssl_dh_groups:modp2048_prime()). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Handsake protocol - RFC 4346 section 7.4 @@ -316,9 +315,9 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -define(SIGNATURE_ALGORITHMS_EXT, 13). --record(hash_sign_algos, { - hash_sign_algos - }). +-record(hash_sign_algos, {hash_sign_algos}). +%% RFC 8446 (TLS 1.3) +-record(signature_algorithms, {signature_scheme_list}). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% RFC 7301 Application-Layer Protocol Negotiation @@ -341,9 +340,8 @@ -record(next_protocol, {selected_protocol}). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% ECC Extensions RFC 8422 section 4 and 5 (RFC 7919 not supported) +%% ECC Extensions RFC 8422 section 4 and 5 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -define(ELLIPTIC_CURVES_EXT, 10). -define(EC_POINT_FORMATS_EXT, 11). @@ -351,11 +349,18 @@ elliptic_curve_list }). +%% RFC 8446 (TLS 1.3) renamed the "elliptic_curve" extension. +-record(supported_groups, { + supported_groups + }). + -record(ec_point_formats, { ec_point_format_list }). -define(ECPOINT_UNCOMPRESSED, 0). +%% Defined in RFC 4492, deprecated by RFC 8422 +%% RFC 8422 compliant implementations MUST not support the two formats below -define(ECPOINT_ANSIX962_COMPRESSED_PRIME, 1). -define(ECPOINT_ANSIX962_COMPRESSED_CHAR2, 2). @@ -401,7 +406,7 @@ %% Datagram Transport Layer Security (DTLS) Heartbeat Extension %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Not supported --define(HEARTBEAT, 15). +-define(HS_HEARTBEAT, 15). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% RFC 6962 Certificate Transparency @@ -431,6 +436,6 @@ -define(SIGNATURE_ALGORITHMS_CERT_EXT, 50). --record(signature_scheme_list, {signature_scheme_list}). +-record(signature_algorithms_cert, {signature_scheme_list}). -endif. % -ifdef(ssl_handshake). diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl index 5a18f6aa99..48798799f7 100644 --- a/lib/ssl/src/ssl_internal.hrl +++ b/lib/ssl/src/ssl_internal.hrl @@ -170,6 +170,7 @@ signature_algs, signature_algs_cert, eccs, + supported_groups, %% RFC 8422, RFC 8446 honor_ecc_order :: boolean(), max_handshake_size :: integer(), handshake, diff --git a/lib/ssl/src/ssl_pem_cache.erl b/lib/ssl/src/ssl_pem_cache.erl index b7d23ef01e..41bca2f7b5 100644 --- a/lib/ssl/src/ssl_pem_cache.erl +++ b/lib/ssl/src/ssl_pem_cache.erl @@ -45,7 +45,7 @@ -record(state, { pem_cache, - last_pem_check :: erlang:timestamp(), + last_pem_check :: integer(), clear :: integer() }). @@ -134,8 +134,9 @@ init([Name]) -> PemCache = ssl_pkix_db:create_pem_cache(Name), Interval = pem_check_interval(), erlang:send_after(Interval, self(), clear_pem_cache), + erlang:system_time(second), {ok, #state{pem_cache = PemCache, - last_pem_check = os:timestamp(), + last_pem_check = erlang:convert_time_unit(os:system_time(), native, second), clear = Interval }}. @@ -183,7 +184,7 @@ handle_cast({invalidate_pem, File}, #state{pem_cache = Db} = State) -> handle_info(clear_pem_cache, #state{pem_cache = PemCache, clear = Interval, last_pem_check = CheckPoint} = State) -> - NewCheckPoint = os:timestamp(), + NewCheckPoint = erlang:convert_time_unit(os:system_time(), native, second), start_pem_cache_validator(PemCache, CheckPoint), erlang:send_after(Interval, self(), clear_pem_cache), {noreply, State#state{last_pem_check = NewCheckPoint}}; @@ -229,24 +230,14 @@ init_pem_cache_validator([CacheName, PemCache, CheckPoint]) -> CheckPoint, PemCache). pem_cache_validate({File, _}, CheckPoint) -> - case file:read_file_info(File, []) of - {ok, #file_info{mtime = Time}} -> - case is_before_checkpoint(Time, CheckPoint) of - true -> - ok; - false -> - invalidate_pem(File) - end; + case file:read_file_info(File, [{time, posix}]) of + {ok, #file_info{mtime = Time}} when Time < CheckPoint -> + ok; _ -> invalidate_pem(File) end, CheckPoint. -is_before_checkpoint(Time, CheckPoint) -> - calendar:datetime_to_gregorian_seconds( - calendar:now_to_datetime(CheckPoint)) - - calendar:datetime_to_gregorian_seconds(Time) > 0. - pem_check_interval() -> case application:get_env(ssl, ssl_pem_cache_clean) of {ok, Interval} when is_integer(Interval) -> diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl index 446bb6c56a..ddc83821b4 100644 --- a/lib/ssl/src/ssl_record.erl +++ b/lib/ssl/src/ssl_record.erl @@ -45,7 +45,7 @@ -export([compress/3, uncompress/3, compressions/0]). %% Payload encryption/decryption --export([cipher/4, decipher/4, cipher_aead/4, is_correct_mac/2]). +-export([cipher/4, decipher/4, cipher_aead/4, decipher_aead/5, is_correct_mac/2, nonce_seed/3]). -export_type([ssl_version/0, ssl_atom_version/0, connection_states/0, connection_state/0]). @@ -305,22 +305,20 @@ cipher(Version, Fragment, {CipherFragment, CipherS1} = ssl_cipher:cipher(BulkCipherAlgo, CipherS0, MacHash, Fragment, Version), {CipherFragment, WriteState0#{cipher_state => CipherS1}}. -%% %%-------------------------------------------------------------------- -%% -spec cipher_aead(ssl_version(), iodata(), connection_state(), MacHash::binary()) -> -%% {CipherFragment::binary(), connection_state()}. -%% %% -%% %% Description: Payload encryption +%%-------------------------------------------------------------------- +-spec cipher_aead(ssl_version(), iodata(), connection_state(), AAD::binary()) -> + {CipherFragment::binary(), connection_state()}. + +%% Description: Payload encryption %% %%-------------------------------------------------------------------- cipher_aead(Version, Fragment, #{cipher_state := CipherS0, - sequence_number := SeqNo, security_parameters := #security_parameters{bulk_cipher_algorithm = BulkCipherAlgo} } = WriteState0, AAD) -> - {CipherFragment, CipherS1} = - ssl_cipher:cipher_aead(BulkCipherAlgo, CipherS0, SeqNo, AAD, Fragment, Version), + cipher_aead(BulkCipherAlgo, CipherS0, AAD, Fragment, Version), {CipherFragment, WriteState0#{cipher_state => CipherS1}}. %%-------------------------------------------------------------------- @@ -343,10 +341,39 @@ decipher(Version, CipherFragment, #alert{} = Alert -> Alert end. +%%-------------------------------------------------------------------- +-spec decipher_aead(ssl_cipher:cipher_enum(), #cipher_state{}, + binary(), binary(), ssl_record:ssl_version()) -> + {binary(), #cipher_state{}} | #alert{}. +%% +%% Description: Decrypts the data and checks the associated data (AAD) MAC using +%% cipher described by cipher_enum() and updating the cipher state. +%% Use for suites that use authenticated encryption with associated data (AEAD) +%%------------------------------------------------------------------- +decipher_aead(Type, #cipher_state{key = Key} = CipherState, AAD0, CipherFragment, _) -> + try + Nonce = decrypt_nonce(Type, CipherState, CipherFragment), + {AAD, CipherText, CipherTag} = aead_ciphertext_split(Type, CipherState, CipherFragment, AAD0), + case ssl_cipher:aead_decrypt(Type, Key, Nonce, CipherText, CipherTag, AAD) of + Content when is_binary(Content) -> + {Content, CipherState}; + _ -> + ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC, decryption_failed) + end + catch + _:_ -> + ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC, decryption_failed) + end. + +nonce_seed(?CHACHA20_POLY1305, Seed, CipherState) -> + ssl_cipher:nonce_seed(Seed, CipherState); +nonce_seed(_,_, CipherState) -> + CipherState. %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- + empty_connection_state(ConnectionEnd, BeastMitigation) -> SecParams = empty_security_params(ConnectionEnd), #{security_parameters => SecParams, @@ -399,3 +426,37 @@ initial_security_params(ConnectionEnd) -> compression_algorithm = ?NULL}, ssl_cipher:security_parameters(?TLS_NULL_WITH_NULL_NULL, SecParams). +cipher_aead(?CHACHA20_POLY1305 = Type, #cipher_state{key=Key} = CipherState, AAD0, Fragment, _Version) -> + AAD = end_additional_data(AAD0, erlang:iolist_size(Fragment)), + Nonce = encrypt_nonce(Type, CipherState), + {Content, CipherTag} = ssl_cipher:aead_encrypt(Type, Key, Nonce, Fragment, AAD), + {<<Content/binary, CipherTag/binary>>, CipherState}; +cipher_aead(Type, #cipher_state{key=Key, nonce = ExplicitNonce} = CipherState, AAD0, Fragment, _Version) -> + AAD = end_additional_data(AAD0, erlang:iolist_size(Fragment)), + Nonce = encrypt_nonce(Type, CipherState), + {Content, CipherTag} = ssl_cipher:aead_encrypt(Type, Key, Nonce, Fragment, AAD), + {<<ExplicitNonce:64/integer, Content/binary, CipherTag/binary>>, CipherState#cipher_state{nonce = ExplicitNonce + 1}}. + +encrypt_nonce(?CHACHA20_POLY1305, #cipher_state{nonce = Nonce, iv = IV}) -> + crypto:exor(<<?UINT32(0), Nonce/binary>>, IV); +encrypt_nonce(?AES_GCM, #cipher_state{iv = IV, nonce = ExplicitNonce}) -> + <<Salt:4/bytes, _/binary>> = IV, + <<Salt/binary, ExplicitNonce:64/integer>>. + +decrypt_nonce(?CHACHA20_POLY1305, #cipher_state{nonce = Nonce, iv = IV}, _) -> + crypto:exor(<<Nonce:96/unsigned-big-integer>>, IV); +decrypt_nonce(?AES_GCM, #cipher_state{iv = <<Salt:4/bytes, _/binary>>}, <<ExplicitNonce:8/bytes, _/binary>>) -> + <<Salt/binary, ExplicitNonce/binary>>. + +aead_ciphertext_split(?CHACHA20_POLY1305, #cipher_state{tag_len = Len}, CipherTextFragment, AAD) -> + CipherLen = size(CipherTextFragment) - Len, + <<CipherText:CipherLen/bytes, CipherTag:Len/bytes>> = CipherTextFragment, + {end_additional_data(AAD, CipherLen), CipherText, CipherTag}; +aead_ciphertext_split(?AES_GCM, #cipher_state{tag_len = Len}, CipherTextFragment, AAD) -> + CipherLen = size(CipherTextFragment) - (Len + 8), %% 8 is length of explicit Nonce + << _:8/bytes, CipherText:CipherLen/bytes, CipherTag:Len/bytes>> = CipherTextFragment, + {end_additional_data(AAD, CipherLen), CipherText, CipherTag}. + +end_additional_data(AAD, Len) -> + <<AAD/binary, ?UINT16(Len)>>. + diff --git a/lib/ssl/src/ssl_record.hrl b/lib/ssl/src/ssl_record.hrl index e8ce50040f..4cb19d9d0d 100644 --- a/lib/ssl/src/ssl_record.hrl +++ b/lib/ssl/src/ssl_record.hrl @@ -140,6 +140,7 @@ -define(ALERT, 21). -define(HANDSHAKE, 22). -define(APPLICATION_DATA, 23). +-define(HEARTBEAT, 24). -define(MAX_PLAIN_TEXT_LENGTH, 16384). -define(MAX_COMPRESSED_LENGTH, (?MAX_PLAIN_TEXT_LENGTH+1024)). -define(MAX_CIPHER_TEXT_LENGTH, (?MAX_PLAIN_TEXT_LENGTH+2048)). diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl index 8ded2cbff7..29988edf76 100644 --- a/lib/ssl/src/tls_connection.erl +++ b/lib/ssl/src/tls_connection.erl @@ -69,6 +69,22 @@ -export([init/3, error/3, downgrade/3, %% Initiation and take down states hello/3, user_hello/3, certify/3, cipher/3, abbreviated/3, %% Handshake states connection/3]). +%% TLS 1.3 state functions (server) +-export([start/3, %% common state with client + negotiated/3, + recvd_ch/3, + wait_cert/3, %% common state with client + wait_cv/3, %% common state with client + wait_eoed/3, + wait_finished/3, %% common state with client + wait_flight2/3, + connected/3 %% common state with client + ]). +%% TLS 1.3 state functions (client) +-export([wait_cert_cr/3, + wait_ee/3, + wait_sh/3 + ]). %% gen_statem callbacks -export([callback_mode/0, terminate/3, code_change/4, format_status/2]). @@ -152,9 +168,10 @@ next_record(#state{protocol_buffers = #protocol_buffers{tls_packets = [], tls_cipher_texts = [CT | Rest]} = Buffers, connection_states = ConnStates0, + negotiated_version = Version, ssl_options = #ssl_options{padding_check = Check}} = State) -> - case tls_record:decode_cipher_text(CT, ConnStates0, Check) of + case tls_record:decode_cipher_text(Version, CT, ConnStates0, Check) of {Plain, ConnStates} -> {Plain, State#state{protocol_buffers = Buffers#protocol_buffers{tls_cipher_texts = Rest}, @@ -559,7 +576,7 @@ hello(internal, #client_hello{client_version = ClientVersion} = Hello, State#state{negotiated_version = ClientVersion}); {Version, {Type, Session}, - ConnectionStates, Protocol0, ServerHelloExt, HashSign} -> + ConnectionStates, Protocol0, ServerHelloExt, HashSign} when Version < {3,4} -> Protocol = case Protocol0 of undefined -> CurrentProtocol; _ -> Protocol0 @@ -570,7 +587,23 @@ hello(internal, #client_hello{client_version = ClientVersion} = Hello, hashsign_algorithm = HashSign, client_hello_version = ClientVersion, session = Session, - negotiated_protocol = Protocol}) + negotiated_protocol = Protocol}); + %% TLS 1.3 + {Version, {Type, Session}, + ConnectionStates, Protocol0, ServerHelloExt, HashSign} -> + Protocol = case Protocol0 of + undefined -> CurrentProtocol; + _ -> Protocol0 + end, + tls_connection_1_3:gen_handshake(?FUNCTION_NAME, + internal, + {common_client_hello, Type, ServerHelloExt}, + State#state{connection_states = ConnectionStates, + negotiated_version = Version, + hashsign_algorithm = HashSign, + client_hello_version = ClientVersion, + session = Session, + negotiated_protocol = Protocol}) end; hello(internal, #server_hello{} = Hello, #state{connection_states = ConnectionStates0, @@ -682,6 +715,117 @@ connection(Type, Event, State) -> downgrade(Type, Event, State) -> ssl_connection:?FUNCTION_NAME(Type, Event, State, ?MODULE). +%%-------------------------------------------------------------------- +%% TLS 1.3 state functions +%%-------------------------------------------------------------------- +%%-------------------------------------------------------------------- +-spec start(gen_statem:event_type(), term(), #state{}) -> + gen_statem:state_function_result(). +%%-------------------------------------------------------------------- +start(info, Event, State) -> + gen_info(Event, ?FUNCTION_NAME, State); +start(Type, Event, State) -> + gen_handshake(?FUNCTION_NAME, Type, Event, State). + +%%-------------------------------------------------------------------- +-spec negotiated(gen_statem:event_type(), term(), #state{}) -> + gen_statem:state_function_result(). +%%-------------------------------------------------------------------- +negotiated(info, Event, State) -> + gen_info(Event, ?FUNCTION_NAME, State); +negotiated(Type, Event, State) -> + gen_handshake(?FUNCTION_NAME, Type, Event, State). + +%%-------------------------------------------------------------------- +-spec recvd_ch(gen_statem:event_type(), term(), #state{}) -> + gen_statem:state_function_result(). +%%-------------------------------------------------------------------- +recvd_ch(info, Event, State) -> + gen_info(Event, ?FUNCTION_NAME, State); +recvd_ch(Type, Event, State) -> + gen_handshake(?FUNCTION_NAME, Type, Event, State). + +%%-------------------------------------------------------------------- +-spec wait_cert(gen_statem:event_type(), term(), #state{}) -> + gen_statem:state_function_result(). +%%-------------------------------------------------------------------- +wait_cert(info, Event, State) -> + gen_info(Event, ?FUNCTION_NAME, State); +wait_cert(Type, Event, State) -> + gen_handshake(?FUNCTION_NAME, Type, Event, State). + +%%-------------------------------------------------------------------- +-spec wait_cv(gen_statem:event_type(), term(), #state{}) -> + gen_statem:state_function_result(). +%%-------------------------------------------------------------------- +wait_cv(info, Event, State) -> + gen_info(Event, ?FUNCTION_NAME, State); +wait_cv(Type, Event, State) -> + gen_handshake(?FUNCTION_NAME, Type, Event, State). + +%%-------------------------------------------------------------------- +-spec wait_eoed(gen_statem:event_type(), term(), #state{}) -> + gen_statem:state_function_result(). +%%-------------------------------------------------------------------- +wait_eoed(info, Event, State) -> + gen_info(Event, ?FUNCTION_NAME, State); +wait_eoed(Type, Event, State) -> + gen_handshake(?FUNCTION_NAME, Type, Event, State). + +%%-------------------------------------------------------------------- +-spec wait_finished(gen_statem:event_type(), term(), #state{}) -> + gen_statem:state_function_result(). +%%-------------------------------------------------------------------- +wait_finished(info, Event, State) -> + gen_info(Event, ?FUNCTION_NAME, State); +wait_finished(Type, Event, State) -> + gen_handshake(?FUNCTION_NAME, Type, Event, State). + +%%-------------------------------------------------------------------- +-spec wait_flight2(gen_statem:event_type(), term(), #state{}) -> + gen_statem:state_function_result(). +%%-------------------------------------------------------------------- +wait_flight2(info, Event, State) -> + gen_info(Event, ?FUNCTION_NAME, State); +wait_flight2(Type, Event, State) -> + gen_handshake(?FUNCTION_NAME, Type, Event, State). + +%%-------------------------------------------------------------------- +-spec connected(gen_statem:event_type(), term(), #state{}) -> + gen_statem:state_function_result(). +%%-------------------------------------------------------------------- +connected(info, Event, State) -> + gen_info(Event, ?FUNCTION_NAME, State); +connected(Type, Event, State) -> + gen_handshake(?FUNCTION_NAME, Type, Event, State). + +%%-------------------------------------------------------------------- +-spec wait_cert_cr(gen_statem:event_type(), term(), #state{}) -> + gen_statem:state_function_result(). +%%-------------------------------------------------------------------- +wait_cert_cr(info, Event, State) -> + gen_info(Event, ?FUNCTION_NAME, State); +wait_cert_cr(Type, Event, State) -> + gen_handshake(?FUNCTION_NAME, Type, Event, State). + +%%-------------------------------------------------------------------- +-spec wait_ee(gen_statem:event_type(), term(), #state{}) -> + gen_statem:state_function_result(). +%%-------------------------------------------------------------------- +wait_ee(info, Event, State) -> + gen_info(Event, ?FUNCTION_NAME, State); +wait_ee(Type, Event, State) -> + gen_handshake(?FUNCTION_NAME, Type, Event, State). + +%%-------------------------------------------------------------------- +-spec wait_sh(gen_statem:event_type(), term(), #state{}) -> + gen_statem:state_function_result(). +%%-------------------------------------------------------------------- +wait_sh(info, Event, State) -> + gen_info(Event, ?FUNCTION_NAME, State); +wait_sh(Type, Event, State) -> + gen_handshake(?FUNCTION_NAME, Type, Event, State). + %-------------------------------------------------------------------- %% gen_statem callbacks %%-------------------------------------------------------------------- @@ -756,7 +900,8 @@ initialize_tls_sender(#state{role = Role, protocol_cb = Connection, transport_cb = Transport, negotiated_version = Version, - ssl_options = #ssl_options{renegotiate_at = RenegotiateAt}, + ssl_options = #ssl_options{renegotiate_at = RenegotiateAt, + log_level = LogLevel}, connection_states = #{current_write := ConnectionWriteState}, protocol_specific = #{sender := Sender}}) -> Init = #{current_write => ConnectionWriteState, @@ -767,7 +912,8 @@ initialize_tls_sender(#state{role = Role, protocol_cb => Connection, transport_cb => Transport, negotiated_version => Version, - renegotiate_at => RenegotiateAt}, + renegotiate_at => RenegotiateAt, + log_level => LogLevel}, tls_sender:initialize(Sender, Init). next_tls_record(Data, StateName, #state{protocol_buffers = @@ -821,6 +967,7 @@ handle_info({CloseTag, Socket}, StateName, #state{socket = Socket, close_tag = CloseTag, socket_options = #socket_options{active = Active}, protocol_buffers = #protocol_buffers{tls_cipher_texts = CTs}, + user_data_buffer = Buffer, negotiated_version = Version} = State) -> %% Note that as of TLS 1.1, @@ -828,7 +975,7 @@ handle_info({CloseTag, Socket}, StateName, %% session not be resumed. This is a change from TLS 1.0 to conform %% with widespread implementation practice. - case (Active == false) andalso (CTs =/= []) of + case (Active == false) andalso ((CTs =/= []) or (Buffer =/= <<>>)) of false -> case Version of {1, N} when N >= 1 -> diff --git a/lib/ssl/src/tls_connection_1_3.erl b/lib/ssl/src/tls_connection_1_3.erl new file mode 100644 index 0000000000..c8732e7847 --- /dev/null +++ b/lib/ssl/src/tls_connection_1_3.erl @@ -0,0 +1,159 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2007-2018. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +%% +%%---------------------------------------------------------------------- +%% Purpose: TODO +%%---------------------------------------------------------------------- + +%% RFC 8446 +%% A.1. Client +%% +%% START <----+ +%% Send ClientHello | | Recv HelloRetryRequest +%% [K_send = early data] | | +%% v | +%% / WAIT_SH ----+ +%% | | Recv ServerHello +%% | | K_recv = handshake +%% Can | V +%% send | WAIT_EE +%% early | | Recv EncryptedExtensions +%% data | +--------+--------+ +%% | Using | | Using certificate +%% | PSK | v +%% | | WAIT_CERT_CR +%% | | Recv | | Recv CertificateRequest +%% | | Certificate | v +%% | | | WAIT_CERT +%% | | | | Recv Certificate +%% | | v v +%% | | WAIT_CV +%% | | | Recv CertificateVerify +%% | +> WAIT_FINISHED <+ +%% | | Recv Finished +%% \ | [Send EndOfEarlyData] +%% | K_send = handshake +%% | [Send Certificate [+ CertificateVerify]] +%% Can send | Send Finished +%% app data --> | K_send = K_recv = application +%% after here v +%% CONNECTED +%% +%% A.2. Server +%% +%% START <-----+ +%% Recv ClientHello | | Send HelloRetryRequest +%% v | +%% RECVD_CH ----+ +%% | Select parameters +%% v +%% NEGOTIATED +%% | Send ServerHello +%% | K_send = handshake +%% | Send EncryptedExtensions +%% | [Send CertificateRequest] +%% Can send | [Send Certificate + CertificateVerify] +%% app data | Send Finished +%% after --> | K_send = application +%% here +--------+--------+ +%% No 0-RTT | | 0-RTT +%% | | +%% K_recv = handshake | | K_recv = early data +%% [Skip decrypt errors] | +------> WAIT_EOED -+ +%% | | Recv | | Recv EndOfEarlyData +%% | | early data | | K_recv = handshake +%% | +------------+ | +%% | | +%% +> WAIT_FLIGHT2 <--------+ +%% | +%% +--------+--------+ +%% No auth | | Client auth +%% | | +%% | v +%% | WAIT_CERT +%% | Recv | | Recv Certificate +%% | empty | v +%% | Certificate | WAIT_CV +%% | | | Recv +%% | v | CertificateVerify +%% +-> WAIT_FINISHED <---+ +%% | Recv Finished +%% | K_recv = application +%% v +%% CONNECTED + +-module(tls_connection_1_3). + +-include("ssl_alert.hrl"). +-include("ssl_connection.hrl"). + +-export([hello/4]). +-export([gen_handshake/4]). + +hello(internal, {common_client_hello, Type, ServerHelloExt}, State, Connection) -> + do_server_hello(Type, ServerHelloExt, State, Connection). + +do_server_hello(Type, #{next_protocol_negotiation := _NextProtocols} = + _ServerHelloExt, + #state{negotiated_version = _Version, + session = #session{session_id = _SessId}, + connection_states = _ConnectionStates0, + ssl_options = #ssl_options{versions = [_HighestVersion|_]}} + = State0, _Connection) when is_atom(Type) -> +%% NEGOTIATED +%% | Send ServerHello +%% | K_send = handshake +%% | Send EncryptedExtensions +%% | [Send CertificateRequest] +%% Can send | [Send Certificate + CertificateVerify] +%% app data | Send Finished +%% after --> | K_send = application +%% here +--------+--------+ +%% No 0-RTT | | 0-RTT +%% | | +%% K_recv = handshake | | K_recv = early data +%% [Skip decrypt errors] | +------> WAIT_EOED -+ +%% | | Recv | | Recv EndOfEarlyData +%% | | early data | | K_recv = handshake +%% | +------------+ | +%% | | +%% +> WAIT_FLIGHT2 <--------+ + %% Will be called implicitly + %% {Record, State} = Connection:next_record(State2#state{session = Session}), + %% Connection:next_event(wait_flight2, Record, State, Actions), + %% OR + %% Connection:next_event(WAIT_EOED, Record, State, Actions) + {next_state, wait_flight2, State0, []}. + %% TODO: Add new states to tls_connection! + %% State0. + + +gen_handshake(StateName, Type, Event, + #state{negotiated_version = Version} = State) -> + try tls_connection_1_3:StateName(Type, Event, State, ?MODULE) of + Result -> + Result + catch + _:_ -> + ssl_connection:handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, + malformed_handshake_data), + Version, StateName, State) + end. diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl index 050b4be870..37f13fcbac 100644 --- a/lib/ssl/src/tls_handshake.erl +++ b/lib/ssl/src/tls_handshake.erl @@ -260,6 +260,8 @@ get_tls_handshake(Version, Data, Buffer, Options) -> %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- +handle_client_hello(Version = {3,4}, ClientHello, SslOpts, Info, Renegotiation) -> + tls_handshake_1_3:handle_client_hello(Version, ClientHello, SslOpts, Info, Renegotiation); handle_client_hello(Version, #client_hello{session_id = SugesstedId, cipher_suites = CipherSuites, @@ -276,6 +278,7 @@ handle_client_hello(Version, true -> Curves = maps:get(elliptic_curves, HelloExt, undefined), ClientHashSigns = maps:get(signature_algs, HelloExt, undefined), + ClientSignatureSchemes = maps:get(signature_algs_cert, HelloExt, undefined), AvailableHashSigns = ssl_handshake:available_signature_algs( ClientHashSigns, SupportedHashSigns, Cert, Version), ECCCurve = ssl_handshake:select_curve(Curves, SupportedECCs, ECCOrder), @@ -289,8 +292,10 @@ handle_client_hello(Version, ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_ciphers); _ -> #{key_exchange := KeyExAlg} = ssl_cipher_format:suite_definition(CipherSuite), - case ssl_handshake:select_hashsign(ClientHashSigns, Cert, KeyExAlg, - SupportedHashSigns, Version) of + case ssl_handshake:select_hashsign({ClientHashSigns, ClientSignatureSchemes}, + Cert, KeyExAlg, + SupportedHashSigns, + Version) of #alert{} = Alert -> Alert; HashSign -> @@ -338,26 +343,19 @@ handle_server_hello_extensions(Version, SessionId, Random, CipherSuite, do_hello(undefined, _Versions, _CipherSuites, _Hello, _SslOpts, _Info, _Renegotiation) -> ?ALERT_REC(?FATAL, ?PROTOCOL_VERSION); do_hello(Version, Versions, CipherSuites, Hello, SslOpts, Info, Renegotiation) -> - case tls_record:is_higher({3,4}, Version) of - true -> %% TLS 1.2 and older - case ssl_cipher:is_fallback(CipherSuites) of + case ssl_cipher:is_fallback(CipherSuites) of + true -> + Highest = tls_record:highest_protocol_version(Versions), + case tls_record:is_higher(Highest, Version) of true -> - Highest = tls_record:highest_protocol_version(Versions), - case tls_record:is_higher(Highest, Version) of - true -> - ?ALERT_REC(?FATAL, ?INAPPROPRIATE_FALLBACK); - false -> - handle_client_hello(Version, Hello, SslOpts, Info, Renegotiation) - end; + ?ALERT_REC(?FATAL, ?INAPPROPRIATE_FALLBACK); false -> handle_client_hello(Version, Hello, SslOpts, Info, Renegotiation) end; false -> - %% Implement TLS 1.3 statem ??? - ?ALERT_REC(?FATAL, ?PROTOCOL_VERSION) + handle_client_hello(Version, Hello, SslOpts, Info, Renegotiation) end. - %%-------------------------------------------------------------------- enc_handshake(#hello_request{}, {3, N}) when N < 4 -> {?HELLO_REQUEST, <<>>}; diff --git a/lib/ssl/src/tls_handshake_1_3.erl b/lib/ssl/src/tls_handshake_1_3.erl index 199054b43b..104017b67c 100644 --- a/lib/ssl/src/tls_handshake_1_3.erl +++ b/lib/ssl/src/tls_handshake_1_3.erl @@ -33,6 +33,9 @@ %% Encode -export([encode_handshake/1, decode_handshake/2]). +%% Handshake +-export([handle_client_hello/5]). + encode_handshake(#certificate_request_1_3{ certificate_request_context = Context, extensions = Exts})-> @@ -147,7 +150,51 @@ decode_cert_entries(<<?UINT24(DSize), Data:DSize/binary, ?UINT16(Esize), BinExts encode_extensions(Exts)-> ssl_handshake:encode_extensions(extensions_list(Exts)). decode_extensions(Exts) -> - ssl_handshake:decode_extensions(Exts). + ssl_handshake:decode_extensions(Exts, {3,4}). extensions_list(HelloExtensions) -> [Ext || {_, Ext} <- maps:to_list(HelloExtensions)]. + + +handle_client_hello(Version, + #client_hello{session_id = SugesstedId, + cipher_suites = CipherSuites, + compression_methods = Compressions, + random = Random, + extensions = HelloExt}, + #ssl_options{versions = Versions, + signature_algs = SupportedHashSigns, + eccs = SupportedECCs, + honor_ecc_order = ECCOrder} = SslOpts, + {Port, Session0, Cache, CacheCb, ConnectionStates0, Cert, _}, + Renegotiation) -> + case tls_record:is_acceptable_version(Version, Versions) of + true -> + %% Get supported_groups + %% SupportedGroups = maps:get(elliptic_curves, HelloExt, undefined), + %% Get KeyShareClientHello + + %% Validate supported_groups + KeyShareClientHello + %% IF valid THEN + %% IF supported_groups IS empty send HelloRetryRequest + %% ELSE continue + %% ELSE + %% send Alert + %% ClientHashSigns = maps:get(signature_algs, HelloExt, undefined), + %% ClientSignatureSchemes = maps:get(signature_algs_cert, HelloExt, undefined), + + %% Implement session handling. + + %% Select curve + + %% Sessions cannot be resumed by ClientHello + + %% Select cipher_suite + %% Select hash_sign + + %% Handle extensions + ok; + false -> + ?ALERT_REC(?FATAL, ?PROTOCOL_VERSION) + end. + diff --git a/lib/ssl/src/tls_record.erl b/lib/ssl/src/tls_record.erl index 444759aafa..7debac7d37 100644 --- a/lib/ssl/src/tls_record.erl +++ b/lib/ssl/src/tls_record.erl @@ -41,7 +41,7 @@ -export([encode_plain_text/4]). %% Decoding --export([decode_cipher_text/3]). +-export([decode_cipher_text/4]). %% Protocol version handling -export([protocol_version/1, lowest_protocol_version/1, lowest_protocol_version/2, @@ -106,6 +106,8 @@ get_tls_records(Data, Versions, Buffer, SslOpts) -> % %% Description: Encodes a handshake message to send on the ssl-socket. %%-------------------------------------------------------------------- +encode_handshake(Frag, {3, 4}, ConnectionStates) -> + tls_record_1_3:encode_handshake(Frag, ConnectionStates); encode_handshake(Frag, Version, #{current_write := #{beast_mitigation := BeastMitigation, @@ -126,6 +128,8 @@ encode_handshake(Frag, Version, %% %% Description: Encodes an alert message to send on the ssl-socket. %%-------------------------------------------------------------------- +encode_alert_record(Alert, {3, 4}, ConnectionStates) -> + tls_record_1_3:encode_handshake(Alert, ConnectionStates); encode_alert_record(#alert{level = Level, description = Description}, Version, ConnectionStates) -> encode_plain_text(?ALERT, Version, <<?BYTE(Level), ?BYTE(Description)>>, @@ -146,6 +150,8 @@ encode_change_cipher_spec(Version, ConnectionStates) -> %% %% Description: Encodes data to send on the ssl-socket. %%-------------------------------------------------------------------- +encode_data(Data, {3, 4}, ConnectionStates) -> + tls_record_1_3:encode_data(Data, ConnectionStates); encode_data(Frag, Version, #{current_write := #{beast_mitigation := BeastMitigation, security_parameters := @@ -159,12 +165,14 @@ encode_data(Frag, Version, %%==================================================================== %%-------------------------------------------------------------------- --spec decode_cipher_text(#ssl_tls{}, ssl_record:connection_states(), boolean()) -> +-spec decode_cipher_text(tls_version(), #ssl_tls{}, ssl_record:connection_states(), boolean()) -> {#ssl_tls{}, ssl_record:connection_states()}| #alert{}. %% %% Description: Decode cipher text %%-------------------------------------------------------------------- -decode_cipher_text(#ssl_tls{type = Type, version = Version, +decode_cipher_text({3,4}, CipherTextRecord, ConnectionStates, _) -> + tls_record_1_3:decode_cipher_text(CipherTextRecord, ConnectionStates); +decode_cipher_text(_, #ssl_tls{type = Type, version = Version, fragment = CipherFragment} = CipherText, #{current_read := #{compression_state := CompressionS0, @@ -177,14 +185,15 @@ decode_cipher_text(#ssl_tls{type = Type, version = Version, BulkCipherAlgo, compression_algorithm = CompAlg} } = ReadState0} = ConnnectionStates0, _) -> - AAD = calc_aad(Type, Version, ReadState0), - case ssl_cipher:decipher_aead(BulkCipherAlgo, CipherS0, Seq, AAD, CipherFragment, Version) of - {PlainFragment, CipherS1} -> + AAD = start_additional_data(Type, Version, ReadState0), + CipherS1 = ssl_record:nonce_seed(BulkCipherAlgo, <<?UINT64(Seq)>>, CipherS0), + case ssl_record:decipher_aead(BulkCipherAlgo, CipherS1, AAD, CipherFragment, Version) of + {PlainFragment, CipherState} -> {Plain, CompressionS1} = ssl_record:uncompress(CompAlg, PlainFragment, CompressionS0), ConnnectionStates = ConnnectionStates0#{ current_read => ReadState0#{ - cipher_state => CipherS1, + cipher_state => CipherState, sequence_number => Seq + 1, compression_state => CompressionS1}}, {CipherText#ssl_tls{fragment = Plain}, ConnnectionStates}; @@ -192,7 +201,7 @@ decode_cipher_text(#ssl_tls{type = Type, version = Version, Alert end; -decode_cipher_text(#ssl_tls{type = Type, version = Version, +decode_cipher_text(_, #ssl_tls{type = Type, version = Version, fragment = CipherFragment} = CipherText, #{current_read := #{compression_state := CompressionS0, @@ -486,15 +495,20 @@ encode_iolist(Type, Data, Version, ConnectionStates0) -> {lists:reverse(EncodedMsg), ConnectionStates}. %%-------------------------------------------------------------------- do_encode_plain_text(Type, Version, Data, #{compression_state := CompS0, - security_parameters := + cipher_state := CipherS0, + sequence_number := Seq, + security_parameters := #security_parameters{ cipher_type = ?AEAD, + bulk_cipher_algorithm = BCAlg, compression_algorithm = CompAlg} } = WriteState0) -> {Comp, CompS1} = ssl_record:compress(CompAlg, Data, CompS0), - WriteState1 = WriteState0#{compression_state => CompS1}, - AAD = calc_aad(Type, Version, WriteState1), - ssl_record:cipher_aead(Version, Comp, WriteState1, AAD); + CipherS = ssl_record:nonce_seed(BCAlg, <<?UINT64(Seq)>>, CipherS0), + WriteState = WriteState0#{compression_state => CompS1, + cipher_state => CipherS}, + AAD = start_additional_data(Type, Version, WriteState), + ssl_record:cipher_aead(Version, Comp, WriteState, AAD); do_encode_plain_text(Type, Version, Data, #{compression_state := CompS0, security_parameters := #security_parameters{compression_algorithm = CompAlg} @@ -506,7 +520,7 @@ do_encode_plain_text(Type, Version, Data, #{compression_state := CompS0, do_encode_plain_text(_,_,_,CS) -> exit({cs, CS}). %%-------------------------------------------------------------------- -calc_aad(Type, {MajVer, MinVer}, +start_additional_data(Type, {MajVer, MinVer}, #{sequence_number := SeqNo}) -> <<?UINT64(SeqNo), ?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer)>>. diff --git a/lib/ssl/src/tls_record_1_3.erl b/lib/ssl/src/tls_record_1_3.erl new file mode 100644 index 0000000000..ff198a09bf --- /dev/null +++ b/lib/ssl/src/tls_record_1_3.erl @@ -0,0 +1,260 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2007-2018. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% + +-module(tls_record_1_3). + +-include("tls_record.hrl"). +-include("tls_record_1_3.hrl"). +-include("ssl_internal.hrl"). +-include("ssl_alert.hrl"). +-include("ssl_cipher.hrl"). + +%% Encoding +-export([encode_handshake/2, encode_alert_record/2, + encode_data/2]). +-export([encode_plain_text/3]). + +%% Decoding +-export([decode_cipher_text/2]). + +%%==================================================================== +%% Encoding +%%==================================================================== + +%%-------------------------------------------------------------------- +-spec encode_handshake(iolist(), ssl_record:connection_states()) -> + {iolist(), ssl_record:connection_states()}. +% +%% Description: Encodes a handshake message to send on the tls-1.3-socket. +%%-------------------------------------------------------------------- +encode_handshake(Frag, ConnectionStates) -> + case iolist_size(Frag) of + N when N > ?MAX_PLAIN_TEXT_LENGTH -> + %% TODO: Consider padding here + Data = split_bin(iolist_to_binary(Frag), ?MAX_PLAIN_TEXT_LENGTH), + encode_iolist(?HANDSHAKE, Data, ConnectionStates); + _ -> + encode_plain_text(?HANDSHAKE, Frag, ConnectionStates) + end. + +%%-------------------------------------------------------------------- +-spec encode_alert_record(#alert{}, ssl_record:connection_states()) -> + {iolist(), ssl_record:connection_states()}. +%% +%% Description: Encodes an alert message to send on the ssl-socket. +%%-------------------------------------------------------------------- +encode_alert_record(#alert{level = Level, description = Description}, + ConnectionStates) -> + encode_plain_text(?ALERT, <<?BYTE(Level), ?BYTE(Description)>>, + ConnectionStates). +%%-------------------------------------------------------------------- +-spec encode_data(binary(), ssl_record:connection_states()) -> + {iolist(), ssl_record:connection_states()}. +%% +%% Description: Encodes data to send on the ssl-socket. +%%-------------------------------------------------------------------- +encode_data(Frag, ConnectionStates) -> + Data = split_bin(Frag, ?MAX_PLAIN_TEXT_LENGTH, {3,4}), + encode_iolist(?APPLICATION_DATA, Data, ConnectionStates). + +encode_plain_text(Type, Data0, #{current_write := Write0} = ConnectionStates) -> + PadLen = 0, %% TODO where to specify PadLen? + Data = inner_plaintext(Type, Data0, PadLen), + {CipherFragment, Write1} = encode_plain_text(Data, Write0), + {CipherText, Write} = encode_tls_cipher_text(CipherFragment, Write1), + {CipherText, ConnectionStates#{current_write => Write}}. + +encode_iolist(Type, Data, ConnectionStates0) -> + {ConnectionStates, EncodedMsg} = + lists:foldl(fun(Text, {CS0, Encoded}) -> + {Enc, CS1} = + encode_plain_text(Type, Text, CS0), + {CS1, [Enc | Encoded]} + end, {ConnectionStates0, []}, Data), + {lists:reverse(EncodedMsg), ConnectionStates}. + +%%==================================================================== +%% Decoding +%%==================================================================== + +%%-------------------------------------------------------------------- +-spec decode_cipher_text(#ssl_tls{}, ssl_record:connection_states()) -> + {#ssl_tls{}, ssl_record:connection_states()}| #alert{}. +%% +%% Description: Decode cipher text, use legacy type ssl_tls instead of tls_cipher_text +%% in decoding context so that we can reuse the code from erlier versions. +%%-------------------------------------------------------------------- +decode_cipher_text(#ssl_tls{type = ?OPAQUE_TYPE, + version = ?LEGACY_VERSION, + fragment = CipherFragment}, + #{current_read := + #{sequence_number := Seq, + cipher_state := CipherS0, + security_parameters := + #security_parameters{ + cipher_type = ?AEAD, + bulk_cipher_algorithm = + BulkCipherAlgo} + } = ReadState0} = ConnnectionStates0) -> + AAD = start_additional_data(), + CipherS1 = ssl_cipher:nonce_seed(<<?UINT64(Seq)>>, CipherS0), + case decipher_aead(BulkCipherAlgo, CipherS1, AAD, CipherFragment) of + {PlainFragment, CipherS1} -> + ConnnectionStates = + ConnnectionStates0#{current_read => + ReadState0#{cipher_state => CipherS1, + sequence_number => Seq + 1}}, + decode_inner_plaintext(PlainFragment, ConnnectionStates); + #alert{} = Alert -> + Alert + end; +decode_cipher_text(#ssl_tls{type = Type}, _) -> + %% Version mismatch is already asserted + ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC, {record_typ_mismatch, Type}). + +%%-------------------------------------------------------------------- +%%% Internal functions +%%-------------------------------------------------------------------- +split_bin(Bin, ChunkSize) -> + split_bin(Bin, ChunkSize, []). +split_bin(Bin, ChunkSize, _) -> + do_split_bin(Bin, ChunkSize, []). + +do_split_bin(<<>>, _, Acc) -> + lists:reverse(Acc); +do_split_bin(Bin, ChunkSize, Acc) -> + case Bin of + <<Chunk:ChunkSize/binary, Rest/binary>> -> + do_split_bin(Rest, ChunkSize, [Chunk | Acc]); + _ -> + lists:reverse(Acc, [Bin]) + end. + +inner_plaintext(Type, Data, Length) -> + #inner_plaintext{ + content = Data, + type = Type, + zeros = zero_padding(Length) + }. +zero_padding(Length)-> + binary:copy(<<?BYTE(0)>>, Length). + +encode_plain_text(#inner_plaintext{ + content = Data, + type = Type, + zeros = Zeros + }, #{cipher_state := CipherS0, + sequence_number := Seq, + security_parameters := + #security_parameters{ + cipher_type = ?AEAD} + } = WriteState0) -> + PlainText = <<Data/binary, ?BYTE(Type), Zeros/binary>>, + AAD = start_additional_data(), + CipherS1 = ssl_cipher:nonce_seed(<<?UINT64(Seq)>>, CipherS0), + {Encoded, WriteState} = cipher_aead(PlainText, WriteState0#{cipher_state => CipherS1}, AAD), + {#tls_cipher_text{encoded_record = Encoded}, WriteState}; +encode_plain_text(_, CS) -> + exit({cs, CS}). + +start_additional_data() -> + {MajVer, MinVer} = ?LEGACY_VERSION, + <<?BYTE(?OPAQUE_TYPE), ?BYTE(MajVer), ?BYTE(MinVer)>>. + +end_additional_data(AAD, Len) -> + <<AAD/binary, ?UINT16(Len)>>. + +nonce(#cipher_state{nonce = Nonce, iv = IV}) -> + Len = size(IV), + crypto:exor(<<Nonce:Len/bytes>>, IV). + +cipher_aead(Fragment, + #{cipher_state := CipherS0, + security_parameters := + #security_parameters{bulk_cipher_algorithm = + BulkCipherAlgo} + } = WriteState0, AAD) -> + {CipherFragment, CipherS1} = + cipher_aead(BulkCipherAlgo, CipherS0, AAD, Fragment), + {CipherFragment, WriteState0#{cipher_state => CipherS1}}. + +cipher_aead(Type, #cipher_state{key=Key} = CipherState, AAD0, Fragment) -> + AAD = end_additional_data(AAD0, erlang:iolist_size(Fragment)), + Nonce = nonce(CipherState), + {Content, CipherTag} = ssl_cipher:aead_encrypt(Type, Key, Nonce, Fragment, AAD), + {<<Content/binary, CipherTag/binary>>, CipherState}. + +encode_tls_cipher_text(#tls_cipher_text{opaque_type = Type, + legacy_version = {MajVer, MinVer}, + encoded_record = Encoded}, #{sequence_number := Seq} = Write) -> + Length = erlang:iolist_size(Encoded), + {[<<?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer), ?UINT16(Length)>>, Encoded], + Write#{sequence_number => Seq +1}}. + +decipher_aead(Type, #cipher_state{key = Key} = CipherState, AAD0, CipherFragment) -> + try + Nonce = nonce(CipherState), + {AAD, CipherText, CipherTag} = aead_ciphertext_split(CipherState, CipherFragment, AAD0), + case ssl_cipher:aead_decrypt(Type, Key, Nonce, CipherText, CipherTag, AAD) of + Content when is_binary(Content) -> + {Content, CipherState}; + _ -> + ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC, decryption_failed) + end + catch + _:_ -> + ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC, decryption_failed) + end. + +aead_ciphertext_split(#cipher_state{tag_len = Len}, CipherTextFragment, AAD) -> + CipherLen = size(CipherTextFragment) - Len, + <<CipherText:CipherLen/bytes, CipherTag:Len/bytes>> = CipherTextFragment, + {end_additional_data(AAD, CipherLen), CipherText, CipherTag}. + +decode_inner_plaintext(PlainText, ConnnectionStates) -> + case remove_padding(PlainText) of + #alert{} = Alert -> + Alert; + {Data, Type} -> + {#ssl_tls{type = Type, + version = {3,4}, %% Internally use real version + fragment = Data}, ConnnectionStates} + end. + +remove_padding(PlainText)-> + case binary:split(PlainText, <<0>>, [global, trim]) of + [] -> + ?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE, padding_error); + [Content] -> + Type = binary:last(Content), + split_content(Type, Content, erlang:byte_size(Content) - 1) + end. + +split_content(?HANDSHAKE, _, 0) -> + ?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE, empty_handshake); +split_content(?ALERT, _, 0) -> + ?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE, empty_alert); +%% For special middlebox compatible case! +split_content(?CHANGE_CIPHER_SPEC, _, 0) -> + ?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE, empty_change_cipher_spec); +split_content(?APPLICATION_DATA = Type, _, 0) -> + {Type, <<>>}; +split_content(Type, Content, N) -> + <<Data:N/bytes, ?BYTE(Type)>> = Content, + {Type, Data}. diff --git a/lib/ssl/src/tls_record_1_3.hrl b/lib/ssl/src/tls_record_1_3.hrl new file mode 100644 index 0000000000..273427a34e --- /dev/null +++ b/lib/ssl/src/tls_record_1_3.hrl @@ -0,0 +1,58 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2018-2018. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +%% +%%---------------------------------------------------------------------- +%% Purpose: Record and constant defenitions for the TLS-1.3-record protocol +%% see RFC 8446 not present in earlier versions +%%---------------------------------------------------------------------- + +-ifndef(tls_record_1_3). +-define(tls_record_1_3, true). + +%% enum { +%% invalid(0), +%% %% defined in ssl_record.hrl +%% change_cipher_spec(20), +%% alert(21), +%% handshake(22), +%% application_data(23), +%% heartbeat(24), /* RFC 6520 */ +%% (255) +%% } ContentType; + +-define(INVALID, 0). +-define(LEGACY_VERSION, {3,3}). +-define(OPAQUE_TYPE, 23). + +-record(inner_plaintext, { + content, %% data + type, %% Contentype + zeros %% padding "uint8 zeros[length_of_padding]" + }). +-record(tls_cipher_text, { %% Equivalent of encrypted version of #ssl_tls from previous versions + %% decrypted version will still use #ssl_tls for code reuse purposes + %% with real values for content type and version + opaque_type = ?OPAQUE_TYPE, + legacy_version = ?LEGACY_VERSION, + encoded_record + }). + +-endif. % -ifdef(tls_record_1_3). diff --git a/lib/ssl/src/tls_sender.erl b/lib/ssl/src/tls_sender.erl index f56b96df65..7694c4a7ec 100644 --- a/lib/ssl/src/tls_sender.erl +++ b/lib/ssl/src/tls_sender.erl @@ -29,7 +29,7 @@ %% API -export([start/0, start/1, initialize/2, send_data/2, send_alert/2, - send_and_ack_alert/2, renegotiate/1, + send_and_ack_alert/2, setopts/2, renegotiate/1, update_connection_state/3, dist_tls_socket/1, dist_handshake_complete/3]). %% gen_statem callbacks @@ -49,7 +49,8 @@ negotiated_version, renegotiate_at, connection_monitor, - dist_handle + dist_handle, + log_level }). %%%=================================================================== @@ -81,7 +82,7 @@ initialize(Pid, InitMsg) -> gen_statem:call(Pid, {self(), InitMsg}). %%-------------------------------------------------------------------- --spec send_data(pid(), iodata()) -> ok. +-spec send_data(pid(), iodata()) -> ok | {error, term()}. %% Description: Send application data %%-------------------------------------------------------------------- send_data(Pid, AppData) -> @@ -97,12 +98,18 @@ send_alert(Pid, Alert) -> gen_statem:cast(Pid, Alert). %%-------------------------------------------------------------------- --spec send_and_ack_alert(pid(), #alert{}) -> ok. +-spec send_and_ack_alert(pid(), #alert{}) -> _. %% Description: TLS connection process wants to send an Alert %% in the connection state and recive an ack. %%-------------------------------------------------------------------- send_and_ack_alert(Pid, Alert) -> gen_statem:cast(Pid, {ack_alert, Alert}). +%%-------------------------------------------------------------------- +-spec setopts(pid(), [{packet, integer() | atom()}]) -> ok | {error, term()}. +%% Description: Send application data +%%-------------------------------------------------------------------- +setopts(Pid, Opts) -> + call(Pid, {set_opts, Opts}). %%-------------------------------------------------------------------- -spec renegotiate(pid()) -> {ok, WriteState::map()} | {error, closed}. @@ -165,7 +172,8 @@ init({call, From}, {Pid, #{current_write := WriteState, protocol_cb := Connection, transport_cb := Transport, negotiated_version := Version, - renegotiate_at := RenegotiateAt}}, + renegotiate_at := RenegotiateAt, + log_level := LogLevel}}, #data{connection_states = ConnectionStates} = StateData0) -> Monitor = erlang:monitor(process, Pid), StateData = @@ -180,7 +188,8 @@ init({call, From}, {Pid, #{current_write := WriteState, protocol_cb = Connection, transport_cb = Transport, negotiated_version = Version, - renegotiate_at = RenegotiateAt}, + renegotiate_at = RenegotiateAt, + log_level = LogLevel}, {next_state, handshake, StateData, [{reply, From, ok}]}; init(info, Msg, StateData) -> handle_info(Msg, ?FUNCTION_NAME, StateData). @@ -201,6 +210,8 @@ connection({call, From}, {application_data, AppData}, Data -> send_application_data(Data, From, ?FUNCTION_NAME, StateData) end; +connection({call, From}, {set_opts, _} = Call, StateData) -> + handle_call(From, Call, ?FUNCTION_NAME, StateData); connection({call, From}, dist_get_tls_socket, #data{protocol_cb = Connection, transport_cb = Transport, @@ -254,6 +265,8 @@ connection(info, Msg, StateData) -> StateData :: term()) -> gen_statem:event_handler_result(atom()). %%-------------------------------------------------------------------- +handshake({call, From}, {set_opts, _} = Call, StateData) -> + handle_call(From, Call, ?FUNCTION_NAME, StateData); handshake({call, _}, _, _) -> {keep_state_and_data, [postpone]}; handshake(cast, {new_write, WritesState, Version}, @@ -298,6 +311,9 @@ code_change(_OldVsn, State, Data, _Extra) -> %%%=================================================================== %%% Internal functions %%%=================================================================== +handle_call(From, {set_opts, Opts}, StateName, #data{socket_options = SockOpts} = StateData) -> + {next_state, StateName, StateData#data{socket_options = set_opts(SockOpts, Opts)}, [{reply, From, ok}]}. + handle_info({'DOWN', Monitor, _, _, Reason}, _, #data{connection_monitor = Monitor, dist_handle = Handle} = StateData) when Handle =/= undefined-> @@ -312,15 +328,15 @@ send_tls_alert(Alert, #data{negotiated_version = Version, socket = Socket, protocol_cb = Connection, transport_cb = Transport, - connection_states = ConnectionStates0} = StateData0) -> + connection_states = ConnectionStates0, + log_level = LogLevel} = StateData0) -> {BinMsg, ConnectionStates} = Connection:encode_alert(Alert, Version, ConnectionStates0), Connection:send(Transport, Socket, BinMsg), - %% TODO: fix ssl_options for this process - %% Report = #{direction => outbound, - %% protocol => 'tls_record', - %% message => BinMsg}, - %% ssl_logger:debug(SslOpts#ssl_options.log_level, Report, #{domain => [otp,ssl,tls_record]}), + Report = #{direction => outbound, + protocol => 'tls_record', + message => BinMsg}, + ssl_logger:debug(LogLevel, Report, #{domain => [otp,ssl,tls_record]}), StateData0#data{connection_states = ConnectionStates}. send_application_data(Data, From, StateName, @@ -331,7 +347,8 @@ send_application_data(Data, From, StateName, protocol_cb = Connection, transport_cb = Transport, connection_states = ConnectionStates0, - renegotiate_at = RenegotiateAt} = StateData0) -> + renegotiate_at = RenegotiateAt, + log_level = LogLevel} = StateData0) -> case time_to_renegotiate(Data, ConnectionStates0, RenegotiateAt) of true -> ssl_connection:internal_renegotiation(Pid, ConnectionStates0), @@ -343,10 +360,18 @@ send_application_data(Data, From, StateName, StateData = StateData0#data{connection_states = ConnectionStates}, case Connection:send(Transport, Socket, Msgs) of ok when DistHandle =/= undefined -> + Report = #{direction => outbound, + protocol => 'tls_record', + message => Msgs}, + ssl_logger:debug(LogLevel, Report, #{domain => [otp,ssl,tls_record]}), {next_state, StateName, StateData, []}; Reason when DistHandle =/= undefined -> {next_state, death_row, StateData, [{state_timeout, 5000, Reason}]}; ok -> + Report = #{direction => outbound, + protocol => 'tls_record', + message => Msgs}, + ssl_logger:debug(LogLevel, Report, #{domain => [otp,ssl,tls_record]}), {next_state, StateName, StateData, [{reply, From, ok}]}; Result -> {next_state, StateName, StateData, [{reply, From, Result}]} @@ -369,6 +394,10 @@ encode_size_packet(Bin, Size, Max) -> false -> <<Len:Size, Bin/binary>> end. + +set_opts(SocketOptions, [{packet, N}]) -> + SocketOptions#socket_options{packet = N}. + time_to_renegotiate(_Data, #{current_write := #{sequence_number := Num}}, RenegotiateAt) -> diff --git a/lib/ssl/src/tls_v1.erl b/lib/ssl/src/tls_v1.erl index 7d28962d2d..e7218c8c8a 100644 --- a/lib/ssl/src/tls_v1.erl +++ b/lib/ssl/src/tls_v1.erl @@ -33,7 +33,8 @@ setup_keys/8, suites/1, prf/5, ecc_curves/1, ecc_curves/2, oid_to_enum/1, enum_to_oid/1, default_signature_algs/1, signature_algs/2, - default_signature_schemes/1, signature_schemes/2]). + default_signature_schemes/1, signature_schemes/2, + groups/1, groups/2, group_to_enum/1, enum_to_group/1]). -type named_curve() :: sect571r1 | sect571k1 | secp521r1 | brainpoolP512r1 | sect409k1 | sect409r1 | brainpoolP384r1 | secp384r1 | @@ -42,7 +43,10 @@ sect193r1 | sect193r2 | secp192k1 | secp192r1 | sect163k1 | sect163r1 | sect163r2 | secp160k1 | secp160r1 | secp160r2. -type curves() :: [named_curve()]. --export_type([curves/0, named_curve/0]). +-type group() :: secp256r1 | secp384r1 | secp521r1 | ffdhe2048 | + ffdhe3072 | ffdhe4096 | ffdhe6144 | ffdhe8192. +-type supported_groups() :: [group()]. +-export_type([curves/0, named_curve/0, group/0, supported_groups/0]). %%==================================================================== %% Internal application API @@ -468,6 +472,7 @@ ecc_curves(all) -> sect239k1,sect233k1,sect233r1,secp224k1,secp224r1, sect193r1,sect193r2,secp192k1,secp192r1,sect163k1, sect163r1,sect163r2,secp160k1,secp160r1,secp160r2]; + ecc_curves(Minor) -> TLSCurves = ecc_curves(all), ecc_curves(Minor, TLSCurves). @@ -482,6 +487,44 @@ ecc_curves(_Minor, TLSCurves) -> end end, [], TLSCurves). +-spec groups(4 | all) -> [group()]. +groups(all) -> + [secp256r1, + secp384r1, + secp521r1, + ffdhe2048, + ffdhe3072, + ffdhe4096, + ffdhe6144, + ffdhe8192]; +groups(Minor) -> + TLSGroups = groups(all), + groups(Minor, TLSGroups). +%% +-spec groups(4, [group()]) -> [group()]. +groups(_Minor, TLSGroups) -> + %% TODO: Adding FFDHE groups to crypto? + CryptoGroups = crypto:ec_curves() ++ [ffdhe2048,ffdhe3072,ffdhe4096,ffdhe6144,ffdhe8192], + lists:filter(fun(Group) -> proplists:get_bool(Group, CryptoGroups) end, TLSGroups). + +group_to_enum(secp256r1) -> 23; +group_to_enum(secp384r1) -> 24; +group_to_enum(secp521r1) -> 25; +group_to_enum(ffdhe2048) -> 256; +group_to_enum(ffdhe3072) -> 257; +group_to_enum(ffdhe4096) -> 258; +group_to_enum(ffdhe6144) -> 259; +group_to_enum(ffdhe8192) -> 260. + +enum_to_group(23) -> secp256r1; +enum_to_group(24) -> secp384r1; +enum_to_group(25) -> secp521r1; +enum_to_group(256) -> ffdhe2048; +enum_to_group(257) -> ffdhe3072; +enum_to_group(258) -> ffdhe4096; +enum_to_group(259) -> ffdhe6144; +enum_to_group(260) -> ffdhe8192; +enum_to_group(_) -> undefined. %% ECC curves from draft-ietf-tls-ecc-12.txt (Oct. 17, 2005) oid_to_enum(?sect163k1) -> 1; diff --git a/lib/ssl/test/make_certs.erl b/lib/ssl/test/make_certs.erl index 8fe7c54549..578f6a731a 100644 --- a/lib/ssl/test/make_certs.erl +++ b/lib/ssl/test/make_certs.erl @@ -365,7 +365,7 @@ req_cnf(Root, C) -> "default_bits = ", integer_to_list(C#config.default_bits), "\n" "RANDFILE = $ROOTDIR/RAND\n" "encrypt_key = no\n" - "default_md = md5\n" + "default_md = sha1\n" "#string_mask = pkix\n" "x509_extensions = ca_ext\n" "prompt = no\n" @@ -415,7 +415,7 @@ ca_cnf( ["crl_extensions = crl_ext\n" || C#config.v2_crls], "unique_subject = no\n" "default_days = 3600\n" - "default_md = md5\n" + "default_md = sha1\n" "preserve = no\n" "policy = policy_match\n" "\n" @@ -499,7 +499,7 @@ ca_cnf( ["crl_extensions = crl_ext\n" || C#config.v2_crls], "unique_subject = no\n" "default_days = 3600\n" - "default_md = md5\n" + "default_md = sha1\n" "preserve = no\n" "policy = policy_match\n" "\n" diff --git a/lib/ssl/test/property_test/ssl_eqc_handshake.erl b/lib/ssl/test/property_test/ssl_eqc_handshake.erl index 99c6554f15..8b3b81aaf1 100644 --- a/lib/ssl/test/property_test/ssl_eqc_handshake.erl +++ b/lib/ssl/test/property_test/ssl_eqc_handshake.erl @@ -85,17 +85,14 @@ prop_tls_hs_encode_decode() -> ). %%-------------------------------------------------------------------- -%% Message Generators -------------------------------------------------- +%% Message Generators ----------------------------------------------- %%-------------------------------------------------------------------- -tls_version() -> - oneof([?'TLS_v1.3', ?'TLS_v1.2', ?'TLS_v1.1', ?'TLS_v1', ?'SSL_v3']). - tls_msg(?'TLS_v1.3'= Version) -> oneof([client_hello(Version), server_hello(Version), %%new_session_ticket() - #end_of_early_data{}, + #end_of_early_data{}, encrypted_extensions(), certificate_1_3(), %%certificate_request_1_3, @@ -104,7 +101,8 @@ tls_msg(?'TLS_v1.3'= Version) -> key_update() ]); tls_msg(Version) -> - oneof([#hello_request{}, + oneof([ + #hello_request{}, client_hello(Version), server_hello(Version), certificate(), @@ -116,6 +114,9 @@ tls_msg(Version) -> finished() ]). +%% +%% Shared messages +%% client_hello(?'TLS_v1.3' = Version) -> #client_hello{session_id = session_id(), client_version = ?'TLS_v1.2', @@ -150,10 +151,6 @@ server_hello(Version) -> extensions = server_hello_extensions(Version) }. -encrypted_extensions() -> - ?LET(Exts, extensions(?'TLS_v1.3'), - #encrypted_extensions{extensions = Exts}). - certificate() -> #certificate{ asn1_certificates = certificate_chain() @@ -166,17 +163,35 @@ certificate_1_3() -> entries = certificate_entries(Certs, []) }). -key_update() -> - #key_update{request_update = request_update()}. - finished() -> ?LET(Size, digest_size(), #finished{verify_data = crypto:strong_rand_bytes(Size)}). +%% +%% TLS 1.0-1.2 messages +%% + + + +%% +%% TLS 1.3 messages +%% + +encrypted_extensions() -> + ?LET(Exts, extensions(?'TLS_v1.3', encrypted_extensions), + #encrypted_extensions{extensions = Exts}). + + +key_update() -> + #key_update{request_update = request_update()}. + + %%-------------------------------------------------------------------- %% Messge Data Generators ------------------------------------------- %%-------------------------------------------------------------------- +tls_version() -> + oneof([?'TLS_v1.3', ?'TLS_v1.2', ?'TLS_v1.1', ?'TLS_v1', ?'SSL_v3']). cipher_suite(Version) -> oneof(cipher_suites(Version)). @@ -200,52 +215,14 @@ server_random(_) -> crypto:strong_rand_bytes(32). -client_hello_extensions(?'TLS_v1.3' = Version) -> - ?LET({Versions, Ext}, {supported_versions(Version), c_hello_extensions(Version)}, - maps:merge(Ext, #{client_hello_versions => client_hello_versions(Versions)}) - ); -client_hello_extensions(?'TLS_v1.2' = Version) -> - ?LET({Versions, Exts}, {supported_versions(Version), c_hello_extensions(Version)}, - maps:merge(Exts, #{client_hello_versions => client_hello_versions(Versions)}) - ); client_hello_extensions(Version) -> - ?LET(Exts, - c_hello_extensions(Version), - maps:merge(empty_hello_extensions(Version, client), Exts)). - -server_hello_extensions(?'TLS_v1.3' = Version) -> - ?LET(Exts, - s_hello_extensions(Version), - maps:merge(Exts, #{server_hello_selected_version => server_hello_selected_version(Version)})); -server_hello_extensions(Version) -> - ?LET(Exts, - s_hello_extensions(Version), - Exts). - -c_hello_extensions(?'TLS_v1.3'= Version) -> - ?LET({KeyShare, PreShare}, {key_share_client_hello(), - pre_shared_keyextension()}, - maps:merge(empty_hello_extensions(Version, client), - #{key_share => KeyShare, - pre_shared_key => PreShare - }) - ); -c_hello_extensions(Version) -> - ?LET(Exts, extensions(Version), - maps:merge(empty_hello_extensions(Version, client), + ?LET(Exts, extensions(Version, client_hello), + maps:merge(ssl_handshake:empty_extensions(Version, client_hello), Exts)). -s_hello_extensions(?'TLS_v1.3'= Version) -> - ?LET({KeyShare, PreShare}, {key_share_server_hello(), - pre_shared_keyextension()}, - maps:merge(empty_hello_extensions(Version, server), - #{key_share => KeyShare, - pre_shared_key => PreShare - }) - ); -s_hello_extensions(Version) -> - ?LET(Exts, extensions(Version), - maps:merge(empty_hello_extensions(Version, server), +server_hello_extensions(Version) -> + ?LET(Exts, extensions(Version, server_hello), + maps:merge(ssl_handshake:empty_extensions(Version, server_hello), Exts)). key_share_client_hello() -> @@ -260,83 +237,260 @@ pre_shared_keyextension() -> oneof([undefined]). %%oneof([#pre_shared_keyextension{},undefined]). -extensions(?'TLS_v1.3') -> - ?LET({Ext_1_3, Exts}, {extensions_1_3(), extensions(?'TLS_v1.2')}, maps:merge(Ext_1_3, Exts)); -extensions(?'SSL_v3') -> +%% +--------------------------------------------------+-------------+ +%% | Extension | TLS 1.3 | +%% +--------------------------------------------------+-------------+ +%% | server_name [RFC6066] | CH, EE | +%% | | | +%% | max_fragment_length [RFC6066] | CH, EE | +%% | | | +%% | status_request [RFC6066] | CH, CR, CT | +%% | | | +%% | supported_groups [RFC7919] | CH, EE | +%% | | | +%% | signature_algorithms (RFC 8446) | CH, CR | +%% | | | +%% | use_srtp [RFC5764] | CH, EE | +%% | | | +%% | heartbeat [RFC6520] | CH, EE | +%% | | | +%% | application_layer_protocol_negotiation [RFC7301] | CH, EE | +%% | | | +%% | signed_certificate_timestamp [RFC6962] | CH, CR, CT | +%% | | | +%% | client_certificate_type [RFC7250] | CH, EE | +%% | | | +%% | server_certificate_type [RFC7250] | CH, EE | +%% | | | +%% | padding [RFC7685] | CH | +%% | | | +%% | key_share (RFC 8446) | CH, SH, HRR | +%% | | | +%% | pre_shared_key (RFC 8446) | CH, SH | +%% | | | +%% | psk_key_exchange_modes (RFC 8446) | CH | +%% | | | +%% | early_data (RFC 8446) | CH, EE, NST | +%% | | | +%% | cookie (RFC 8446) | CH, HRR | +%% | | | +%% | supported_versions (RFC 8446) | CH, SH, HRR | +%% | | | +%% | certificate_authorities (RFC 8446) | CH, CR | +%% | | | +%% | oid_filters (RFC 8446) | CR | +%% | | | +%% | post_handshake_auth (RFC 8446) | CH | +%% | | | +%% | signature_algorithms_cert (RFC 8446) | CH, CR | +%% +--------------------------------------------------+-------------+ +extensions(?'TLS_v1.3' = Version, client_hello) -> + ?LET({ + ServerName, + %% MaxFragmentLength, + %% StatusRequest, + SupportedGroups, + SignatureAlgorithms, + %% UseSrtp, + %% Heartbeat, + ALPN, + %% SignedCertTimestamp, + %% ClientCertiticateType, + %% ServerCertificateType, + %% Padding, + %% KeyShare, + %% PreSharedKey, + %% PSKKeyExchangeModes, + %% EarlyData, + %% Cookie, + SupportedVersions, + %% CertAuthorities, + %% PostHandshakeAuth, + SignatureAlgorithmsCert + }, + { + oneof([server_name(), undefined]), + %% oneof([max_fragment_length(), undefined]), + %% oneof([status_request(), undefined]), + oneof([supported_groups(Version), undefined]), + oneof([signature_algs(Version), undefined]), + %% oneof([use_srtp(), undefined]), + %% oneof([heartbeat(), undefined]), + oneof([alpn(), undefined]), + %% oneof([signed_cert_timestamp(), undefined]), + %% oneof([client_cert_type(), undefined]), + %% oneof([server_cert_type(), undefined]), + %% oneof([padding(), undefined]), + %% oneof([key_share(), undefined]), + %% oneof([pre_shared_key(), undefined]), + %% oneof([psk_key_exchange_modes(), undefined]), + %% oneof([early_data(), undefined]), + %% oneof([cookie(), undefined]), + oneof([client_hello_versions(Version), undefined]), + %% oneof([cert_authorities(), undefined]), + %% oneof([post_handshake_auth(), undefined]), + oneof([signature_algs_cert(), undefined]) + }, + maps:filter(fun(_, undefined) -> + false; + (_,_) -> + true + end, + #{ + sni => ServerName, + %% max_fragment_length => MaxFragmentLength, + %% status_request => StatusRequest, + elliptic_curves => SupportedGroups, + signature_algs => SignatureAlgorithms, + %% use_srtp => UseSrtp, + %% heartbeat => Heartbeat, + alpn => ALPN, + %% signed_cert_timestamp => SignedCertTimestamp, + %% client_cert_type => ClientCertificateType, + %% server_cert_type => ServerCertificateType, + %% padding => Padding, + %% key_share => KeyShare, + %% pre_shared_key => PreSharedKey, + %% psk_key_exhange_modes => PSKKeyExchangeModes, + %% early_data => EarlyData, + %% cookie => Cookie, + client_hello_versions => SupportedVersions, + %% cert_authorities => CertAuthorities, + %% post_handshake_auth => PostHandshakeAuth, + signature_algs_cert => SignatureAlgorithmsCert + })); +extensions(?'SSL_v3', client_hello) -> #{}; -extensions(Version) -> - ?LET({SNI, ECPoitF, ECCurves, ALPN, NextP, SRP}, - {oneof([sni(), undefined]), - oneof([ec_poit_formats(), undefined]), +extensions(Version, client_hello) -> + ?LET({ + SNI, + ECPoitF, + ECCurves, + ALPN, + NextP, + SRP + %% RenegotiationInfo + }, + { + oneof([sni(), undefined]), + oneof([ec_point_formats(), undefined]), oneof([elliptic_curves(Version), undefined]), oneof([alpn(), undefined]), oneof([next_protocol_negotiation(), undefined]), - oneof([srp(), undefined])}, + oneof([srp(), undefined]) + %% oneof([renegotiation_info(), undefined]) + }, maps:filter(fun(_, undefined) -> false; (_,_) -> true end, - #{sni => SNI, + #{ + sni => SNI, ec_point_formats => ECPoitF, elliptic_curves => ECCurves, alpn => ALPN, next_protocol_negotiation => NextP, - srp => SRP})). - -extensions_1_3() -> - %% ?LET(Entry, key_share_entry(), - %% maps:filter(fun(_, undefined) -> - %% false; - %% (_,_) -> - %% true - %% end, #{key_share_entry => Entry})). - ?LET({HashSign, SigAlgCert}, {oneof([hash_sign_algos(?'TLS_v1.2')]), oneof([signature_scheme_list()])}, - #{signature_algs => HashSign, - signature_algs_cert => SigAlgCert}). - -empty_hello_extensions({3, 4}, server) -> - #{server_hello_selected_version => undefined, - key_share => undefined, - pre_shared_key => undefined, - sni => undefined - }; -empty_hello_extensions({3, 4}, client) -> - #{client_hello_versions => undefined, - signature_algs => undefined, - signature_algs_cert => undefined, - sni => undefined, - alpn => undefined, - key_share => undefined, - pre_shared_key => undefined - }; -empty_hello_extensions({3, 3}, client) -> - Ext = empty_hello_extensions({3,2}, client), - Ext#{client_hello_versions => undefined, - signature_algs => undefined, - signature_algs_cert => undefined}; -empty_hello_extensions(_, client) -> - #{renegotiation_info => undefined, - alpn => undefined, - next_protocol_negotiation => undefined, - srp => undefined, - ec_point_formats => undefined, - elliptic_curves => undefined, - sni => undefined}; -empty_hello_extensions(_, server) -> - #{renegotiation_info => undefined, - alpn => undefined, - next_protocol_negotiation => undefined, - ec_point_formats => undefined, - sni => undefined}. + srp => SRP + %% renegotiation_info => RenegotiationInfo + })); +extensions(?'TLS_v1.3' = Version, server_hello) -> + ?LET({ + %% KeyShare, + %% PreSharedKeys, + SupportedVersions + }, + { + %% oneof([key_share(), undefined]), + %% oneof([pre_shared_keys(), undefined]), + oneof([server_hello_selected_version(), undefined]) + }, + maps:filter(fun(_, undefined) -> + false; + (_,_) -> + true + end, + #{ + %% key_share => KeyShare, + %% pre_shared_keys => PreSharedKeys, + server_hello_selected_version => SupportedVersions + })); +extensions(Version, server_hello) -> + ?LET({ + ECPoitF, + ALPN, + NextP + %% RenegotiationInfo, + }, + { + oneof([ec_point_formats(), undefined]), + oneof([alpn(), undefined]), + oneof([next_protocol_negotiation(), undefined]) + %% oneof([renegotiation_info(), undefined]), + }, + maps:filter(fun(_, undefined) -> + false; + (_,_) -> + true + end, + #{ + ec_point_formats => ECPoitF, + alpn => ALPN, + next_protocol_negotiation => NextP + %% renegotiation_info => RenegotiationInfo + })); +extensions(?'TLS_v1.3' = Version, encrypted_extensions) -> + ?LET({ + ServerName, + %% MaxFragmentLength, + SupportedGroups, + %% UseSrtp, + %% Heartbeat, + ALPN + %% ClientCertiticateType, + %% ServerCertificateType, + %% EarlyData + }, + { + oneof([server_name(), undefined]), + %% oneof([max_fragment_length(), undefined]), + oneof([supported_groups(Version), undefined]), + %% oneof([use_srtp(), undefined]), + %% oneof([heartbeat(), undefined]), + oneof([alpn(), undefined]) + %% oneof([client_cert_type(), undefined]), + %% oneof([server_cert_type(), undefined]), + %% oneof([early_data(), undefined]) + }, + maps:filter(fun(_, undefined) -> + false; + (_,_) -> + true + end, + #{ + sni => ServerName, + %% max_fragment_length => MaxFragmentLength, + elliptic_curves => SupportedGroups, + %% use_srtp => UseSrtp, + %% heartbeat => Heartbeat, + alpn => ALPN + %% client_cert_type => ClientCertificateType, + %% server_cert_type => ServerCertificateType, + %% early_data => EarlyData + })). + +server_name() -> + ?LET(ServerName, sni(), + ServerName). + %% sni(). signature_algs_cert() -> - ?LET(Algs, signature_scheme_list(), - Algs). + ?LET(List, sig_scheme_list(), + #signature_algorithms_cert{signature_scheme_list = List}). -signature_scheme_list() -> +signature_algorithms() -> ?LET(List, sig_scheme_list(), - #signature_scheme_list{signature_scheme_list = List}). + #signature_algorithms{signature_scheme_list = List}). sig_scheme_list() -> oneof([[rsa_pkcs1_sha256], @@ -357,16 +511,23 @@ sig_scheme_list() -> ecdsa_sha1] ]). -supported_versions(?'TLS_v1.3') -> - oneof([[{3,4}], - [{3,3},{3,4}], - [{3,4},{3,3},{3,2},{3,1},{3,0}] - ]); -supported_versions(_) -> - oneof([[{3,3}], - [{3,3},{3,2}], - [{3,3},{3,2},{3,1},{3,0}] - ]). +client_hello_versions(?'TLS_v1.3') -> + ?LET(SupportedVersions, + oneof([[{3,4}], + [{3,3},{3,4}], + [{3,4},{3,3},{3,2},{3,1},{3,0}] + ]), + #client_hello_versions{versions = SupportedVersions}); +client_hello_versions(_) -> + ?LET(SupportedVersions, + oneof([[{3,3}], + [{3,3},{3,2}], + [{3,3},{3,2},{3,1},{3,0}] + ]), + #client_hello_versions{versions = SupportedVersions}). + +server_hello_selected_version() -> + #server_hello_selected_version{selected_version = {3,4}}. request_update() -> oneof([?UPDATE_NOT_REQUESTED, ?UPDATE_REQUESTED]). @@ -431,13 +592,25 @@ certificate_types(?'TLS_v1.2') -> certificate_types(_) -> iolist_to_binary([<<?BYTE(?ECDSA_SIGN)>>, <<?BYTE(?RSA_SIGN)>>, <<?BYTE(?DSS_SIGN)>>]). + + +signature_algs({3,4}) -> + ?LET(Algs, signature_algorithms(), + Algs); +signature_algs({3,3} = Version) -> + #hash_sign_algos{hash_sign_algos = hash_alg_list(Version)}; +signature_algs(Version) when Version < {3,3} -> + undefined. + + + hashsign_algorithms({_, N} = Version) when N >= 3 -> #hash_sign_algos{hash_sign_algos = hash_alg_list(Version)}; hashsign_algorithms(_) -> undefined. hash_alg_list(Version) -> - ?LET(NumOf, choose(0,15), + ?LET(NumOf, choose(1,15), ?LET(List, [hash_alg(Version) || _ <- lists:seq(1,NumOf)], lists:usort(List) )). @@ -481,27 +654,27 @@ key_share_entry() -> undefined. %%#key_share_entry{}. -client_hello_versions(Versions) -> - #client_hello_versions{versions = Versions}. - server_hello_selected_version(Version) -> #server_hello_selected_version{selected_version = Version}. sni() -> #sni{hostname = net_adm:localhost()}. -ec_poit_formats() -> +ec_point_formats() -> #ec_point_formats{ec_point_format_list = ec_point_format_list()}. ec_point_format_list() -> [?ECPOINT_UNCOMPRESSED]. -elliptic_curves({_, Minor}) -> +elliptic_curves({_, Minor}) when Minor < 4 -> Curves = tls_v1:ecc_curves(Minor), #elliptic_curves{elliptic_curve_list = Curves}. -hash_sign_algos(Version) -> - #hash_sign_algos{hash_sign_algos = hash_alg_list(Version)}. +%% RFC 8446 (TLS 1.3) renamed the "elliptic_curve" extension. +supported_groups({_, Minor}) when Minor >= 4 -> + SupportedGroups = tls_v1:groups(Minor), + #supported_groups{supported_groups = SupportedGroups}. + alpn() -> ?LET(ExtD, alpn_protocols(), #alpn{extension_data = ExtD}). @@ -520,7 +693,7 @@ renegotiation_info() -> #renegotiation_info{renegotiated_connection = 0}. gen_name() -> - ?LET(Size, choose(0,10), gen_string(Size)). + ?LET(Size, choose(1,10), gen_string(Size)). gen_char() -> choose($a,$z). diff --git a/lib/ssl/test/ssl_ECC_SUITE.erl b/lib/ssl/test/ssl_ECC_SUITE.erl index c93f066825..a5309e866b 100644 --- a/lib/ssl/test/ssl_ECC_SUITE.erl +++ b/lib/ssl/test/ssl_ECC_SUITE.erl @@ -395,10 +395,25 @@ client_ecdhe_rsa_server_ecdhe_ecdsa_client_custom(Config) -> end. mix_sign(Config) -> - {COpts0, SOpts0} = ssl_test_lib:make_mix_cert(Config), + mix_sign_rsa_peer(Config), + mix_sign_ecdsa_peer(Config). + +mix_sign_ecdsa_peer(Config) -> + {COpts0, SOpts0} = ssl_test_lib:make_mix_cert([{mix, peer_ecc} |Config]), COpts = ssl_test_lib:ssl_options(COpts0, Config), SOpts = ssl_test_lib:ssl_options(SOpts0, Config), ECDHE_ECDSA = ssl:filter_cipher_suites(ssl:cipher_suites(default, 'tlsv1.2'), [{key_exchange, fun(ecdhe_ecdsa) -> true; (_) -> false end}]), ssl_test_lib:basic_test(COpts, [{ciphers, ECDHE_ECDSA} | SOpts], Config). + + +mix_sign_rsa_peer(Config) -> + {COpts0, SOpts0} = ssl_test_lib:make_mix_cert([{mix, peer_rsa} |Config]), + COpts = ssl_test_lib:ssl_options(COpts0, Config), + SOpts = ssl_test_lib:ssl_options(SOpts0, Config), + ECDHE_RSA = + ssl:filter_cipher_suites(ssl:cipher_suites(default, 'tlsv1.2'), + [{key_exchange, fun(ecdhe_rsa) -> true; (_) -> false end}]), + ssl_test_lib:basic_test(COpts, [{ciphers, ECDHE_RSA} | SOpts], Config). + diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index a0cc9f5c08..1cfff436d2 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -244,7 +244,9 @@ error_handling_tests()-> recv_active_once, recv_error_handling, call_in_error_state, - close_in_error_state + close_in_error_state, + abuse_transport_accept_socket, + controlling_process_transport_accept_socket ]. error_handling_tests_tls()-> @@ -3570,14 +3572,14 @@ conf_signature_algs(Config) when is_list(Config) -> ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, {mfa, {ssl_test_lib, send_recv_result, []}}, - {options, [{active, false}, {signature_algs, [{sha256, rsa}]} | ServerOpts]}]), + {options, [{active, false}, {signature_algs, [{sha, rsa}]} | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, {mfa, {ssl_test_lib, send_recv_result, []}}, - {options, [{active, false}, {signature_algs, [{sha256, rsa}]} | ClientOpts]}]), + {options, [{active, false}, {signature_algs, [{sha, rsa}]} | ClientOpts]}]), ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), @@ -4054,7 +4056,51 @@ close_in_error_state(Config) when is_list(Config) -> Other -> ct:fail(Other) end. +%%-------------------------------------------------------------------- +abuse_transport_accept_socket() -> + [{doc,"Only ssl:handshake and ssl:controlling_process is allowed for transport_accept:sockets"}]. +abuse_transport_accept_socket(Config) when is_list(Config) -> + ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server_transport_abuse_socket([{node, ServerNode}, + {port, 0}, + {from, self()}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, ClientOpts}]), + ssl_test_lib:check_result(Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +controlling_process_transport_accept_socket() -> + [{doc,"Only ssl:handshake and ssl:controlling_process is allowed for transport_accept:sockets"}]. +controlling_process_transport_accept_socket(Config) when is_list(Config) -> + ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server_transport_control([{node, ServerNode}, + {port, 0}, + {from, self()}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + _Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {options, ClientOpts}]), + ssl_test_lib:check_result(Server, ok), + ssl_test_lib:close(Server). + +%%-------------------------------------------------------------------- run_error_server_close([Pid | Opts]) -> {ok, Listen} = ssl:listen(0, Opts), {ok,{_, Port}} = ssl:sockname(Listen), diff --git a/lib/ssl/test/ssl_bench_SUITE.erl b/lib/ssl/test/ssl_bench_SUITE.erl index 3fe6338d69..13097b08b6 100644 --- a/lib/ssl/test/ssl_bench_SUITE.erl +++ b/lib/ssl/test/ssl_bench_SUITE.erl @@ -44,6 +44,7 @@ init_per_suite(Config) -> nonode@nohost -> {skipped, "Node not distributed"}; _ -> + ssl_test_lib:clean_start(), [{server_node, ssl_bench_test_lib:setup(perf_server)}|Config] end. diff --git a/lib/ssl/test/ssl_handshake_SUITE.erl b/lib/ssl/test/ssl_handshake_SUITE.erl index ef1f6be286..c35ee6cb57 100644 --- a/lib/ssl/test/ssl_handshake_SUITE.erl +++ b/lib/ssl/test/ssl_handshake_SUITE.erl @@ -25,6 +25,7 @@ -compile(export_all). -include_lib("common_test/include/ct.hrl"). +-include("ssl_alert.hrl"). -include("ssl_internal.hrl"). -include("tls_handshake.hrl"). -include_lib("public_key/include/public_key.hrl"). @@ -41,7 +42,7 @@ all() -> [decode_hello_handshake, decode_empty_server_sni_correctly, select_proper_tls_1_2_rsa_default_hashsign, ignore_hassign_extension_pre_tls_1_2, - unorded_chain]. + unorded_chain, signature_algorithms]. %%-------------------------------------------------------------------- init_per_suite(Config) -> @@ -55,7 +56,9 @@ init_per_group(_GroupName, Config) -> end_per_group(_,Config) -> Config. -init_per_testcase(ignore_hassign_extension_pre_tls_1_2, Config0) -> +init_per_testcase(TC, Config0) when + TC =:= ignore_hassign_extension_pre_tls_1_2 orelse + TC =:= signature_algorithms -> catch crypto:stop(), try crypto:start() of ok -> @@ -109,7 +112,7 @@ decode_hello_handshake(_Config) -> decode_single_hello_extension_correctly(_Config) -> Renegotiation = <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(1), 0>>, - Extensions = ssl_handshake:decode_extensions(Renegotiation), + Extensions = ssl_handshake:decode_extensions(Renegotiation, {3,3}), #{renegotiation_info := #renegotiation_info{renegotiated_connection = <<0>>}} = Extensions. decode_supported_elliptic_curves_hello_extension_correctly(_Config) -> @@ -163,11 +166,11 @@ ignore_hassign_extension_pre_tls_1_2(Config) -> Opts = proplists:get_value(server_opts, Config), CertFile = proplists:get_value(certfile, Opts), [{_, Cert, _}] = ssl_test_lib:pem_to_der(CertFile), - HashSigns = #hash_sign_algos{hash_sign_algos = [{sha512, rsa}, {sha, dsa}]}, - {sha512, rsa} = ssl_handshake:select_hashsign(HashSigns, Cert, ecdhe_rsa, tls_v1:default_signature_algs({3,3}), {3,3}), + HashSigns = #hash_sign_algos{hash_sign_algos = [{sha512, rsa}, {sha, dsa}, {sha, rsa}]}, + {sha512, rsa} = ssl_handshake:select_hashsign({HashSigns, undefined}, Cert, ecdhe_rsa, tls_v1:default_signature_algs({3,3}), {3,3}), %%% Ignore - {md5sha, rsa} = ssl_handshake:select_hashsign(HashSigns, Cert, ecdhe_rsa, tls_v1:default_signature_algs({3,2}), {3,2}), - {md5sha, rsa} = ssl_handshake:select_hashsign(HashSigns, Cert, ecdhe_rsa, tls_v1:default_signature_algs({3,0}), {3,0}). + {md5sha, rsa} = ssl_handshake:select_hashsign({HashSigns, undefined}, Cert, ecdhe_rsa, tls_v1:default_signature_algs({3,2}), {3,2}), + {md5sha, rsa} = ssl_handshake:select_hashsign({HashSigns, undefined}, Cert, ecdhe_rsa, tls_v1:default_signature_algs({3,0}), {3,0}). unorded_chain(Config) when is_list(Config) -> DefConf = ssl_test_lib:default_cert_chain_conf(), @@ -188,6 +191,55 @@ unorded_chain(Config) when is_list(Config) -> ssl_certificate:certificate_chain(PeerCert, ets:new(foo, []), ExtractedCerts, UnordedChain). +signature_algorithms(Config) -> + Opts = proplists:get_value(server_opts, Config), + CertFile = proplists:get_value(certfile, Opts), + io:format("Cert = ~p~n", [CertFile]), + [{_, Cert, _}] = ssl_test_lib:pem_to_der(CertFile), + HashSigns0 = #hash_sign_algos{ + hash_sign_algos = [{sha512, rsa}, + {sha, dsa}, + {sha, rsa}]}, + Schemes0 = #signature_algorithms_cert{ + signature_scheme_list = [rsa_pkcs1_sha1, + ecdsa_sha1]}, + {sha512, rsa} = ssl_handshake:select_hashsign( + {HashSigns0, Schemes0}, + Cert, ecdhe_rsa, + tls_v1:default_signature_algs({3,3}), + {3,3}), + HashSigns1 = #hash_sign_algos{ + hash_sign_algos = [{sha, dsa}, + {sha, rsa}]}, + {sha, rsa} = ssl_handshake:select_hashsign( + {HashSigns1, Schemes0}, + Cert, ecdhe_rsa, + tls_v1:default_signature_algs({3,3}), + {3,3}), + Schemes1 = #signature_algorithms_cert{ + signature_scheme_list = [rsa_pkcs1_sha256, + ecdsa_sha1]}, + %% Signature not supported + #alert{} = ssl_handshake:select_hashsign( + {HashSigns1, Schemes1}, + Cert, ecdhe_rsa, + tls_v1:default_signature_algs({3,3}), + {3,3}), + %% No scheme, hashsign is used + {sha, rsa} = ssl_handshake:select_hashsign( + {HashSigns1, undefined}, + Cert, ecdhe_rsa, + tls_v1:default_signature_algs({3,3}), + {3,3}), + HashSigns2 = #hash_sign_algos{ + hash_sign_algos = [{sha, dsa}]}, + %% Signature not supported + #alert{} = ssl_handshake:select_hashsign( + {HashSigns2, Schemes1}, + Cert, ecdhe_rsa, + tls_v1:default_signature_algs({3,3}), + {3,3}). + %%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- diff --git a/lib/ssl/test/ssl_packet_SUITE.erl b/lib/ssl/test/ssl_packet_SUITE.erl index 3261244ace..ebf8ddbfac 100644 --- a/lib/ssl/test/ssl_packet_SUITE.erl +++ b/lib/ssl/test/ssl_packet_SUITE.erl @@ -141,6 +141,7 @@ socket_active_packet_tests() -> packet_4_active_some_big, packet_wait_active, packet_size_active, + packet_switch, %% inet header option should be deprecated! header_decode_one_byte_active, header_decode_two_bytes_active, @@ -702,6 +703,34 @@ packet_size_passive(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +packet_switch() -> + [{doc,"Test packet option {packet, 2} followd by {packet, 4}"}]. + +packet_switch(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, send_switch_packet ,["Hello World", 4]}}, + {options, [{nodelay, true},{packet, 2} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, recv_switch_packet, ["Hello World", 4]}}, + {options, [{nodelay, true}, {packet, 2} | + ClientOpts]}]), + + ssl_test_lib:check_result(Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + + %%-------------------------------------------------------------------- packet_cdr_decode() -> [{doc,"Test setting the packet option {packet, cdr}, {mode, binary}"}]. @@ -2286,3 +2315,26 @@ client_reject_packet_opt(Config, PacketOpt) -> ClientOpts]}]), ssl_test_lib:check_result(Client, {error, {options, {not_supported, PacketOpt}}}). + + +send_switch_packet(SslSocket, Data, NextPacket) -> + ssl:send(SslSocket, Data), + receive + {ssl, SslSocket, "Hello World"} -> + ssl:setopts(SslSocket, [{packet, NextPacket}]), + ssl:send(SslSocket, Data), + receive + {ssl, SslSocket, "Hello World"} -> + ok + end + end. +recv_switch_packet(SslSocket, Data, NextPacket) -> + receive + {ssl, SslSocket, "Hello World"} -> + ssl:send(SslSocket, Data), + ssl:setopts(SslSocket, [{packet, NextPacket}]), + receive + {ssl, SslSocket, "Hello World"} -> + ssl:send(SslSocket, Data) + end + end. diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl index 39a5bcaad6..8a2f0824fb 100644 --- a/lib/ssl/test/ssl_test_lib.erl +++ b/lib/ssl/test/ssl_test_lib.erl @@ -196,6 +196,55 @@ connect(ListenSocket, Node, _, _, Timeout, Opts, _) -> rpc:call(Node, ssl, ssl_accept, [AcceptSocket, Opts, Timeout]), AcceptSocket. + +start_server_transport_abuse_socket(Args) -> + Result = spawn_link(?MODULE, transport_accept_abuse, [Args]), + receive + {listen, up} -> + Result + end. + +start_server_transport_control(Args) -> + Result = spawn_link(?MODULE, transport_switch_control, [Args]), + receive + {listen, up} -> + Result + end. + + +transport_accept_abuse(Opts) -> + Node = proplists:get_value(node, Opts), + Port = proplists:get_value(port, Opts), + Options = proplists:get_value(options, Opts), + Pid = proplists:get_value(from, Opts), + Transport = proplists:get_value(transport, Opts, ssl), + ct:log("~p:~p~nssl:listen(~p, ~p)~n", [?MODULE,?LINE, Port, Options]), + {ok, ListenSocket} = rpc:call(Node, Transport, listen, [Port, Options]), + Pid ! {listen, up}, + send_selected_port(Pid, Port, ListenSocket), + {ok, AcceptSocket} = rpc:call(Node, ssl, transport_accept, + [ListenSocket]), + {error, _} = rpc:call(Node, ssl, connection_information, [AcceptSocket]), + _ = rpc:call(Node, ssl, handshake, [AcceptSocket, infinity]), + Pid ! {self(), ok}. + + +transport_switch_control(Opts) -> + Node = proplists:get_value(node, Opts), + Port = proplists:get_value(port, Opts), + Options = proplists:get_value(options, Opts), + Pid = proplists:get_value(from, Opts), + Transport = proplists:get_value(transport, Opts, ssl), + ct:log("~p:~p~nssl:listen(~p, ~p)~n", [?MODULE,?LINE, Port, Options]), + {ok, ListenSocket} = rpc:call(Node, Transport, listen, [Port, Options]), + Pid ! {listen, up}, + send_selected_port(Pid, Port, ListenSocket), + {ok, AcceptSocket} = rpc:call(Node, ssl, transport_accept, + [ListenSocket]), + ok = rpc:call(Node, ssl, controlling_process, [AcceptSocket, self()]), + Pid ! {self(), ok}. + + remove_close_msg(0) -> ok; remove_close_msg(ReconnectTimes) -> @@ -693,20 +742,12 @@ make_mix_cert(Config) -> Ext = x509_test:extensions([{key_usage, [digitalSignature]}]), Digest = {digest, appropriate_sha(crypto:supports())}, CurveOid = hd(tls_v1:ecc_curves(0)), - ClientFileBase = filename:join([proplists:get_value(priv_dir, Config), "mix"]), - ServerFileBase = filename:join([proplists:get_value(priv_dir, Config), "mix"]), - ClientChain = [[Digest, {key, {namedCurve, CurveOid}}], - [Digest, {key, hardcode_rsa_key(1)}], - [Digest, {key, {namedCurve, CurveOid}}, {extensions, Ext}] - ], - ServerChain = [[Digest, {key, {namedCurve, CurveOid}}], - [Digest, {key, hardcode_rsa_key(2)}], - [Digest, {key, {namedCurve, CurveOid}},{extensions, Ext}] - ], + Mix = proplists:get_value(mix, Config, peer_ecc), ClientChainType =ServerChainType = mix, + {ClientChain, ServerChain} = mix(Mix, Digest, CurveOid, Ext), CertChainConf = gen_conf(ClientChainType, ServerChainType, ClientChain, ServerChain), - ClientFileBase = filename:join([proplists:get_value(priv_dir, Config), atom_to_list(ClientChainType)]), - ServerFileBase = filename:join([proplists:get_value(priv_dir, Config), atom_to_list(ServerChainType)]), + ClientFileBase = filename:join([proplists:get_value(priv_dir, Config), "mix" ++ atom_to_list(Mix)]), + ServerFileBase = filename:join([proplists:get_value(priv_dir, Config), "mix" ++ atom_to_list(Mix)]), GenCertData = public_key:pkix_test_data(CertChainConf), [{server_config, ServerConf}, {client_config, ClientConf}] = @@ -715,6 +756,28 @@ make_mix_cert(Config) -> [{reuseaddr, true}, {verify, verify_peer} | ServerConf] }. +mix(peer_ecc, Digest, CurveOid, Ext) -> + ClientChain = [[Digest, {key, {namedCurve, CurveOid}}], + [Digest, {key, hardcode_rsa_key(1)}], + [Digest, {key, {namedCurve, CurveOid}}, {extensions, Ext}] + ], + ServerChain = [[Digest, {key, {namedCurve, CurveOid}}], + [Digest, {key, hardcode_rsa_key(2)}], + [Digest, {key, {namedCurve, CurveOid}},{extensions, Ext}] + ], + {ClientChain, ServerChain}; + +mix(peer_rsa, Digest, CurveOid, Ext) -> + ClientChain = [[Digest, {key, {namedCurve, CurveOid}}], + [Digest, {key, {namedCurve, CurveOid}}], + [Digest, {key, hardcode_rsa_key(1)}, {extensions, Ext}] + ], + ServerChain = [[Digest, {key, {namedCurve, CurveOid}}], + [Digest, {key, {namedCurve, CurveOid}}], + [Digest, {key, hardcode_rsa_key(2)},{extensions, Ext}] + ], + {ClientChain, ServerChain}. + make_ecdsa_cert(Config) -> CryptoSupport = crypto:supports(), case proplists:get_bool(ecdsa, proplists:get_value(public_keys, CryptoSupport)) of |