diff options
Diffstat (limited to 'lib/ssl')
70 files changed, 14863 insertions, 2766 deletions
diff --git a/lib/ssl/doc/src/Makefile b/lib/ssl/doc/src/Makefile index 7cf251d8f9..064131944c 100644 --- a/lib/ssl/doc/src/Makefile +++ b/lib/ssl/doc/src/Makefile @@ -47,6 +47,7 @@ XML_CHAPTER_FILES = \ ssl_protocol.xml \ using_ssl.xml \ ssl_distribution.xml \ + standards_compliance.xml \ notes.xml BOOK_FILES = book.xml diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml index cee6752020..f0231da2ad 100644 --- a/lib/ssl/doc/src/notes.xml +++ b/lib/ssl/doc/src/notes.xml @@ -843,7 +843,7 @@ <p> TLS sessions must be registered with SNI if provided, so that sessions where client hostname verification would - fail can not connect reusing a session created when the + fail cannot connect reusing a session created when the server name verification succeeded.</p> <p> Own Id: OTP-14632</p> @@ -1021,7 +1021,7 @@ public_key:pkix_verify_hostname/2 to verify the hostname of the connection with the server certificates specified hostname during certificate path validation. The user may - explicitly disables it. Also if the hostname can not be + explicitly disables it. Also if the hostname cannot be derived from the first argument to connect or is not supplied by the server name indication option, the check will not be performed.</p> diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml index 24d063e1bd..d626748af6 100644 --- a/lib/ssl/doc/src/ssl.xml +++ b/lib/ssl/doc/src/ssl.xml @@ -128,7 +128,7 @@ <name name="hostname"/> </datatype> - <datatype> + <datatype> <name name="ip_address"/> </datatype> @@ -136,19 +136,23 @@ <name name="protocol_version"/> </datatype> - <datatype> + <datatype> <name name="tls_version"/> </datatype> - + <datatype> <name name="dtls_version"/> </datatype> - + <datatype> - <name name="legacy_version"/> + <name name="tls_legacy_version"/> </datatype> - - <datatype> + + <datatype> + <name name="dtls_legacy_version"/> + </datatype> + + <datatype> <name name="prf_random"/> </datatype> @@ -195,21 +199,21 @@ <datatype> <name name="signature_algs"/> </datatype> - + <datatype> <name name="sign_algo"/> </datatype> - + <datatype> - <name name="kex_algo"/> + <name name="sign_scheme"/> </datatype> - + <datatype> - <name name="algo_filter"/> + <name name="kex_algo"/> </datatype> <datatype> - <name name="eccs"/> + <name name="algo_filter"/> </datatype> <datatype> @@ -244,6 +248,10 @@ <name name="tls_alert"/> </datatype> + <datatype> + <name name="reason"/> + </datatype> + <datatype_title>TLS/DTLS OPTION DESCRIPTIONS - COMMON for SERVER and CLIENT</datatype_title> <datatype> @@ -334,12 +342,28 @@ </datatype> <datatype> - <name name="eccs"/> - <desc><p> Allows to specify the order of preference for named curves - and to restrict their usage when using a cipher suite supporting them.</p> + <name name="signature_schemes"/> + <desc> + <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> </desc> - </datatype> - + </datatype> + <datatype> <name name="secure_renegotiation"/> <desc><p>Specifies if to reject renegotiation attempt that does @@ -479,7 +503,7 @@ marker="public_key:public_key#pkix_path_validation-3">public_key:pkix_path_valid <item>check is only performed on the peer certificate.</item> <tag><c>best_effort</c></tag> - <item>if certificate revocation status can not be determined + <item>if certificate revocation status cannot be determined it will be accepted as valid.</item> </taglist> @@ -612,10 +636,19 @@ fun(srp, Username :: string(), UserState :: term()) -> </desc> </datatype> - <datatype> - <name name="log_alert"/> - <desc><p>If set to <c>false</c>, error reports are not displayed.</p> - </desc> + <datatype> + <name name="log_alert"/> + <desc><p>If set to <c>false</c>, error reports are not displayed. + Deprecated in OTP 22, use {log_level, <seealso marker="#type-logging_level">logging_level()</seealso>} instead.</p> + </desc> + </datatype> + + <datatype> + <name name="logging_level"/> + <desc><p>Specifies the log level for TLS/DTLS. At verbosity level <c>notice</c> and above error reports are + displayed in TLS/DTLS. The level <c>debug</c> triggers verbose logging of TLS/DTLS protocol + messages.</p> + </desc> </datatype> <datatype> @@ -856,7 +889,6 @@ fun(srp, Username :: string(), UserState :: term()) -> </desc> </datatype> - <datatype_title>TLS/DTLS OPTION DESCRIPTIONS - SERVER </datatype_title> @@ -870,8 +902,7 @@ fun(srp, Username :: string(), UserState :: term()) -> is supplied it overrides option <c>cacertfile</c>.</p> </desc> </datatype> - - + <datatype> <name name="server_cafile"/> <desc><p>Path to a file containing PEM-encoded CA @@ -899,7 +930,6 @@ fun(srp, Username :: string(), UserState :: term()) -> default parameters are used.</p> </desc> </datatype> - <datatype> <name name="server_verify_type"/> @@ -1071,13 +1101,8 @@ fun(srp, Username :: string(), UserState :: term()) -> <funcs> <func> - <name since="OTP 20.3">append_cipher_suites(Deferred, Suites) -> ciphers() </name> + <name name="append_cipher_suites" arity="2" since="OTP 20.3"/> <fsummary></fsummary> - <type> - <v>Deferred = <seealso marker="#type-ciphers">ciphers()</seealso> | - <seealso marker="#type-cipher_filters">cipher_filters()</seealso></v> - <v>Suites = <seealso marker="#type-ciphers">ciphers()</seealso></v> - </type> <desc><p>Make <c>Deferred</c> suites become the least preferred suites, that is put them at the end of the cipher suite list <c>Suites</c> after removing them from <c>Suites</c> if @@ -1088,25 +1113,18 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name since="OTP R14B">cipher_suites() -></name> - <name since="OTP R14B">cipher_suites(Type) -> [old_cipher_suite()]</name> + <name name="cipher_suites" arity="0" since="OTP R14B"/> + <name name="cipher_suites" arity="1" since="OTP R14B"/> <fsummary>Returns a list of supported cipher suites.</fsummary> - <type> - <v>Type = erlang | openssl | all</v> - </type> <desc> <p>Deprecated in OTP 21, use <seealso marker="#cipher_suites-2">cipher_suites/2</seealso> instead.</p> </desc> </func> <func> - <name since="OTP 20.3">cipher_suites(Supported, Version) -> ciphers()</name> + <name name="cipher_suites" arity="2" since="OTP 20.3"/> <fsummary>Returns a list of all default or all supported cipher suites.</fsummary> - <type> - <v> Supported = default | all | anonymous </v> - <v> Version = <seealso marker="#type-protocol_version">protocol_version() </seealso></v> - </type> <desc><p>Returns all default or all supported (except anonymous), or all anonymous cipher suites for a TLS version</p> @@ -1114,16 +1132,9 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name since="OTP 19.2">eccs() -></name> - <name since="OTP 19.2">eccs(Version) -> NamedCurves</name> + <name name="eccs" arity="0" since="OTP 19.2"/> + <name name="eccs" arity="1" since="OTP 19.2"/> <fsummary>Returns a list of supported ECCs.</fsummary> - - <type> - <v> Version = <seealso marker="#type-protocol_version">protocol_version() </seealso></v> - <v> NamedCurves = <seealso marker="#type-named_curve">[named_curve()] </seealso></v> - - </type> - <desc><p>Returns a list of supported ECCs. <c>eccs()</c> is equivalent to calling <c>eccs(Protocol)</c> with all supported protocols and then deduplicating the output.</p> @@ -1131,9 +1142,8 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name since="OTP 17.5">clear_pem_cache() -> ok </name> + <name name="clear_pem_cache" arity="0" since="OTP 17.5"/> <fsummary> Clears the pem cache</fsummary> - <desc><p>PEM files, used by ssl API-functions, are cached. The cache is regularly checked to see if any cache entries should be invalidated, however this function provides a way to @@ -1143,19 +1153,10 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name since="OTP R14B">connect(Socket, Options) -> </name> - <name since="">connect(Socket, Options, Timeout) -> {ok, SslSocket} | {ok, SslSocket, Ext} - | {error, Reason}</name> + <name name="connect" arity="2" since="OTP R14B"/> + <name name="connect" arity="3" clause_i="1" since=""/> <fsummary>Upgrades a <c>gen_tcp</c>, or equivalent, connected socket to an TLS socket.</fsummary> - <type> - <v>Socket = <seealso marker="#type-socket"> socket() </seealso></v> - <v>Options = <seealso marker="#type-tls_client_option"> [tls_client_option()] </seealso></v> - <v>Timeout = timeout()</v> - <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> - <v>Ext = <seealso marker="#type-protocol_extensions">protocol_extensions()</seealso></v> - <v>Reason = closed | timeout | <seealso marker="#type-error_alert"> error_alert() </seealso></v> - </type> <desc><p>Upgrades a <c>gen_tcp</c>, or equivalent, connected socket to an TLS socket, that is, performs the client-side TLS handshake.</p> @@ -1187,18 +1188,9 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name since="">connect(Host, Port, Options) -></name> - <name since="">connect(Host, Port, Options, Timeout) -> - {ok, SslSocket}| {ok, SslSocket, Ext} | {error, Reason}</name> + <name since="" name="connect" arity="3" clause_i="2"/> + <name since="" name="connect" arity="4"/> <fsummary>Opens an TLS/DTLS connection to <c>Host</c>, <c>Port</c>.</fsummary> - <type> - <v>Host =<seealso marker="#type-host"> host() </seealso> </v> - <v>Port = <seealso marker="kernel:inet#type-port_number">inet:port_number()</seealso></v> - <v>Options = <seealso marker="#type-tls_client_option"> [tls_client_option()]</seealso></v> - <v>Timeout = timeout()</v> - <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> - <v>Reason = closed | timeout | <seealso marker="#type-error_alert"> error_alert() </seealso></v> - </type> <desc><p>Opens an TLS/DTLS connection to <c>Host</c>, <c>Port</c>.</p> <p> When the option <c>verify</c> is set to <c>verify_peer</c> the check @@ -1235,24 +1227,15 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name since="">close(SslSocket) -> ok | {error, Reason}</name> + <name since="" name="close" arity="1" /> <fsummary>Closes an TLS/DTLS connection.</fsummary> - <type> - <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> - <v>Reason = term()</v> - </type> <desc><p>Closes an TLS/DTLS connection.</p> </desc> </func> <func> - <name since="OTP 18.1">close(SslSocket, How) -> ok | {ok, port()} | {error, Reason}</name> + <name since="OTP 18.1" name="close" arity="2"/> <fsummary>Closes an TLS connection.</fsummary> - <type> - <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> - <v>How = timeout() | {NewController::pid(), timeout()} </v> - <v>Reason = term()</v> - </type> <desc><p>Closes or downgrades an TLS connection. In the latter case the transport connection will be handed over to the <c>NewController</c> process after receiving the TLS close alert from the peer. The returned transport socket will have @@ -1261,15 +1244,9 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name since="">controlling_process(SslSocket, NewOwner) -> - ok | {error, Reason}</name> + <name since="" name="controlling_process" arity="2" /> <fsummary>Assigns a new controlling process to the TLS/DTLS socket.</fsummary> - <type> - <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> - <v>NewOwner = pid()</v> - <v>Reason = term()</v> - </type> <desc><p>Assigns a new controlling process to the SSL socket. A controlling process is the owner of an SSL socket, and receives all messages from the socket.</p> @@ -1277,17 +1254,9 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name since="OTP 18.0">connection_information(SslSocket) -> - {ok, Result} | {error, Reason} </name> + <name since="OTP 18.0" name="connection_information" arity="1"/> <fsummary>Returns all the connection information. </fsummary> - <type> - <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> - <v>Item = protocol | selected_cipher_suite | sni_hostname | ecc | session_id | atom()</v> - <d>Meaningful atoms, not specified above, are the ssl option names.</d> - <v>Result = [{Item::atom(), Value::term()}]</v> - <v>Reason = term()</v> - </type> <desc><p>Returns the most relevant information about the connection, ssl options that are undefined will be filtered out. Note that values that affect the security of the connection will only be returned if explicitly requested by connection_information/2.</p> @@ -1298,34 +1267,23 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name since="OTP 18.0">connection_information(SslSocket, Items) -> - {ok, Result} | {error, Reason} </name> + <name since="OTP 18.0" name="connection_information" arity="2"/> <fsummary>Returns the requested connection information. </fsummary> - <type> - <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> - <v>Items = [Item]</v> - <v>Item = protocol | cipher_suite | sni_hostname | ecc | session_id | client_random - | server_random | master_secret | atom()</v> - <d>Note that client_random, server_random and master_secret are values - that affect the security of connection. Meaningful atoms, not specified above, are the ssl option names.</d> - <v>Result = [{Item::atom(), Value::term()}]</v> - <v>Reason = term()</v> - </type> <desc><p>Returns the requested information items about the connection, if they are defined.</p> + <p>Note that client_random, server_random and master_secret are values + that affect the security of connection. Meaningful atoms, not specified + above, are the ssl option names.</p> + <note><p>If only undefined options are requested the resulting list can be empty.</p></note> </desc> </func> <func> - <name since="OTP 20.3">filter_cipher_suites(Suites, Filters) -> ciphers()</name> + <name since="OTP 20.3" name="filter_cipher_suites" arity="2" /> <fsummary></fsummary> - <type> - <v> Suites = <seealso marker="#type-ciphers"> ciphers() </seealso></v> - <v> Filters = <seealso marker="#type-cipher_filters"> cipher_filters() </seealso></v> - </type> <desc><p>Removes cipher suites if any of the filter functions returns false for any part of the cipher suite. This function also calls default filter functions to make sure the cipher @@ -1335,24 +1293,16 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name since="">format_error(Reason) -> string()</name> + <name since="" name="format_error" arity="1" /> <fsummary>Returns an error string.</fsummary> - <type> - <v>Reason = term()</v> - </type> <desc> <p>Presents the error returned by an SSL function as a printable string.</p> </desc> </func> <func> - <name since="">getopts(SslSocket, OptionNames) -> - {ok, [socketoption()]} | {error, Reason}</name> + <name since="" name="getopts" arity="2" /> <fsummary>Gets the values of the specified options.</fsummary> - <type> - <v>Socket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> - <v>OptionNames = [atom()]</v> - </type> <desc> <p>Gets the values of the specified socket options. </p> @@ -1360,16 +1310,9 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name since="OTP 19.0">getstat(SslSocket) -> - {ok, OptionValues} | {error, inet:posix()}</name> - <name since="OTP 19.0">getstat(SslSocket, OptionNames) -> - {ok, OptionValues} | {error, inet:posix()}</name> + <name since="OTP 19.0" name="getstat" arity="1" /> + <name since="OTP 19.0" name="getstat" arity="2" /> <fsummary>Get one or more statistic options for a socket</fsummary> - <type> - <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> - <v>OptionNames = [atom()]</v> - <v>OptionValues = [{inet:stat_option(), integer()}]</v> - </type> <desc> <p>Gets one or more statistic options for the underlying TCP socket.</p> <p>See inet:getstat/2 for statistic options description.</p> @@ -1377,14 +1320,9 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name since="OTP 21.0">handshake(HsSocket) -> </name> - <name since="OTP 21.0">handshake(HsSocket, Timeout) -> {ok, SslSocket} | {error, Reason}</name> + <name since="OTP 21.0" name="handshake" arity="1" /> + <name since="OTP 21.0" name="handshake" arity="2" clause_i="1" /> <fsummary>Performs server-side SSL/TLS handshake.</fsummary> - <type> - <v>HsSocket = SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> - <v>Timeout = timeout()</v> - <v>Reason = closed | timeout | <seealso marker="#type-error_alert"> error_alert() </seealso></v> - </type> <desc> <p>Performs the SSL/TLS/DTLS server-side handshake.</p> <p>Returns a new TLS/DTLS socket if the handshake is successful.</p> @@ -1397,17 +1335,9 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name since="OTP 21.0">handshake(Socket, Options) -> </name> - <name since="OTP 21.0">handshake(Socket, Options, Timeout) -> {ok, SslSocket} | {ok, SslSocket, Ext} | {error, Reason}</name> + <name since="OTP 21.0" name="handshake" arity="2" clause_i="2" /> + <name since="OTP 21.0" name="handshake" arity="3" /> <fsummary>Performs server-side SSL/TLS/DTLS handshake.</fsummary> - <type> - <v>Socket = socket() | <seealso marker="#type-sslsocket"> socket() </seealso> </v> - <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso> </v> - <v>Ext = <seealso marker="#type-protocol_extensions">protocol_extensions()</seealso></v> - <v>Options = <seealso marker="#type-tls_server_option"> [server_option()] </seealso> </v> - <v>Timeout = timeout()</v> - <v>Reason = closed | timeout | <seealso marker="#type-error_alert"> error_alert() </seealso></v> - </type> <desc> <p>If <c>Socket</c> is a ordinary <c>socket()</c>: upgrades a <c>gen_tcp</c>, or equivalent, socket to an SSL socket, that is, performs @@ -1443,52 +1373,33 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name since="OTP 21.0">handshake_cancel(SslSocket) -> ok </name> + <name since="OTP 21.0" name="handshake_cancel" arity="1" /> <fsummary>Cancel handshake with a fatal alert</fsummary> - <type> - <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> - </type> <desc> <p>Cancel the handshake with a fatal <c>USER_CANCELED</c> alert.</p> </desc> </func> <func> - <name since="OTP 21.0">handshake_continue(HsSocket, Options) -> {ok, SslSocket} | {error, Reason}</name> - <name since="OTP 21.0">handshake_continue(HsSocket, Options, Timeout) -> {ok, SslSocket} | {error, Reason}</name> + <name since="OTP 21.0" name="handshake_continue" arity="2" /> + <name since="OTP 21.0" name="handshake_continue" arity="3" /> <fsummary>Continue the SSL/TLS handshake.</fsummary> - <type> - <v>HsSocket = SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> - <v>Options = <seealso marker="#type-tls_option"> tls_option() </seealso> </v> - <v>Timeout = timeout()</v> - <v>Reason = closed | timeout | <seealso marker="#type-error_alert"> error_alert() </seealso></v> - </type> <desc> <p>Continue the SSL/TLS handshake possiby with new, additional or changed options.</p> </desc> </func> <func> - <name since="">listen(Port, Options) -> - {ok, ListenSocket} | {error, Reason}</name> + <name since="" name="listen" arity="2" /> <fsummary>Creates an SSL listen socket.</fsummary> - <type> - <v>Port = <seealso marker="kernel:inet#type-port_number">inet:port_number()</seealso></v> - <v>Options = <seealso marker="#type-tls_server_option"> [server_option()] </seealso></v> - <v>ListenSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> - </type> <desc> <p>Creates an SSL listen socket.</p> </desc> </func> <func> - <name since="OTP 18.0">negotiated_protocol(SslSocket) -> {ok, Protocol} | {error, protocol_not_negotiated}</name> + <name since="OTP 18.0" name="negotiated_protocol" arity="1" /> <fsummary>Returns the protocol negotiated through ALPN or NPN extensions.</fsummary> - <type> - <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> - <v>Protocol = binary()</v> - </type> <desc> <p> Returns the protocol negotiated through ALPN or NPN extensions. @@ -1497,12 +1408,8 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name since="">peercert(SslSocket) -> {ok, Cert} | {error, Reason}</name> + <name since="" name="peercert" arity="1" /> <fsummary>Returns the peer certificate.</fsummary> - <type> - <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> - <v>Cert = binary()</v> - </type> <desc> <p>The peer certificate is returned as a DER-encoded binary. The certificate can be decoded with @@ -1512,27 +1419,16 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name since="">peername(SslSocket) -> {ok, {Address, Port}} | - {error, Reason}</name> + <name since="" name="peername" arity="1" /> <fsummary>Returns the peer address and port.</fsummary> - <type> - <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> - <v>Address = ipaddress()</v> - <v>Port = <seealso marker="kernel:inet#type-port_number">inet:port_number()</seealso></v> - </type> <desc> <p>Returns the address and port number of the peer.</p> </desc> </func> <func> - <name since="OTP 20.3">prepend_cipher_suites(Preferred, Suites) -> ciphers()</name> + <name since="OTP 20.3" name="prepend_cipher_suites" arity="2" /> <fsummary></fsummary> - <type> - <v>Preferred = <seealso marker="#type-ciphers">ciphers()</seealso> | - <seealso marker="#type-cipher_filters">cipher_filters()</seealso></v> - <v>Suites = <seealso marker="#type-ciphers">ciphers()</seealso></v> - </type> <desc><p>Make <c>Preferred</c> suites become the most preferred suites that is put them at the head of the cipher suite list <c>Suites</c> after removing them from <c>Suites</c> if @@ -1543,15 +1439,8 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name since="OTP R15B01">prf(Socket, Secret, Label, Seed, WantedLength) -> {ok, binary()} | {error, reason()}</name> + <name since="OTP R15B01" name="prf" arity="5" /> <fsummary>Uses a session Pseudo-Random Function to generate key material.</fsummary> - <type> - <v>Socket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> - <v>Secret = binary() | master_secret</v> - <v>Label = binary()</v> - <v>Seed = [binary() | <seealso marker="#type-prf_random"> prf_random()</seealso>]</v> - <v>WantedLength = non_neg_integer()</v> - </type> <desc> <p>Uses the Pseudo-Random Function (PRF) of a TLS session to generate extra key material. It either takes user-generated values for @@ -1563,16 +1452,14 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name since="">recv(SslSocket, Length) -> </name> - <name since="">recv(SslSocket, Length, Timeout) -> {ok, Data} | {error, - Reason}</name> + <name since="" name="recv" arity="2" /> + <name since="" name="recv" arity="3" /> <fsummary>Receives data on a socket.</fsummary> - <type> - <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> - <v>Length = integer()</v> - <v>Timeout = timeout()</v> - <v>Data = [char()] | binary()</v> - </type> + <type_desc variable="HttpPacket">See the description of + <c>HttpPacket</c> in + <seealso marker="erts:erlang#decode_packet/3"><c>erlang:decode_packet/3</c></seealso> + in ERTS. + </type_desc> <desc> <p>Receives a packet from a socket in passive mode. A closed socket is indicated by return value @@ -1590,11 +1477,8 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name since="OTP R14B">renegotiate(SslSocket) -> ok | {error, Reason}</name> + <name since="OTP R14B" name="renegotiate" arity="1" /> <fsummary>Initiates a new handshake.</fsummary> - <type> - <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> - </type> <desc><p>Initiates a new handshake. A notable return value is <c>{error, renegotiation_rejected}</c> indicating that the peer refused to go through with the renegotiation, but the connection @@ -1603,40 +1487,27 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name since="">send(SslSocket, Data) -> ok | {error, Reason}</name> + <name since="" name="send" arity="2" /> <fsummary>Writes data to a socket.</fsummary> - <type> - <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> - <v>Data = iodata()</v> - </type> <desc> - <p>Writes <c>Data</c> to <c>Socket</c>.</p> + <p>Writes <c>Data</c> to <c>SslSocket</c>.</p> <p>A notable return value is <c>{error, closed}</c> indicating that the socket is closed.</p> </desc> </func> <func> - <name since="">setopts(SslSocket, Options) -> ok | {error, Reason}</name> + <name since="" name="setopts" arity="2" /> <fsummary>Sets socket options.</fsummary> - <type> - <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> - <v>Options = <seealso marker="#type-socket_option"> [socket_option()] </seealso></v> - </type> <desc> <p>Sets options according to <c>Options</c> for socket - <c>Socket</c>.</p> + <c>SslSocket</c>.</p> </desc> </func> <func> - <name since="OTP R14B">shutdown(SslSocket, How) -> ok | {error, Reason}</name> + <name since="OTP R14B" name="shutdown" arity="2" /> <fsummary>Immediately closes a socket.</fsummary> - <type> - <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> - <v>How = read | write | read_write</v> - <v>Reason = reason()</v> - </type> <desc> <p>Immediately closes a socket in one or two directions.</p> <p><c>How == write</c> means closing the socket for writing, @@ -1648,14 +1519,9 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name since="">ssl_accept(SslSocket) -> </name> - <name since="">ssl_accept(SslSocket, Timeout) -> ok | {error, Reason}</name> + <name since="" name="ssl_accept" arity="1" /> + <name since="" name="ssl_accept" arity="2" /> <fsummary>Performs server-side SSL/TLS handshake.</fsummary> - <type> - <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> - <v>Timeout = timeout()</v> - <v>Reason = closed | timeout | <seealso marker="#type-error_alert"> error_alert() </seealso></v> - </type> <desc> <p>Deprecated in OTP 21, use <seealso marker="#handshake-1">handshake/[1,2]</seealso> instead.</p> <note><p>handshake/[1,2] always returns a new socket.</p></note> @@ -1663,15 +1529,8 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name since="">ssl_accept(Socket, Options) -> </name> - <name since="OTP R14B">ssl_accept(Socket, Options, Timeout) -> {ok, Socket} | ok | {error, Reason}</name> + <name since="OTP R14B" name="ssl_accept" arity="3" /> <fsummary>Performs server-side SSL/TLS/DTLS handshake.</fsummary> - <type> - <v>Socket = socket() | <seealso marker="#type-sslsocket"> sslsocket() </seealso> </v> - <v>Options = <seealso marker="#type-tls_server_option"> [server_option()] </seealso> </v> - <v>Timeout = timeout()</v> - <v>Reason = closed | timeout | <seealso marker="#type-error_alert"> error_alert() </seealso></v> - </type> <desc> <p>Deprecated in OTP 21, use <seealso marker="#handshake-3">handshake/[2,3]</seealso> instead.</p> <note><p>handshake/[2,3] always returns a new socket.</p></note> @@ -1679,27 +1538,18 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name since="">sockname(SslSocket) -> {ok, {Address, Port}} | - {error, Reason}</name> + <name since="" name="sockname" arity="1" /> <fsummary>Returns the local address and port.</fsummary> - <type> - <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> - <v>Address = <seealso marker="#type-ip_address">ip_address()</seealso></v> - <v>Port = <seealso marker="kernel:inet#type-port_number">inet:port_number()</seealso></v> - </type> <desc> <p>Returns the local address and port number of socket - <c>Socket</c>.</p> + <c>SslSocket</c>.</p> </desc> </func> <func> - <name since="OTP R14B">start() -> </name> + <name since="OTP R14B" name="start" arity="0" /> <name since="OTP R14B">start(Type) -> ok | {error, Reason}</name> <fsummary>Starts the SSL application.</fsummary> - <type> - <v>Type = permanent | transient | temporary</v> - </type> <desc> <p>Starts the SSL application. Default type is <c>temporary</c>.</p> @@ -1707,7 +1557,7 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name since="OTP R14B">stop() -> ok </name> + <name since="OTP R14B" name="stop" arity="0" /> <fsummary>Stops the SSL application.</fsummary> <desc> <p>Stops the SSL application.</p> @@ -1715,28 +1565,18 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name since="OTP 21.0">suite_to_str(CipherSuite) -> String</name> + <name since="OTP 21.0" name="suite_to_str" arity="1" clause_i="1" /> <fsummary>Returns the string representation of a cipher suite.</fsummary> - <type> - <v>CipherSuite = <seealso marker="#type-erl_cipher_suite"> erl_cipher_suite() </seealso></v> - <v>String = string()</v> - </type> <desc> <p>Returns the string representation of a cipher suite.</p> </desc> </func> <func> - <name since="">transport_accept(ListenSocket) -></name> - <name since="">transport_accept(ListenSocket, Timeout) -> - {ok, SslSocket} | {error, Reason}</name> + <name since="" name="transport_accept" arity="1" /> + <name since="" name="transport_accept" arity="2" /> <fsummary>Accepts an incoming connection and prepares for <c>ssl_accept</c>.</fsummary> - <type> - <v>ListenSocket = SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> - <v>Timeout = timeout()</v> - <v>Reason = reason()</v> - </type> <desc> <p>Accepts an incoming connection request on a listen socket. <c>ListenSocket</c> must be a socket returned from @@ -1762,13 +1602,9 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name since="OTP R14B">versions() -> [versions_info()]</name> + <name since="OTP R14B" name="versions" arity="0" /> <fsummary>Returns version information relevant for the SSL application.</fsummary> - <type> - <v>versions_info() = {app_vsn, string()} | {supported | available, [ssl_tls_protocol()]} | - {supported_dtls | available_dtls, [dtls_protocol()]} </v> - </type> <desc> <p>Returns version information relevant for the SSL application.</p> diff --git a/lib/ssl/doc/src/ssl_app.xml b/lib/ssl/doc/src/ssl_app.xml index 893919aeb4..b05caf44ea 100644 --- a/lib/ssl/doc/src/ssl_app.xml +++ b/lib/ssl/doc/src/ssl_app.xml @@ -35,45 +35,10 @@ <description> <p> - The ssl application is an implementation of the SSL/TLS/DTLS protocol in Erlang. + The ssl application is an implementation of the SSL, TLS and DTLS protocols in Erlang. </p> - <list type="bulleted"> - <item>Supported SSL/TLS/DTLS-versions are SSL-3.0, TLS-1.0, - TLS-1.1, TLS-1.2, DTLS-1.0 (based on TLS-1.1), DTLS-1.2 (based on TLS-1.2)</item> - <item>For security reasons SSL-2.0 is not supported. - Interoperability with SSL-2.0 enabled clients dropped. (OTP 21) </item> - <item>For security reasons SSL-3.0 is no longer supported by default, - but can be configured. (OTP 19) </item> - <item>For security reasons RSA key exchange cipher suites are no longer supported by default, - but can be configured. (OTP 21) </item> - <item>For security reasons DES cipher suites are no longer supported by default, - but can be configured. (OTP 20) </item> - <item>For security reasons 3DES cipher suites are no longer supported by default, - but can be configured. (OTP 21) </item> - <item> Renegotiation Indication Extension <url href="http://www.ietf.org/rfc/rfc5746.txt">RFC 5746</url> is supported - </item> - <item>Ephemeral Diffie-Hellman cipher suites are supported, - but not Diffie Hellman Certificates cipher suites.</item> - <item>Elliptic Curve cipher suites are supported if the Crypto - application supports it and named curves are used. - </item> - <item>Export cipher suites are not supported as the - U.S. lifted its export restrictions in early 2000.</item> - <item>IDEA cipher suites are not supported as they have - become deprecated by the latest TLS specification so it is not - motivated to implement them.</item> - <item>Compression is not supported.</item> - <item>CRL validation is supported.</item> - <item>Policy certificate extensions are not supported.</item> - <item>'Server Name Indication' extension - (<url href="http://www.ietf.org/rfc/rfc6066.txt">RFC 6066</url>) is supported.</item> - <item>Application Layer Protocol Negotiation (ALPN) and its successor Next Protocol Negotiation (NPN) - are supported. </item> - <item>It is possible to use Pre-Shared Key (PSK) and Secure Remote Password (SRP) - cipher suites, but they are not enabled by default. - </item> - </list> - </description> + <p>For current statement of standards compliance see the <seealso marker="standards_compliance">User's Guide</seealso>.</p> + </description> <section> <title>DEPENDENCIES</title> diff --git a/lib/ssl/doc/src/ssl_distribution.xml b/lib/ssl/doc/src/ssl_distribution.xml index e14f3f90dc..1774bd8f77 100644 --- a/lib/ssl/doc/src/ssl_distribution.xml +++ b/lib/ssl/doc/src/ssl_distribution.xml @@ -191,7 +191,7 @@ Eshell V5.0 (abort with ^G) Any available SSL/TLS option can be specified in an options file, but note that options that take a <c>fun()</c> has to use the syntax <c>fun Mod:Func/Arity</c> since a function - body can not be compiled when consulting a file. + body cannot be compiled when consulting a file. </p> <p> Do not tamper with the socket options diff --git a/lib/ssl/doc/src/standards_compliance.xml b/lib/ssl/doc/src/standards_compliance.xml new file mode 100644 index 0000000000..25840d0fc6 --- /dev/null +++ b/lib/ssl/doc/src/standards_compliance.xml @@ -0,0 +1,2337 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>2015</year> + <year>2019</year> + <holder>Ericsson AB, All Rights Reserved</holder> + </copyright> + <legalnotice> + 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. + + The Initial Developer of the Original Code is Ericsson AB. + </legalnotice> + + <title>Standards Compliance</title> + <prepared>OTP team</prepared> + <docno></docno> + <date>2019-03-20</date> + <rev>A</rev> + <file>standards_compliance.xml</file> + </header> + + <section> + <title>Purpose</title> + <p>This section describes the current state of standards compliance of the ssl application.</p> + </section> + + <section> + <title>Common (pre TLS 1.3)</title> + <list type="bulleted"> + <item>For security reasons RSA key exchange cipher suites are no longer supported by default, + but can be configured. (OTP 21) + </item> + <item>For security reasons DES cipher suites are no longer supported by default, + but can be configured. (OTP 20) + </item> + <item>For security reasons 3DES cipher suites are no longer supported by default, + but can be configured. (OTP 21) + </item> + <item>Renegotiation Indication Extension <url href="http://www.ietf.org/rfc/rfc5746.txt">RFC 5746</url> is supported + </item> + <item>Ephemeral Diffie-Hellman cipher suites are supported, + but not Diffie Hellman Certificates cipher suites. + </item> + <item>Elliptic Curve cipher suites are supported if the Crypto + application supports it and named curves are used. + </item> + <item>Export cipher suites are not supported as the + U.S. lifted its export restrictions in early 2000. + </item> + <item>IDEA cipher suites are not supported as they have + become deprecated by the TLS 1.2 specification so it is not + motivated to implement them. + </item> + <item>Compression is not supported. + </item> + </list> + </section> + + <section> + <title>Common</title> + <list type="bulleted"> + <item>CRL validation is supported.</item> + <item>Policy certificate extensions are not supported.</item> + <item>'Server Name Indication' extension + (<url href="http://www.ietf.org/rfc/rfc6066.txt">RFC 6066</url>) is supported.</item> + <item>Application Layer Protocol Negotiation (ALPN) and its successor Next Protocol Negotiation (NPN) are supported. </item> + <item>It is possible to use Pre-Shared Key (PSK) and Secure Remote Password (SRP) + cipher suites, but they are not enabled by default. + </item> + </list> + </section> + + + <section> + <title>SSL 2.0</title> + <p>For security reasons SSL-2.0 is not supported. Interoperability with SSL-2.0 enabled clients dropped. (OTP 21)</p> + </section> + + <section> + <title>SSL 3.0</title> + <p>For security reasons SSL-3.0 is no longer supported by default, but can be configured. (OTP 19)</p> + </section> + + <section> + <title>TLS 1.0</title> + <p>For security reasons TLS-1.0 is no longer supported by default, but can be configured. (OTP 22)</p> + </section> + + <section> + <title>TLS 1.1</title> + <p>For security reasons TLS-1.1 is no longer supported by default, but can be configured. (OTP 22)</p> + </section> + + <section> + <title>TLS 1.2</title> + <p>Supported</p> + </section> + + <section> + <title>DTLS 1.0</title> + <p>For security reasons DTLS-1.0 (based on TLS 1.1) is no longer supported by default, but can be configured. (OTP 22)</p> + </section> + + <section> + <title>DTLS 1.2</title> + <p>Supported (based on TLS 1.2)</p> + </section> + + <section> + <title>DTLS 1.3</title> + <p>Not yet supported</p> + </section> + + <section> + <title>TLS 1.3</title> + <p>OTP-22 introduces basic support for TLS 1.3 on the server side. Basic functionality + covers a simple TLS 1.3 handshake with support of the mandatory extensions + (supported_groups, signature_algorithms, key_share, supported_versions and + signature_algorithms_cert). The server supports a selective set of cryptographic algorithms:</p> + <list type="bulleted"> + <item>Key Exchange: ECDHE</item> + <item>Groups: all standard groups supported for the Diffie-Hellman key exchange</item> + <item>Ciphers: TLS_AES_128_GCM_SHA256 and TLS_AES_256_GCM_SHA384</item> + <item>Signature Algorithms: RSA and RSA PSS</item> + <item>Certificates: currently only certificates with RSA keys are supported</item> + </list> + <p>Other notable features:</p> + <list type="bulleted"> + <item>The server supports the HelloRetryRequest mechanism</item> + <item>PSK and session resumption not supported</item> + <item>Early data and 0-RTT not supported</item> + <item>Key and Initialization Vector Update not supported</item> + </list> + <p>For more detailed information see the + <seealso marker="#soc_table">Standards Compliance</seealso> below.</p> + <warning><p>Note that the client side is not yet functional. It is planned to be released + later in OTP-22.</p></warning> + + <p> The following table describes the current state of standards compliance for TLS 1.3.</p> + <p>(<em>C</em> = Compliant, <em>NC</em> = Non-Compliant, <em>PC</em> = Partially-Compliant, + <em>NA</em> = Not Applicable)</p> + <marker id="soc_table"/> + <table> + <row> + <cell align="left" valign="middle"><em>Section</em></cell> + <cell align="left" valign="middle"><em>Feature</em></cell> + <cell align="left" valign="middle"><em>State</em></cell> + <cell align="left" valign="middle"><em>Since</em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-1.2"> + 1.3. Updates Affecting TLS 1.2 + </url> + </cell> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">Version downgrade protection mechanism</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">RSASSA-PSS signature schemes</cell> + <cell align="left" valign="middle"><em>PC</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">supported_versions (ClientHello) extension</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">signature_algorithms_cert extension</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-2"> + 2. Protocol Overview + </url> + </cell> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>PC</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">(EC)DHE</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">PSK-only</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">PSK with (EC)DHE</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-2.1"> + 2.1. Incorrect DHE share + </url> + </cell> + <cell align="left" valign="middle">HelloRetryRequest</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-2.2"> + 2.2. Resumption and Pre-Shared Key (PSK) + </url> + </cell> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-2.3"> + 2.3. 0-RTT Data + </url> + </cell> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-4.1.1"> + 4.1.1. Cryptographic Negotiation + </url> + </cell> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>PC</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">supported_groups extension</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">signature_algorithms extension</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">pre_shared_key extension</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-4.1.2"> + 4.1.2. Client Hello + </url> + </cell> + <cell align="left" valign="middle"><em>Client</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">server_name (RFC6066)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">max_fragment_length (RFC6066)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">status_request (RFC6066)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">supported_groups (RFC7919)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">signature_algorithms (RFC8446)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">use_srtp (RFC5764)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">heartbeat (RFC6520)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">application_layer_protocol_negotiation (RFC7301)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">signed_certificate_timestamp (RFC6962)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">client_certificate_type (RFC7250)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">server_certificate_type (RFC7250)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">padding (RFC7685)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">key_share (RFC8446)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">pre_shared_key (RFC8446)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">psk_key_exchange_modes (RFC8446)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">early_data (RFC8446)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">cookie (RFC8446) </cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">supported_versions (RFC8446)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">certificate_authorities (RFC8446)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">oid_filters (RFC8446)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">post_handshake_auth (RFC8446)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">signature_algorithms_cert (RFC8446)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>Server</em></cell> + <cell align="left" valign="middle"><em>PC</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">server_name (RFC6066)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">max_fragment_length (RFC6066)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">status_request (RFC6066)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">supported_groups (RFC7919)</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">signature_algorithms (RFC8446)</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">use_srtp (RFC5764)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">heartbeat (RFC6520)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">application_layer_protocol_negotiation (RFC7301)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">signed_certificate_timestamp (RFC6962)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">client_certificate_type (RFC7250)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">server_certificate_type (RFC7250)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">padding (RFC7685)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">key_share (RFC8446)</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">pre_shared_key (RFC8446)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">psk_key_exchange_modes (RFC8446)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">early_data (RFC8446)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">cookie (RFC8446) </cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">supported_versions (RFC8446)</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">certificate_authorities (RFC8446)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">oid_filters (RFC8446)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">post_handshake_auth (RFC8446)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">signature_algorithms_cert (RFC8446)</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-4.1.3"> + 4.1.3. Server Hello + </url> + </cell> + <cell align="left" valign="middle"><em>Client</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">Version downgrade protection</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">key_share (RFC8446)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">pre_shared_key (RFC8446)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">supported_versions (RFC8446)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>Server</em></cell> + <cell align="left" valign="middle"><em>PC</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">Version downgrade protection</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">key_share (RFC8446)</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">pre_shared_key (RFC8446)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">supported_versions (RFC8446)</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-4.1.4"> + 4.1.4. Hello Retry Request + </url> + </cell> + <cell align="left" valign="middle"><em>Server</em></cell> + <cell align="left" valign="middle"><em>PC</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">key_share (RFC8446)</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">cookie (RFC8446)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">supported_versions (RFC8446)</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-4.2.1"> + 4.2.1. Supported Versions + </url> + </cell> + <cell align="left" valign="middle"><em>Client</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>Server</em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-4.2.2"> + 4.2.2. Cookie + </url> + </cell> + <cell align="left" valign="middle"><em>Client</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>Server</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"><em></em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-4.2.3"> + 4.2.3. Signature Algorithms + </url> + </cell> + <cell align="left" valign="middle"><em>Client</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">rsa_pkcs1_sha256</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">rsa_pkcs1_sha384</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">rsa_pkcs1_sha512</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">ecdsa_secp256r1_sha256</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">ecdsa_secp384r1_sha384</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">ecdsa_secp521r1_sha512</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">rsa_pss_rsae_sha256</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">rsa_pss_rsae_sha384</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">rsa_pss_rsae_sha512</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">ed25519</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">ed448</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">rsa_pss_pss_sha256</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">rsa_pss_pss_sha384</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">rsa_pss_pss_sha512</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">rsa_pkcs1_sha1</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">ecdsa_sha1</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>Server</em></cell> + <cell align="left" valign="middle"><em>PC</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">rsa_pkcs1_sha256</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">rsa_pkcs1_sha384</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">rsa_pkcs1_sha512</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">ecdsa_secp256r1_sha256</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">ecdsa_secp384r1_sha384</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">ecdsa_secp521r1_sha512</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">rsa_pss_rsae_sha256</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">rsa_pss_rsae_sha384</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">rsa_pss_rsae_sha512</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">ed25519</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">ed448</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">rsa_pss_pss_sha256</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">rsa_pss_pss_sha384</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">rsa_pss_pss_sha512</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">rsa_pkcs1_sha1</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">ecdsa_sha1</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-4.2.4"> + 4.2.4. Certificate Authorities + </url> + </cell> + <cell align="left" valign="middle"><em>Client</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>Server</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"><em></em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-4.2.5"> + 4.2.5. OID Filters + </url> + </cell> + <cell align="left" valign="middle"><em>Client</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>Server</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"><em></em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-4.2.6"> + 4.2.6. Post-Handshake Client Authentication + </url> + </cell> + <cell align="left" valign="middle"><em>Client</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>Server</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"><em></em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-4.2.7"> + 4.2.7. Supported Groups + </url> + </cell> + <cell align="left" valign="middle"><em>Client</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">secp256r1</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">secp384r1</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">secp521r1</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">x25519</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">x448</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">ffdhe2048</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">ffdhe3072</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">ffdhe4096</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">ffdhe6144</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">ffdhe8192</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>Server</em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">secp256r1</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">secp384r1</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">secp521r1</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">x25519</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">x448</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">ffdhe2048</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">ffdhe3072</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">ffdhe4096</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">ffdhe6144</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">ffdhe8192</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-4.2.8"> + 4.2.8. Key Share + </url> + </cell> + <cell align="left" valign="middle"><em>Client</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>Server</em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-4.2.9"> + 4.2.9. Pre-Shared Key Exchange Modes + </url> + </cell> + <cell align="left" valign="middle"><em>Client</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>Server</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"><em></em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-4.2.10"> + 4.2.10. Early Data Indication + </url> + </cell> + <cell align="left" valign="middle"><em>Client</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>Server</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"><em></em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-4.2.11"> + 4.2.11. Pre-Shared Key Extension + </url> + </cell> + <cell align="left" valign="middle"><em>Client</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>Server</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"><em></em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-4.2.11.1"> + 4.2.11.1. Ticket Age + </url> + </cell> + <cell align="left" valign="middle"><em>Client</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>Server</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"><em></em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-4.2.11.2"> + 4.2.11.2. PSK Binder + </url> + </cell> + <cell align="left" valign="middle"><em>Client</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>Server</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"><em></em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-4.2.11.3"> + 4.2.11.3. Processing Order + </url> + </cell> + <cell align="left" valign="middle"><em>Client</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>Server</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"><em></em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-4.3.1"> + 4.3.1. Encrypted Extensions + </url> + </cell> + <cell align="left" valign="middle"><em>Client</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">server_name (RFC6066)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">max_fragment_length (RFC6066)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">supported_groups (RFC7919)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">use_srtp (RFC5764)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">heartbeat (RFC6520)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">application_layer_protocol_negotiation (RFC7301)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">client_certificate_type (RFC7250)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">server_certificate_type (RFC7250)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">early_data (RFC8446)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">supported_versions (RFC8446)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>Server</em></cell> + <cell align="left" valign="middle"><em>PC</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">server_name (RFC6066)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">max_fragment_length (RFC6066)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">supported_groups (RFC7919)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">use_srtp (RFC5764)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">heartbeat (RFC6520)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">application_layer_protocol_negotiation (RFC7301)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">client_certificate_type (RFC7250)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">server_certificate_type (RFC7250)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">early_data (RFC8446)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">supported_versions (RFC8446)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-4.3.2"> + 4.3.2. Certificate Request + </url> + </cell> + <cell align="left" valign="middle"><em>Client</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">status_request (RFC6066)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">signature_algorithms (RFC8446)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">signed_certificate_timestamp (RFC6962)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">certificate_authorities (RFC8446)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">oid_filters (RFC8446)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">signature_algorithms_cert (RFC8446)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>Server</em></cell> + <cell align="left" valign="middle"><em>PC</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">status_request (RFC6066)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">signature_algorithms (RFC8446)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">signed_certificate_timestamp (RFC6962)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">certificate_authorities (RFC8446)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">oid_filters (RFC8446)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">signature_algorithms_cert (RFC8446)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-4.4.1"> + 4.4.1. The Transcript Hash + </url> + </cell> + <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-4.4.2"> + 4.4.2. Certificate + </url> + </cell> + <cell align="left" valign="middle"><em>Client</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">status_request (RFC6066)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">signed_certificate_timestamp (RFC6962)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>Server</em></cell> + <cell align="left" valign="middle"><em>PC</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">status_request (RFC6066)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">signed_certificate_timestamp (RFC6962)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-4.4.2.1"> + 4.4.2.1. OCSP Status and SCT Extensions + </url> + </cell> + <cell align="left" valign="middle"><em>Client</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>Server</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"><em></em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-4.4.2.2"> + 4.4.2.2. Server Certificate Selection + </url> + </cell> + <cell align="left" valign="middle"><em>Client</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"><em></em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">certificate type MUST be X.509v3</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"><em></em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">certificate's public key is compatible</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"><em></em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">The certificate MUST allow the key to be used for signing</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"><em></em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">server_name and certificate_authorities are used</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"><em></em></cell> + </row> + + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>Server</em></cell> + <cell align="left" valign="middle"><em>PC</em></cell> + <cell align="left" valign="middle"><em></em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">certificate type MUST be X.509v3</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">certificate's public key is compatible</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">The certificate MUST allow the key to be used for signing</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">server_name and certificate_authorities are used</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-4.4.2.3"> + 4.4.2.3. Client Certificate Selection + </url> + </cell> + <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"><em></em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-4.4.2.4"> + 4.4.2.4. Receiving a Certificate Message + </url> + </cell> + <cell align="left" valign="middle"><em>Client</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>Server</em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-4.4.3"> + 4.4.3. Certificate Verify + </url> + </cell> + <cell align="left" valign="middle"><em>Client</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>Server</em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-4.4.4"> + 4.4.4. Finished + </url> + </cell> + <cell align="left" valign="middle"><em>Client</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>Server</em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-4.5"> + 4.5. End of Early Data + </url> + </cell> + <cell align="left" valign="middle"><em>Client</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>Server</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"><em></em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-4.6.1"> + 4.6.1. New Session Ticket Message + </url> + </cell> + <cell align="left" valign="middle"><em>Client</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">early_data (RFC8446)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>Server</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"><em></em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">early_data (RFC8446)</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-4.6.2"> + 4.6.2. Post-Handshake Authentication + </url> + </cell> + <cell align="left" valign="middle"><em>Client</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>Server</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"><em></em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-4.6.3"> + 4.6.3. Key and Initialization Vector Update + </url> + </cell> + <cell align="left" valign="middle"><em>Client</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>Server</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"><em></em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-5.1"> + 5.1. Record Layer + </url> + </cell> + <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">MUST NOT be interleaved with other record types</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">MUST NOT span key changes</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">MUST NOT send zero-length fragments</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">Alert messages MUST NOT be fragmented</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-5.2"> + 5.2. Record Payload Protection + </url> + </cell> + <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-5.3"> + 5.3. Per-Record Nonce + </url> + </cell> + <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-5.4"> + 5.4. Record Padding + </url> + </cell> + <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>PC</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">MAY choose to pad</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">MUST NOT send Handshake and Alert records that have a zero-length TLSInnerPlaintext.content</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">The padding sent is automatically verified</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-5.5"> + 5.5. Limits on Key Usage + </url> + </cell> + <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"><em></em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-6.1"> + 6.1. Closure Alerts + </url> + </cell> + <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"><em></em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">close_notify</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">user_cancelled</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-6.2"> + 6.2. Error Alerts + </url> + </cell> + <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>PC</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-7.1"> + 7.1. Key Schedule + </url> + </cell> + <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-7.2"> + 7.2. Updating Traffic Secrets + </url> + </cell> + <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-7.3"> + 7.3. Traffic Key Calculation + </url> + </cell> + <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-7.5"> + 7.5. Exporters + </url> + </cell> + <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"><em></em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-8"> + 8. 0-RTT and Anti-Replay + </url> + </cell> + <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"><em></em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-8.1"> + 8.1. Single-Use Tickets + </url> + </cell> + <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"><em></em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-8.2"> + 8.2. Client Hello Recording + </url> + </cell> + <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"><em></em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-8.3"> + 8.3. Freshness Checks + </url> + </cell> + <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"><em></em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-9.1"> + 9.1. Mandatory-to-Implement Cipher Suites + </url> + </cell> + <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>PC</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">MUST implement the TLS_AES_128_GCM_SHA256</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">SHOULD implement the TLS_AES_256_GCM_SHA384</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">SHOULD implement the TLS_CHACHA20_POLY1305_SHA256</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>Digital signatures</em></cell> + <cell align="left" valign="middle"><em>PC</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">MUST support rsa_pkcs1_sha256 (for certificates)</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">MUST support rsa_pss_rsae_sha256 (for CertificateVerify and certificates)</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">MUST support ecdsa_secp256r1_sha256</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>Key Exchange</em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">MUST support key exchange with secp256r1</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">SHOULD support key exchange with X25519</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-9.2"> + 9.2. Mandatory-to-Implement Extensions + </url> + </cell> + <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>PC</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">Supported Versions</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">Cookie</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">Signature Algorithms</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">Signature Algorithms Certificate</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">Negotiated Groups</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">Key Share</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">Server Name Indication</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>MUST send and use these extensions</em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">"supported_versions" is REQUIRED for ClientHello, ServerHello and HelloRetryRequest</cell> + <cell align="left" valign="middle"><em>PC</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">"signature_algorithms" is REQUIRED for certificate authentication</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">"supported_groups" is REQUIRED for ClientHello messages using (EC)DHE key exchange</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">"key_share" is REQUIRED for (EC)DHE key exchange</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">"pre_shared_key" is REQUIRED for PSK key agreement</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">"psk_key_exchange_modes" is REQUIRED for PSK key agreement</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>TLS 1.3 ClientHello</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"><em></em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">If not containing a "pre_shared_key" extension, it MUST contain both a "signature_algorithms" extension and a "supported_groups" extension.</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">If containing a "supported_groups" extension, it MUST also contain a "key_share" extension, and vice versa. An empty KeyShare.client_shares vector is permitted.</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>TLS 1.3 ServerHello</em></cell> + <cell align="left" valign="middle"><em>PC</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">MUST support the use of the "server_name" extension</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-9.3"> + 9.3. Protocol Invariants + </url> + </cell> + <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"><em></em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>MUST correctly handle extensible fields</em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"><em></em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">A client sending a ClientHello MUST support all parameters advertised in it.</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">A middlebox which terminates a TLS connection MUST behave as a compliant TLS server</cell> + <cell align="left" valign="middle"><em>NA</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">A middlebox which forwards ClientHello parameters it does not understand MUST NOT process any messages beyond that ClientHello.</cell> + <cell align="left" valign="middle"><em>NA</em></cell> + <cell align="left" valign="middle"></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-B.4"> + B.4. Cipher Suites + </url> + </cell> + <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>PC</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">TLS_AES_128_GCM_SHA256</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">TLS_AES_256_GCM_SHA384</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle">22</cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">TLS_CHACHA20_POLY1305_SHA256</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">TLS_AES_128_CCM_SHA256</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">TLS_AES_128_CCM_8_SHA256</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-C.1"> + C.1. Random Number Generation and Seeding + </url> + </cell> + <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-C.2"> + C.2. Certificates and Authentication + </url> + </cell> + <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-C.3"> + C.3. Implementation Pitfalls + </url> + </cell> + <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>PC</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-C.4"> + C.4. Client Tracking Prevention + </url> + </cell> + <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"><em></em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-C.5"> + C.5. Unauthenticated Operation + </url> + </cell> + <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-D.1"> + D.1. Negotiating with an Older Server + </url> + </cell> + <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"><em></em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-D.2"> + D.2. Negotiating with an Older Client + </url> + </cell> + <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-D.3"> + D.3. 0-RTT Backward Compatibility + </url> + </cell> + <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"><em></em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-D.4"> + D.4. Middlebox Compatibility Mode + </url> + </cell> + <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>PC</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + + <row> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-D.5"> + D.5. Security Restrictions Related to Backward Compatibility + </url> + </cell> + <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> + </row> + + <tcaption>Standards Compliance</tcaption> + </table> + + </section> + +</chapter> diff --git a/lib/ssl/doc/src/usersguide.xml b/lib/ssl/doc/src/usersguide.xml index 23ccf668c3..b22b2456e4 100644 --- a/lib/ssl/doc/src/usersguide.xml +++ b/lib/ssl/doc/src/usersguide.xml @@ -38,6 +38,7 @@ <xi:include href="ssl_protocol.xml"/> <xi:include href="using_ssl.xml"/> <xi:include href="ssl_distribution.xml"/> + <xi:include href="standards_compliance.xml"/> </part> diff --git a/lib/ssl/src/Makefile b/lib/ssl/src/Makefile index 8d1341f594..8dc76f2638 100644 --- a/lib/ssl/src/Makefile +++ b/lib/ssl/src/Makefile @@ -39,60 +39,80 @@ RELSYSDIR = $(RELEASE_PATH)/lib/ssl-$(VSN) # ---------------------------------------------------- BEHAVIOUR_MODULES= \ - ssl_session_cache_api \ - ssl_crl_cache_api + ssl_crl_cache_api \ + ssl_session_cache_api + MODULES= \ - ssl \ - ssl_alert \ - ssl_app \ - ssl_sup \ - ssl_admin_sup\ - tls_connection_sup \ - ssl_connection_sup \ - ssl_listen_tracker_sup\ + dtls_connection \ dtls_connection_sup \ - dtls_packet_demux \ + dtls_handshake \ dtls_listener_sup \ - ssl_dist_sup\ - ssl_dist_admin_sup\ - ssl_dist_connection_sup\ + dtls_packet_demux \ + dtls_record \ + dtls_socket \ + dtls_v1 \ inet_tls_dist \ inet6_tls_dist \ - ssl_certificate\ - ssl_pkix_db\ + ssl \ + ssl_admin_sup \ + ssl_alert \ + ssl_app \ + ssl_certificate \ ssl_cipher \ ssl_cipher_format \ - ssl_srp_primes \ - tls_connection \ - dtls_connection \ - tls_sender\ ssl_config \ ssl_connection \ - tls_handshake \ - dtls_handshake\ - ssl_handshake\ - ssl_manager \ - ssl_session \ - ssl_session_cache \ - ssl_pem_cache \ - ssl_crl\ + ssl_connection_sup \ + ssl_crl \ ssl_crl_cache \ ssl_crl_hash_dir \ - tls_socket \ - dtls_socket \ - tls_record \ - dtls_record \ + ssl_dh_groups \ + ssl_dist_admin_sup \ + ssl_dist_connection_sup \ + ssl_dist_sup \ + ssl_handshake \ + ssl_listen_tracker_sup \ + ssl_logger \ + ssl_manager \ + ssl_pem_cache \ + ssl_pkix_db \ ssl_record \ + ssl_session \ + ssl_session_cache \ + ssl_srp_primes \ + ssl_sup \ ssl_v3 \ - tls_v1 \ - dtls_v1 + tls_connection \ + tls_connection_sup \ + tls_connection_1_3 \ + tls_handshake \ + tls_handshake_1_3 \ + tls_record \ + tls_record_1_3 \ + tls_sender \ + tls_socket \ + tls_v1 + INTERNAL_HRL_FILES = \ - ssl_alert.hrl ssl_cipher.hrl \ - tls_connection.hrl dtls_connection.hrl ssl_connection.hrl \ - ssl_handshake.hrl tls_handshake.hrl dtls_handshake.hrl ssl_api.hrl ssl_internal.hrl \ - ssl_record.hrl tls_record.hrl dtls_record.hrl ssl_srp.hrl + dtls_connection.hrl \ + dtls_handshake.hrl \ + dtls_record.hrl \ + ssl_alert.hrl \ + ssl_api.hrl \ + ssl_cipher.hrl \ + ssl_connection.hrl \ + ssl_handshake.hrl \ + ssl_internal.hrl \ + ssl_record.hrl \ + ssl_srp.hrl \ + tls_connection.hrl \ + tls_handshake.hrl \ + tls_handshake_1_3.hrl \ + tls_record.hrl \ + tls_record_1_3.hrl + ERL_FILES= \ $(MODULES:%=%.erl) \ @@ -111,6 +131,10 @@ APP_TARGET= $(EBIN)/$(APP_FILE) APPUP_SRC= $(APPUP_FILE).src APPUP_TARGET= $(EBIN)/$(APPUP_FILE) +DEPDIR=$(ERL_TOP)/lib/ssl/src/deps +DEP_FILE=$(DEPDIR)/ssl.d +$(shell mkdir -p $(dir $(DEP_FILE)) >/dev/null) + # ---------------------------------------------------- # FLAGS # ---------------------------------------------------- @@ -118,7 +142,7 @@ EXTRA_ERLC_FLAGS = +warn_unused_vars ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/kernel/src \ -pz $(EBIN) \ -pz $(ERL_TOP)/lib/public_key/ebin \ - $(EXTRA_ERLC_FLAGS) -DVSN=\"$(VSN)\" + $(EXTRA_ERLC_FLAGS) # ---------------------------------------------------- @@ -127,11 +151,22 @@ ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/kernel/src \ $(TARGET_FILES): $(BEHAVIOUR_TARGET_FILES) -debug opt: $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) +$(DEP_FILE): $(ERL_FILES) + $(gen_verbose)erlc -M $(ERL_FILES) \ + | sed "s@$(ERL_TOP)@../../..@g" \ + | sed "s/\.$(EMULATOR)/\.$$\(EMULATOR\)/" \ + | sed 's@^dtls_@$$(EBIN)/dtls_@' \ + | sed 's@^inet_@$$(EBIN)/inet_@' \ + | sed 's@^ssl_@$$(EBIN)/ssl_@' \ + | sed 's@^tls_@$$(EBIN)/tls_@' \ + > $(DEP_FILE) + +debug opt: $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(DEP_FILE) clean: rm -f $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(BEHAVIOUR_TARGET_FILES) rm -f errs core *~ + rm -rf $(DEPDIR) $(APP_TARGET): $(APP_SRC) ../vsn.mk $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@ @@ -141,7 +176,6 @@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk docs: - # ---------------------------------------------------- # Release Target # ---------------------------------------------------- @@ -159,22 +193,4 @@ release_docs_spec: # ---------------------------------------------------- # Dependencies # ---------------------------------------------------- -$(EBIN)/inet_tls_dist.$(EMULATOR): ../../kernel/include/net_address.hrl ../../kernel/include/dist.hrl ../../kernel/include/dist_util.hrl -$(EBIN)/tls.$(EMULATOR): ssl_internal.hrl ssl_record.hrl ssl_cipher.hrl ssl_handshake.hrl ../../public_key/include/public_key.hrl -$(EBIN)/ssl_alert.$(EMULATOR): ssl_alert.hrl ssl_record.hrl -$(EBIN)/ssl_certificate.$(EMULATOR): ssl_internal.hrl ssl_alert.hrl ssl_handshake.hrl ../../public_key/include/public_key.hrl -$(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)/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 -$(EBIN)/ssl_manager.$(EMULATOR): ssl_internal.hrl ssl_handshake.hrl ../../kernel/include/file.hrl -$(EBIN)/ssl_record.$(EMULATOR): ssl_internal.hrl ssl_record.hrl ssl_cipher.hrl ssl_handshake.hrl ssl_alert.hrl -$(EBIN)/ssl_session.$(EMULATOR): ssl_internal.hrl ssl_handshake.hrl -$(EBIN)/ssl_session_cache.$(EMULATOR): ssl_internal.hrl ssl_handshake.hrl -$(EBIN)/ssl_session_cache_api.$(EMULATOR): ssl_internal.hrl ssl_handshake.hrl -$(EBIN)/ssl_ssl3.$(EMULATOR): ssl_internal.hrl ssl_record.hrl ssl_cipher.hrl -$(EBIN)/ssl_tls1.$(EMULATOR): ssl_internal.hrl ssl_record.hrl ssl_cipher.hrl -$(EBIN)/ssl_cache.$(EMULATOR): ssl_cache.erl ssl_internal.hrl ../../public_key/include/public_key.hrl - +-include $(DEP_FILE) diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index 2c6b71c97a..e070006900 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -32,6 +32,7 @@ -include("ssl_internal.hrl"). -include("ssl_srp.hrl"). -include_lib("public_key/include/public_key.hrl"). +-include_lib("kernel/include/logger.hrl"). %% Internal application API @@ -66,7 +67,7 @@ %% Setup %%==================================================================== start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = false},_, Tracker} = Opts, - User, {CbModule, _,_, _} = CbInfo, + User, {CbModule, _, _, _, _} = CbInfo, Timeout) -> try {ok, Pid} = dtls_connection_sup:start_child([Role, Host, Port, Socket, @@ -146,13 +147,16 @@ next_record(#state{static_env = #static_env{role = server, socket = {Listener, {Client, _}}}} = State) -> dtls_packet_demux:active_once(Listener, Client, self()), {no_record, State}; -next_record(#state{static_env = #static_env{role = client, +next_record(#state{protocol_specific = #{active_n_toggle := true, + active_n := N} = ProtocolSpec, + static_env = #static_env{role = client, socket = {_Server, Socket} = DTLSSocket, close_tag = CloseTag, transport_cb = Transport}} = State) -> - case dtls_socket:setopts(Transport, Socket, [{active,once}]) of + case dtls_socket:setopts(Transport, Socket, [{active,N}]) of ok -> - {no_record, State}; + {no_record, State#state{protocol_specific = + ProtocolSpec#{active_n_toggle => false}}}; _ -> self() ! {CloseTag, DTLSSocket}, {no_record, State} @@ -192,7 +196,8 @@ next_event(StateName, no_record, %% TODO maybe buffer later epoch next_event(StateName, no_record, State, Actions); {#alert{} = Alert, State} -> - {next_state, StateName, State, [{next_event, internal, Alert} | Actions]} + Version = State#state.connection_env#connection_env.negotiated_version, + handle_own_alert(Alert, Version, StateName, State) end; next_event(connection = StateName, Record, #state{connection_states = #{current_read := #{epoch := CurrentEpoch}}} = State0, Actions) -> @@ -232,7 +237,8 @@ next_event(StateName, Record, %% TODO maybe buffer later epoch next_event(StateName, no_record, State0, Actions); #alert{} = Alert -> - {next_state, StateName, State0, [{next_event, internal, Alert} | Actions]} + Version = State0#state.connection_env#connection_env.negotiated_version, + handle_own_alert(Alert, Version, StateName, State0) end. %%% DTLS record protocol level application data messages @@ -249,10 +255,11 @@ handle_protocol_record(#ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, Stat handle_protocol_record(#ssl_tls{type = ?HANDSHAKE, fragment = Data}, StateName, - #state{protocol_buffers = Buffers0, - connection_env = #connection_env{negotiated_version = Version}} = State) -> + #state{protocol_buffers = Buffers0, + connection_env = #connection_env{negotiated_version = Version}, + ssl_options = Options} = State) -> try - case dtls_handshake:get_dtls_handshake(Version, Data, Buffers0) of + case dtls_handshake:get_dtls_handshake(Version, Data, Buffers0, Options) of {[], Buffers} -> next_event(StateName, no_record, State#state{protocol_buffers = Buffers}); {Packets, Buffers} -> @@ -287,9 +294,10 @@ handle_protocol_record(#ssl_tls{type = _Unknown}, StateName, State) -> %% Handshake handling %%==================================================================== -renegotiate(#state{static_env = #static_env{role = client}} = State, Actions) -> +renegotiate(#state{static_env = #static_env{role = client}} = State0, Actions) -> %% Handle same way as if server requested %% the renegotiation + State = reinit_handshake_data(State0), {next_state, connection, State, [{next_event, internal, #hello_request{}} | Actions]}; @@ -307,9 +315,12 @@ queue_handshake(Handshake0, #state{handshake_env = #handshake_env{tls_handshake_ connection_env = #connection_env{negotiated_version = Version}, flight_buffer = #{handshakes := HsBuffer0, change_cipher_spec := undefined, - next_sequence := Seq} = Flight0} = State) -> + next_sequence := Seq} = Flight0, + ssl_options = SslOpts} = State) -> Handshake = dtls_handshake:encode_handshake(Handshake0, Version, Seq), Hist = update_handshake_history(Handshake0, Handshake, Hist0), + ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'handshake', Handshake0), + State#state{flight_buffer = Flight0#{handshakes => [Handshake | HsBuffer0], next_sequence => Seq +1}, handshake_env = HsEnv#handshake_env{tls_handshake_history = Hist}}; @@ -317,9 +328,12 @@ queue_handshake(Handshake0, #state{handshake_env = #handshake_env{tls_handshake_ queue_handshake(Handshake0, #state{handshake_env = #handshake_env{tls_handshake_history = Hist0} = HsEnv, connection_env = #connection_env{negotiated_version = Version}, flight_buffer = #{handshakes_after_change_cipher_spec := Buffer0, - next_sequence := Seq} = Flight0} = State) -> + next_sequence := Seq} = Flight0, + ssl_options = SslOpts} = State) -> Handshake = dtls_handshake:encode_handshake(Handshake0, Version, Seq), Hist = update_handshake_history(Handshake0, Handshake, Hist0), + ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'handshake', Handshake0), + State#state{flight_buffer = Flight0#{handshakes_after_change_cipher_spec => [Handshake | Buffer0], next_sequence => Seq +1}, handshake_env = HsEnv#handshake_env{tls_handshake_history = Hist}}. @@ -327,7 +341,7 @@ queue_handshake(Handshake0, #state{handshake_env = #handshake_env{tls_handshake_ queue_change_cipher(ChangeCipher, #state{flight_buffer = Flight, connection_states = ConnectionStates0} = State) -> ConnectionStates = - dtls_record:next_epoch(ConnectionStates0, write), + dtls_record:next_epoch(ConnectionStates0, write), State#state{flight_buffer = Flight#{change_cipher_spec => ChangeCipher}, connection_states = ConnectionStates}. @@ -350,8 +364,8 @@ reinit_handshake_data(#state{static_env = #static_env{data_tag = DataTag}, dtls_handshake_later_fragments = [] }}. -select_sni_extension(#client_hello{extensions = HelloExtensions}) -> - HelloExtensions#hello_extensions.sni; +select_sni_extension(#client_hello{extensions = #{sni := SNI}}) -> + SNI; select_sni_extension(_) -> undefined. @@ -367,11 +381,14 @@ encode_alert(#alert{} = Alert, Version, ConnectionStates) -> send_alert(Alert, #state{static_env = #static_env{socket = Socket, transport_cb = Transport}, + connection_env = #connection_env{negotiated_version = Version}, - connection_states = ConnectionStates0} = State0) -> + connection_states = ConnectionStates0, + ssl_options = SslOpts} = State0) -> {BinMsg, ConnectionStates} = encode_alert(Alert, Version, ConnectionStates0), send(Transport, Socket, BinMsg), + ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'record', BinMsg), State0#state{connection_states = ConnectionStates}. send_alert_in_connection(Alert, State) -> @@ -438,8 +455,7 @@ init({call, From}, {start, Timeout}, session = Session0#session{session_id = Hello#client_hello.session_id}, start_or_recv_from = From}, - {Record, State} = next_record(State3), - next_event(hello, Record, State, [{{timeout, handshake}, Timeout, close} | Actions]); + next_event(hello, no_record, State3, [{{timeout, handshake}, Timeout, close} | Actions]); init({call, _} = Type, Event, #state{static_env = #static_env{role = server}, protocol_specific = PS} = State) -> Result = gen_handshake(?FUNCTION_NAME, Type, Event, @@ -497,9 +513,8 @@ hello(internal, #client_hello{cookie = <<>>, %% negotiated. VerifyRequest = dtls_handshake:hello_verify_request(Cookie, ?HELLO_VERIFY_REQUEST_VERSION), State1 = prepare_flight(State0#state{connection_env = CEnv#connection_env{negotiated_version = Version}}), - {State2, Actions} = send_handshake(VerifyRequest, State1), - {Record, State} = next_record(State2), - next_event(?FUNCTION_NAME, Record, + {State, Actions} = send_handshake(VerifyRequest, State1), + next_event(?FUNCTION_NAME, no_record, State#state{handshake_env = HsEnv#handshake_env{ tls_handshake_history = ssl_handshake:init_handshake_history()}}, @@ -537,14 +552,14 @@ hello(internal, #client_hello{extensions = Extensions} = Hello, start_or_recv_from = From} = State) -> {next_state, user_hello, State#state{start_or_recv_from = undefined, handshake_env = HsEnv#handshake_env{hello = Hello}}, - [{reply, From, {ok, ssl_connection:map_extensions(Extensions)}}]}; + [{reply, From, {ok, Extensions}}]}; hello(internal, #server_hello{extensions = Extensions} = Hello, #state{ssl_options = #ssl_options{handshake = hello}, handshake_env = HsEnv, start_or_recv_from = From} = State) -> {next_state, user_hello, State#state{start_or_recv_from = undefined, handshake_env = HsEnv#handshake_env{hello = Hello}}, - [{reply, From, {ok, ssl_connection:map_extensions(Extensions)}}]}; + [{reply, From, {ok, Extensions}}]}; hello(internal, #client_hello{cookie = Cookie} = Hello, #state{static_env = #static_env{role = server, transport_cb = Transport, @@ -701,12 +716,10 @@ connection(internal, #hello_request{}, #state{static_env = #static_env{host = Ho HelloVersion = dtls_record:hello_version(Version, SslOpts#ssl_options.versions), State1 = prepare_flight(State0), {State2, Actions} = send_handshake(Hello, State1#state{connection_env = CEnv#connection_env{negotiated_version = HelloVersion}}), - {Record, State} = - next_record( - State2#state{protocol_specific = PS#{flight_state => initial_flight_state(DataTag)}, - session = Session0#session{session_id - = Hello#client_hello.session_id}}), - next_event(hello, Record, State, Actions); + State = State2#state{protocol_specific = PS#{flight_state => initial_flight_state(DataTag)}, + session = Session0#session{session_id + = Hello#client_hello.session_id}}, + next_event(hello, no_record, State, Actions); connection(internal, #client_hello{} = Hello, #state{static_env = #static_env{role = server}, handshake_env = #handshake_env{allow_renegotiate = true} = HsEnv} = State) -> %% Mitigate Computational DoS attack @@ -762,7 +775,7 @@ format_status(Type, Data) -> %%% Internal functions %%-------------------------------------------------------------------- initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions, _}, User, - {CbModule, DataTag, CloseTag, ErrorTag}) -> + {CbModule, DataTag, CloseTag, ErrorTag, PassiveTag}) -> #ssl_options{beast_mitigation = BeastMitigation} = SSLOptions, ConnectionStates = dtls_record:init_connection_states(Role, BeastMitigation), @@ -772,7 +785,12 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions, _}, User, _ -> ssl_session_cache end, - + InternalActiveN = case application:get_env(ssl, internal_active_n) of + {ok, N} when is_integer(N) -> + N; + _ -> + ?INTERNAL_ACTIVE_N + end, Monitor = erlang:monitor(process, User), InitStatEnv = #static_env{ role = Role, @@ -781,6 +799,7 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions, _}, User, data_tag = DataTag, close_tag = CloseTag, error_tag = ErrorTag, + passive_tag = PassiveTag, host = Host, port = Port, socket = Socket, @@ -804,7 +823,9 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions, _}, User, user_data_buffer = {[],0,[]}, start_or_recv_from = undefined, flight_buffer = new_flight(), - protocol_specific = #{flight_state => initial_flight_state(DataTag)} + protocol_specific = #{active_n => InternalActiveN, + active_n_toggle => true, + flight_state => initial_flight_state(DataTag)} }. initial_flight_state(udp)-> @@ -814,10 +835,11 @@ initial_flight_state(_) -> next_dtls_record(Data, StateName, #state{protocol_buffers = #protocol_buffers{ dtls_record_buffer = Buf0, - dtls_cipher_texts = CT0} = Buffers} = State0) -> + dtls_cipher_texts = CT0} = Buffers, + ssl_options = SslOpts} = State0) -> case dtls_record:get_dtls_records(Data, acceptable_record_versions(StateName, State0), - Buf0) of + Buf0, SslOpts) of {Records, Buf1} -> CT1 = CT0 ++ Records, next_record(State0#state{protocol_buffers = @@ -828,7 +850,7 @@ next_dtls_record(Data, StateName, #state{protocol_buffers = #protocol_buffers{ end. acceptable_record_versions(hello, _) -> - [dtls_record:protocol_version(Vsn) || Vsn <- ?ALL_DATAGRAM_SUPPORTED_VERSIONS]; + [dtls_record:protocol_version(Vsn) || Vsn <- ?ALL_AVAILABLE_DATAGRAM_VERSIONS]; acceptable_record_versions(_, #state{connection_env = #connection_env{negotiated_version = Version}}) -> [Version]. @@ -900,12 +922,21 @@ handle_info({Protocol, _, _, _, Data}, StateName, ssl_connection:handle_normal_shutdown(Alert, StateName, State0), {stop, {shutdown, own_alert}, State0} end; + +handle_info({PassiveTag, Socket}, StateName, + #state{static_env = #static_env{socket = {_, Socket}, + passive_tag = PassiveTag}, + protocol_specific = PS} = State) -> + next_event(StateName, no_record, + State#state{protocol_specific = PS#{active_n_toggle => true}}); + handle_info({CloseTag, Socket}, StateName, #state{static_env = #static_env{socket = Socket, close_tag = CloseTag}, connection_env = #connection_env{negotiated_version = Version}, socket_options = #socket_options{active = Active}, - protocol_buffers = #protocol_buffers{dtls_cipher_texts = CTs}} = State) -> + protocol_buffers = #protocol_buffers{dtls_cipher_texts = CTs}, + protocol_specific = PS} = State) -> %% Note that as of DTLS 1.2 (TLS 1.1), %% failure to properly close a connection no longer requires that a %% session not be resumed. This is a change from DTLS 1.0 to conform @@ -928,7 +959,8 @@ handle_info({CloseTag, Socket}, StateName, %% Fixes non-delivery of final DTLS record in {active, once}. %% Basically allows the application the opportunity to set {active, once} again %% and then receive the final message. - next_event(StateName, no_record, State) + next_event(StateName, no_record, State#state{ + protocol_specific = PS#{active_n_toggle => true}}) end; handle_info(new_cookie_secret, StateName, @@ -963,7 +995,7 @@ handle_own_alert(Alert, Version, StateName, #state{static_env = #static_env{data ssl_options = Options} = State0) -> case ignore_alert(Alert, State0) of {true, State} -> - log_ignore_alert(Options#ssl_options.log_alert, StateName, Alert, Role), + log_ignore_alert(Options#ssl_options.log_level, StateName, Alert, Role), {next_state, StateName, State}; {false, State} -> ssl_connection:handle_own_alert(Alert, Version, StateName, State) @@ -1063,21 +1095,24 @@ start_retransmision_timer(Timeout, #state{protocol_specific = PS} = State) -> {State#state{protocol_specific = PS#{flight_state => {retransmit, new_timeout(Timeout)}}}, [{state_timeout, Timeout, flight_retransmission_timeout}]}. -new_timeout(N) when N =< 30 -> +new_timeout(N) when N =< 30000 -> N * 2; new_timeout(_) -> - 60. + 60000. send_handshake_flight(#state{static_env = #static_env{socket = Socket, transport_cb = Transport}, connection_env = #connection_env{negotiated_version = Version}, flight_buffer = #{handshakes := Flight, change_cipher_spec := undefined}, - connection_states = ConnectionStates0} = State0, Epoch) -> + connection_states = ConnectionStates0, + ssl_options = #ssl_options{log_level = LogLevel}} = State0, + Epoch) -> %% TODO remove hardcoded Max size {Encoded, ConnectionStates} = encode_handshake_flight(lists:reverse(Flight), Version, 1400, Epoch, ConnectionStates0), - send(Transport, Socket, Encoded), + send(Transport, Socket, Encoded), + ssl_logger:debug(LogLevel, outbound, 'record', Encoded), {State0#state{connection_states = ConnectionStates}, []}; send_handshake_flight(#state{static_env = #static_env{socket = Socket, @@ -1086,12 +1121,16 @@ send_handshake_flight(#state{static_env = #static_env{socket = Socket, flight_buffer = #{handshakes := [_|_] = Flight0, change_cipher_spec := ChangeCipher, handshakes_after_change_cipher_spec := []}, - connection_states = ConnectionStates0} = State0, Epoch) -> + connection_states = ConnectionStates0, + ssl_options = #ssl_options{log_level = LogLevel}} = State0, + Epoch) -> {HsBefore, ConnectionStates1} = encode_handshake_flight(lists:reverse(Flight0), Version, 1400, Epoch, ConnectionStates0), {EncChangeCipher, ConnectionStates} = encode_change_cipher(ChangeCipher, Version, Epoch, ConnectionStates1), send(Transport, Socket, [HsBefore, EncChangeCipher]), + ssl_logger:debug(LogLevel, outbound, 'record', [HsBefore]), + ssl_logger:debug(LogLevel, outbound, 'record', [EncChangeCipher]), {State0#state{connection_states = ConnectionStates}, []}; send_handshake_flight(#state{static_env = #static_env{socket = Socket, @@ -1100,7 +1139,9 @@ send_handshake_flight(#state{static_env = #static_env{socket = Socket, flight_buffer = #{handshakes := [_|_] = Flight0, change_cipher_spec := ChangeCipher, handshakes_after_change_cipher_spec := Flight1}, - connection_states = ConnectionStates0} = State0, Epoch) -> + connection_states = ConnectionStates0, + ssl_options = #ssl_options{log_level = LogLevel}} = State0, + Epoch) -> {HsBefore, ConnectionStates1} = encode_handshake_flight(lists:reverse(Flight0), Version, 1400, Epoch-1, ConnectionStates0), {EncChangeCipher, ConnectionStates2} = @@ -1108,6 +1149,9 @@ send_handshake_flight(#state{static_env = #static_env{socket = Socket, {HsAfter, ConnectionStates} = encode_handshake_flight(lists:reverse(Flight1), Version, 1400, Epoch, ConnectionStates2), send(Transport, Socket, [HsBefore, EncChangeCipher, HsAfter]), + ssl_logger:debug(LogLevel, outbound, 'record', [HsBefore]), + ssl_logger:debug(LogLevel, outbound, 'record', [EncChangeCipher]), + ssl_logger:debug(LogLevel, outbound, 'record', [HsAfter]), {State0#state{connection_states = ConnectionStates}, []}; send_handshake_flight(#state{static_env = #static_env{socket = Socket, @@ -1116,12 +1160,16 @@ send_handshake_flight(#state{static_env = #static_env{socket = Socket, flight_buffer = #{handshakes := [], change_cipher_spec := ChangeCipher, handshakes_after_change_cipher_spec := Flight1}, - connection_states = ConnectionStates0} = State0, Epoch) -> + connection_states = ConnectionStates0, + ssl_options = #ssl_options{log_level = LogLevel}} = State0, + Epoch) -> {EncChangeCipher, ConnectionStates1} = encode_change_cipher(ChangeCipher, Version, Epoch-1, ConnectionStates0), {HsAfter, ConnectionStates} = encode_handshake_flight(lists:reverse(Flight1), Version, 1400, Epoch, ConnectionStates1), send(Transport, Socket, [EncChangeCipher, HsAfter]), + ssl_logger:debug(LogLevel, outbound, 'record', [EncChangeCipher]), + ssl_logger:debug(LogLevel, outbound, 'record', [HsAfter]), {State0#state{connection_states = ConnectionStates}, []}. retransmit_epoch(_StateName, #state{connection_states = ConnectionStates}) -> @@ -1160,11 +1208,11 @@ is_ignore_alert(#alert{description = ?ILLEGAL_PARAMETER}) -> is_ignore_alert(_) -> false. -log_ignore_alert(true, StateName, Alert, Role) -> +log_ignore_alert(debug, StateName, Alert, Role) -> Txt = ssl_alert:alert_txt(Alert), - error_logger:format("DTLS over UDP ~p: In state ~p ignored to send ALERT ~s as DoS-attack mitigation \n", - [Role, StateName, Txt]); -log_ignore_alert(false, _, _,_) -> + ?LOG_ERROR("DTLS over UDP ~p: In state ~p ignored to send ALERT ~s as DoS-attack mitigation \n", + [Role, StateName, Txt]); +log_ignore_alert(_, _, _, _) -> ok. send_application_data(Data, From, _StateName, @@ -1173,7 +1221,8 @@ send_application_data(Data, From, _StateName, connection_env = #connection_env{negotiated_version = Version}, handshake_env = HsEnv, connection_states = ConnectionStates0, - ssl_options = #ssl_options{renegotiate_at = RenegotiateAt}} = State0) -> + ssl_options = #ssl_options{renegotiate_at = RenegotiateAt, + log_level = LogLevel}} = State0) -> case time_to_renegotiate(Data, ConnectionStates0, RenegotiateAt) of true -> @@ -1185,6 +1234,7 @@ send_application_data(Data, From, _StateName, State = State0#state{connection_states = ConnectionStates}, case send(Transport, Socket, Msgs) of ok -> + ssl_logger:debug(LogLevel, outbound, 'record', Msgs), ssl_connection:hibernate_after(connection, State, [{reply, From, ok}]); Result -> ssl_connection:hibernate_after(connection, State, [{reply, From, Result}]) diff --git a/lib/ssl/src/dtls_handshake.erl b/lib/ssl/src/dtls_handshake.erl index 6e9bf99e52..d8c0e30973 100644 --- a/lib/ssl/src/dtls_handshake.erl +++ b/lib/ssl/src/dtls_handshake.erl @@ -37,7 +37,7 @@ -export([fragment_handshake/2, encode_handshake/3]). %% Handshake decodeing --export([get_dtls_handshake/3]). +-export([get_dtls_handshake/4]). -type dtls_handshake() :: #client_hello{} | #hello_verify_request{} | ssl_handshake:ssl_handshake(). @@ -79,7 +79,7 @@ client_hello(Host, Port, Cookie, ConnectionStates, Extensions = ssl_handshake:client_hello_extensions(TLSVersion, CipherSuites, SslOpts, ConnectionStates, - Renegotiation), + Renegotiation, undefined), Id = ssl_session:client_id({Host, Port, SslOpts}, Cache, CacheCb, OwnCert), #client_hello{session_id = Id, @@ -151,15 +151,15 @@ encode_handshake(Handshake, Version, Seq) -> %%-------------------------------------------------------------------- %%-------------------------------------------------------------------- --spec get_dtls_handshake(ssl_record:ssl_version(), binary(), #protocol_buffers{}) -> +-spec get_dtls_handshake(ssl_record:ssl_version(), binary(), #protocol_buffers{}, #ssl_options{}) -> {[dtls_handshake()], #protocol_buffers{}}. %% %% Description: Given buffered and new data from dtls_record, collects %% and returns it as a list of handshake messages, also returns %% possible leftover data in the new "protocol_buffers". %%-------------------------------------------------------------------- -get_dtls_handshake(Version, Fragment, ProtocolBuffers) -> - handle_fragments(Version, Fragment, ProtocolBuffers, []). +get_dtls_handshake(Version, Fragment, ProtocolBuffers, Options) -> + handle_fragments(Version, Fragment, ProtocolBuffers, Options, []). %%-------------------------------------------------------------------- %%% Internal functions @@ -169,10 +169,7 @@ handle_client_hello(Version, cipher_suites = CipherSuites, compression_methods = Compressions, random = Random, - extensions = - #hello_extensions{elliptic_curves = Curves, - signature_algs = ClientHashSigns} - = HelloExt}, + extensions = HelloExt}, #ssl_options{versions = Versions, signature_algs = SupportedHashSigns, eccs = SupportedECCs, @@ -181,6 +178,8 @@ handle_client_hello(Version, Renegotiation) -> case dtls_record:is_acceptable_version(Version, Versions) of true -> + Curves = maps:get(elliptic_curves, HelloExt, undefined), + ClientHashSigns = maps:get(signature_algs, HelloExt, undefined), TLSVersion = dtls_v1:corresponding_tls_version(Version), AvailableHashSigns = ssl_handshake:available_signature_algs( ClientHashSigns, SupportedHashSigns, Cert,TLSVersion), @@ -194,8 +193,8 @@ handle_client_hello(Version, no_suite -> ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY); _ -> - #{key_exchange := KeyExAlg} = ssl_cipher_format:suite_definition(CipherSuite), - case ssl_handshake:select_hashsign(ClientHashSigns, Cert, KeyExAlg, + #{key_exchange := KeyExAlg} = ssl_cipher_format:suite_bin_to_map(CipherSuite), + case ssl_handshake:select_hashsign({ClientHashSigns, undefined}, Cert, KeyExAlg, SupportedHashSigns, TLSVersion) of #alert{} = Alert -> Alert; @@ -311,20 +310,21 @@ address_to_bin({A,B,C,D,E,F,G,H}, Port) -> %%-------------------------------------------------------------------- -handle_fragments(Version, FragmentData, Buffers0, Acc) -> +handle_fragments(Version, FragmentData, Buffers0, Options, Acc) -> Fragments = decode_handshake_fragments(FragmentData), - do_handle_fragments(Version, Fragments, Buffers0, Acc). + do_handle_fragments(Version, Fragments, Buffers0, Options, Acc). -do_handle_fragments(_, [], Buffers, Acc) -> +do_handle_fragments(_, [], Buffers, _Options, Acc) -> {lists:reverse(Acc), Buffers}; -do_handle_fragments(Version, [Fragment | Fragments], Buffers0, Acc) -> +do_handle_fragments(Version, [Fragment | Fragments], Buffers0, Options, Acc) -> case reassemble(Version, Fragment, Buffers0) of {more_data, Buffers} when Fragments == [] -> {lists:reverse(Acc), Buffers}; {more_data, Buffers} -> - do_handle_fragments(Version, Fragments, Buffers, Acc); - {HsPacket, Buffers} -> - do_handle_fragments(Version, Fragments, Buffers, [HsPacket | Acc]) + do_handle_fragments(Version, Fragments, Buffers, Options, Acc); + {{Handshake, _} = HsPacket, Buffers} -> + ssl_logger:debug(Options#ssl_options.log_level, inbound, 'handshake', Handshake), + do_handle_fragments(Version, Fragments, Buffers, Options, [HsPacket | Acc]) end. decode_handshake(Version, <<?BYTE(Type), Bin/binary>>) -> @@ -332,7 +332,7 @@ decode_handshake(Version, <<?BYTE(Type), Bin/binary>>) -> decode_handshake(_, ?HELLO_REQUEST, <<>>) -> #hello_request{}; -decode_handshake(_Version, ?CLIENT_HELLO, <<?UINT24(_), ?UINT16(_), +decode_handshake(Version, ?CLIENT_HELLO, <<?UINT24(_), ?UINT16(_), ?UINT24(_), ?UINT24(_), ?BYTE(Major), ?BYTE(Minor), Random:32/binary, ?BYTE(SID_length), Session_ID:SID_length/binary, @@ -340,8 +340,10 @@ decode_handshake(_Version, ?CLIENT_HELLO, <<?UINT24(_), ?UINT16(_), ?UINT16(Cs_length), CipherSuites:Cs_length/binary, ?BYTE(Cm_length), Comp_methods:Cm_length/binary, Extensions/binary>>) -> - - DecodedExtensions = ssl_handshake:decode_hello_extensions({client, Extensions}), + TLSVersion = dtls_v1:corresponding_tls_version(Version), + LegacyVersion = dtls_v1:corresponding_tls_version({Major, Minor}), + Exts = ssl_handshake:decode_vector(Extensions), + DecodedExtensions = ssl_handshake:decode_hello_extensions(Exts, TLSVersion, LegacyVersion, client), #client_hello{ client_version = {Major,Minor}, @@ -362,9 +364,9 @@ decode_handshake(_Version, ?HELLO_VERIFY_REQUEST, <<?UINT24(_), ?UINT16(_), decode_handshake(Version, Tag, <<?UINT24(_), ?UINT16(_), ?UINT24(_), ?UINT24(_), Msg/binary>>) -> %% DTLS specifics stripped - decode_tls_thandshake(Version, Tag, Msg). + decode_tls_handshake(Version, Tag, Msg). -decode_tls_thandshake(Version, Tag, Msg) -> +decode_tls_handshake(Version, Tag, Msg) -> TLSVersion = dtls_v1:corresponding_tls_version(Version), ssl_handshake:decode_handshake(TLSVersion, Tag, Msg). @@ -425,74 +427,135 @@ merge_fragment(Frag0, [Frag1 | Rest]) -> Frag -> merge_fragment(Frag, Rest) end. -%% Duplicate + + +%% Duplicate (fully contained fragment) +%% 2,5 _ _ P P P P P +%% 2,5 _ _ C C C C C merge_fragments(#handshake_fragment{ - fragment_offset = PreviousOffSet, + fragment_offset = PreviousOffSet, fragment_length = PreviousLen, fragment = PreviousData - } = Previous, + } = Previous, #handshake_fragment{ fragment_offset = PreviousOffSet, fragment_length = PreviousLen, fragment = PreviousData}) -> Previous; -%% Lager fragment save new data +%% Duplicate (fully contained fragment) +%% 2,5 _ _ P P P P P +%% 2,2 _ _ C C +%% 0,3 X X X +%% 5,3 _ _ _ _ _ X X X merge_fragments(#handshake_fragment{ - fragment_offset = PreviousOffSet, - fragment_length = PreviousLen, + fragment_offset = PreviousOffset, + fragment_length = PreviousLen + } = Previous, + #handshake_fragment{ + fragment_offset = CurrentOffset, + fragment_length = CurrentLen}) + when PreviousOffset =< CurrentOffset andalso + CurrentOffset =< PreviousOffset + PreviousLen andalso + CurrentOffset + CurrentLen =< PreviousOffset + PreviousLen -> + Previous; + +%% Fully overlapping fragments +%% 2,5 _ _ P P P P P +%% 0,8 C C C C C C C C +merge_fragments(#handshake_fragment{ + fragment_offset = PreviousOffset, + fragment_length = PreviousLen + }, + #handshake_fragment{ + fragment_offset = CurrentOffset, + fragment_length = CurrentLen} = Current) + when CurrentOffset =< PreviousOffset andalso + CurrentOffset + CurrentLen >= PreviousOffset + PreviousLen -> + Current; + +%% Overlapping fragments +%% 2,5 _ _ P P P P P +%% 0,3 C C C +merge_fragments(#handshake_fragment{ + fragment_offset = PreviousOffset, + fragment_length = PreviousLen, fragment = PreviousData - } = Previous, - #handshake_fragment{ - fragment_offset = PreviousOffSet, - fragment_length = CurrentLen, - fragment = CurrentData}) when CurrentLen > PreviousLen -> - NewLength = CurrentLen - PreviousLen, - <<_:PreviousLen/binary, NewData/binary>> = CurrentData, + } = Previous, + #handshake_fragment{ + fragment_offset = CurrentOffset, + fragment_length = CurrentLen, + fragment = CurrentData}) + when CurrentOffset < PreviousOffset andalso + CurrentOffset + CurrentLen < PreviousOffset + PreviousLen -> + NewDataLen = PreviousOffset - CurrentOffset, + <<NewData:NewDataLen/binary, _/binary>> = CurrentData, Previous#handshake_fragment{ - fragment_length = PreviousLen + NewLength, - fragment = <<PreviousData/binary, NewData/binary>> + fragment_length = PreviousLen + NewDataLen, + fragment = <<NewData/binary, PreviousData/binary>> }; -%% Smaller fragment +%% Overlapping fragments +%% 2,5 _ _ P P P P P +%% 5,3 _ _ _ _ _ C C C merge_fragments(#handshake_fragment{ - fragment_offset = PreviousOffSet, - fragment_length = PreviousLen - } = Previous, - #handshake_fragment{ - fragment_offset = PreviousOffSet, - fragment_length = CurrentLen}) when CurrentLen < PreviousLen -> - Previous; -%% Next fragment, might be overlapping + fragment_offset = PreviousOffset, + fragment_length = PreviousLen, + fragment = PreviousData + } = Previous, + #handshake_fragment{ + fragment_offset = CurrentOffset, + fragment_length = CurrentLen, + fragment = CurrentData}) + when CurrentOffset > PreviousOffset andalso + CurrentOffset < PreviousOffset + PreviousLen -> + NewDataLen = CurrentOffset + CurrentLen - (PreviousOffset + PreviousLen), + DropLen = CurrentLen - NewDataLen, + <<_:DropLen/binary, NewData/binary>> = CurrentData, + Previous#handshake_fragment{ + fragment_length = PreviousLen + NewDataLen, + fragment = <<PreviousData/binary, NewData/binary>> + }; + +%% Adjacent fragments +%% 2,5 _ _ P P P P P +%% 7,3 _ _ _ _ _ _ _ C C C merge_fragments(#handshake_fragment{ - fragment_offset = PreviousOffSet, - fragment_length = PreviousLen, + fragment_offset = PreviousOffset, + fragment_length = PreviousLen, fragment = PreviousData - } = Previous, - #handshake_fragment{ - fragment_offset = CurrentOffSet, - fragment_length = CurrentLen, - fragment = CurrentData}) - when PreviousOffSet + PreviousLen >= CurrentOffSet andalso - PreviousOffSet + PreviousLen < CurrentOffSet + CurrentLen -> - CurrentStart = PreviousOffSet + PreviousLen - CurrentOffSet, - <<_:CurrentStart/bytes, Data/binary>> = CurrentData, + } = Previous, + #handshake_fragment{ + fragment_offset = CurrentOffset, + fragment_length = CurrentLen, + fragment = CurrentData}) + when CurrentOffset =:= PreviousOffset + PreviousLen -> Previous#handshake_fragment{ - fragment_length = PreviousLen + CurrentLen - CurrentStart, - fragment = <<PreviousData/binary, Data/binary>>}; -%% already fully contained fragment + fragment_length = PreviousLen + CurrentLen, + fragment = <<PreviousData/binary, CurrentData/binary>> + }; + +%% Adjacent fragments +%% 2,5 _ _ P P P P P +%% 0,2 C C merge_fragments(#handshake_fragment{ - fragment_offset = PreviousOffSet, - fragment_length = PreviousLen - } = Previous, + fragment_offset = PreviousOffset, + fragment_length = PreviousLen, + fragment = PreviousData + } = Previous, #handshake_fragment{ - fragment_offset = CurrentOffSet, - fragment_length = CurrentLen}) - when PreviousOffSet + PreviousLen >= CurrentOffSet andalso - PreviousOffSet + PreviousLen >= CurrentOffSet + CurrentLen -> - Previous; + fragment_offset = CurrentOffset, + fragment_length = CurrentLen, + fragment = CurrentData}) + when PreviousOffset =:= CurrentOffset + CurrentLen -> + Previous#handshake_fragment{ + fragment_length = PreviousLen + CurrentLen, + fragment = <<CurrentData/binary, PreviousData/binary>> + }; %% No merge there is a gap +%% 3,5 _ _ _ P P P P +%% 0,2 C C merge_fragments(Previous, Current) -> [Previous, Current]. diff --git a/lib/ssl/src/dtls_handshake.hrl b/lib/ssl/src/dtls_handshake.hrl index 41da8e5c8c..de2be1daeb 100644 --- a/lib/ssl/src/dtls_handshake.hrl +++ b/lib/ssl/src/dtls_handshake.hrl @@ -26,23 +26,13 @@ -ifndef(dtls_handshake). -define(dtls_handshake, true). +-include("tls_handshake.hrl"). %% Common TLS and DTLS records and Constantes -include("ssl_handshake.hrl"). %% Common TLS and DTLS records and Constantes -include("ssl_api.hrl"). -define(HELLO_VERIFY_REQUEST, 3). -define(HELLO_VERIFY_REQUEST_VERSION, {254, 255}). --record(client_hello, { - client_version, - random, - session_id, % opaque SessionID<0..32> - cookie, % opaque<2..2^16-1> - cipher_suites, % cipher_suites<2..2^16-1> - compression_methods, % compression_methods<1..2^8-1>, - %% Extensions - extensions - }). - -record(hello_verify_request, { protocol_version, cookie @@ -57,4 +47,11 @@ fragment }). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% RFC 7764 Datagram Transport Layer Security (DTLS) Extension to Establish Keys +%% for the Secure Real-time Transport Protocol (SRTP) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Not supported +-define(USE_SRTP, 14). + -endif. % -ifdef(dtls_handshake). diff --git a/lib/ssl/src/dtls_packet_demux.erl b/lib/ssl/src/dtls_packet_demux.erl index 092366b7c0..c6431b55a9 100644 --- a/lib/ssl/src/dtls_packet_demux.erl +++ b/lib/ssl/src/dtls_packet_demux.erl @@ -24,6 +24,7 @@ -behaviour(gen_server). -include("ssl_internal.hrl"). +-include_lib("kernel/include/logger.hrl"). %% API -export([start_link/5, active_once/3, accept/2, sockname/1, close/1, @@ -34,7 +35,8 @@ terminate/2, code_change/3]). -record(state, - {port, + {active_n, + port, listener, transport, dtls_options, @@ -75,10 +77,18 @@ set_sock_opts(PacketSocket, Opts) -> %%% gen_server callbacks %%%=================================================================== -init([Port, {TransportModule, _,_,_} = TransportInfo, EmOpts, InetOptions, DTLSOptions]) -> +init([Port, {TransportModule, _,_,_,_} = TransportInfo, EmOpts, InetOptions, DTLSOptions]) -> try {ok, Socket} = TransportModule:open(Port, InetOptions), - {ok, #state{port = Port, + InternalActiveN = case application:get_env(ssl, internal_active_n) of + {ok, N} when is_integer(N) -> + N; + _ -> + ?INTERNAL_ACTIVE_N + end, + + {ok, #state{active_n = InternalActiveN, + port = Port, first = true, transport = TransportInfo, dtls_options = DTLSOptions, @@ -91,10 +101,11 @@ init([Port, {TransportModule, _,_,_} = TransportInfo, EmOpts, InetOptions, DTLSO handle_call({accept, _}, _, #state{close = true} = State) -> {reply, {error, closed}, State}; -handle_call({accept, Accepter}, From, #state{first = true, +handle_call({accept, Accepter}, From, #state{active_n = N, + first = true, accepters = Accepters, listener = Socket} = State0) -> - next_datagram(Socket), + next_datagram(Socket, N), State = State0#state{first = false, accepters = queue:in({Accepter, From}, Accepters)}, {noreply, State}; @@ -136,21 +147,26 @@ handle_cast({active_once, Client, Pid}, State0) -> State = handle_active_once(Client, Pid, State0), {noreply, State}. -handle_info({Transport, Socket, IP, InPortNo, _} = Msg, #state{listener = Socket, transport = {_,Transport,_,_}} = State0) -> +handle_info({Transport, Socket, IP, InPortNo, _} = Msg, #state{listener = Socket, transport = {_,Transport,_,_,_}} = State0) -> State = handle_datagram({IP, InPortNo}, Msg, State0), - next_datagram(Socket), {noreply, State}; +handle_info({PassiveTag, Socket}, + #state{active_n = N, + listener = Socket, + transport = {_,_,_, udp_error, PassiveTag}}) -> + next_datagram(Socket, N); + %% UDP socket does not have a connection and should not receive an econnreset %% This does however happens on some windows versions. Just ignoring it %% appears to make things work as expected! -handle_info({udp_error, Socket, econnreset = Error}, #state{listener = Socket, transport = {_,_,_, udp_error}} = State) -> +handle_info({udp_error, Socket, econnreset = Error}, #state{listener = Socket, transport = {_,_,_, udp_error,_}} = State) -> Report = io_lib:format("Ignore SSL UDP Listener: Socket error: ~p ~n", [Error]), - error_logger:info_report(Report), + ?LOG_NOTICE(Report), {noreply, State}; -handle_info({ErrorTag, Socket, Error}, #state{listener = Socket, transport = {_,_,_, ErrorTag}} = State) -> +handle_info({ErrorTag, Socket, Error}, #state{listener = Socket, transport = {_,_,_, ErrorTag,_}} = State) -> Report = io_lib:format("SSL Packet muliplxer shutdown: Socket error: ~p ~n", [Error]), - error_logger:info_report(Report), + ?LOG_NOTICE(Report), {noreply, State#state{close=true}}; handle_info({'DOWN', _, process, Pid, _}, #state{clients = Clients, @@ -202,16 +218,16 @@ dispatch(Client, Msg, #state{dtls_msq_queues = MsgQueues} = State) -> Pid ! Msg, State#state{dtls_msq_queues = kv_update(Client, Queue, MsgQueues)}; - {{value, _}, Queue} -> + {{value, _UDP}, _Queue} -> State#state{dtls_msq_queues = - kv_update(Client, queue:in(Msg, Queue), MsgQueues)}; + kv_update(Client, queue:in(Msg, Queue0), MsgQueues)}; {empty, Queue} -> State#state{dtls_msq_queues = kv_update(Client, queue:in(Msg, Queue), MsgQueues)} end end. -next_datagram(Socket) -> - inet:setopts(Socket, [{active, once}]). +next_datagram(Socket, N) -> + inet:setopts(Socket, [{active, N}]). handle_active_once(Client, Pid, #state{dtls_msq_queues = MsgQueues} = State0) -> Queue0 = kv_get(Client, MsgQueues), diff --git a/lib/ssl/src/dtls_record.erl b/lib/ssl/src/dtls_record.erl index 2fe875da31..a4846f42c5 100644 --- a/lib/ssl/src/dtls_record.erl +++ b/lib/ssl/src/dtls_record.erl @@ -30,7 +30,7 @@ -include("ssl_cipher.hrl"). %% Handling of incoming data --export([get_dtls_records/3, init_connection_states/2, empty_connection_state/1]). +-export([get_dtls_records/4, init_connection_states/2, empty_connection_state/1]). -export([save_current_connection_state/2, next_epoch/2, get_connection_state_by_epoch/3, replay_detect/2, init_connection_state_seq/2, current_connection_state_epoch/2]). @@ -162,24 +162,25 @@ current_connection_state_epoch(#{current_write := #{epoch := Epoch}}, Epoch. %%-------------------------------------------------------------------- --spec get_dtls_records(binary(), [ssl_record:ssl_version()], binary()) -> {[binary()], binary()} | #alert{}. +-spec get_dtls_records(binary(), [ssl_record:ssl_version()], binary(), + #ssl_options{}) -> {[binary()], binary()} | #alert{}. %% %% Description: Given old buffer and new data from UDP/SCTP, packs up a records %% and returns it as a list of tls_compressed binaries also returns leftover %% data %%-------------------------------------------------------------------- -get_dtls_records(Data, Versions, Buffer) -> +get_dtls_records(Data, Versions, Buffer, SslOpts) -> BinData = list_to_binary([Buffer, Data]), case erlang:byte_size(BinData) of N when N >= 3 -> case assert_version(BinData, Versions) of true -> - get_dtls_records_aux(BinData, []); + get_dtls_records_aux(BinData, [], SslOpts); false -> ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC) end; _ -> - get_dtls_records_aux(BinData, []) + get_dtls_records_aux(BinData, [], SslOpts) end. %%==================================================================== @@ -409,42 +410,47 @@ assert_version(<<?BYTE(_), ?BYTE(MajVer), ?BYTE(MinVer), _/binary>>, Versions) - get_dtls_records_aux(<<?BYTE(?APPLICATION_DATA),?BYTE(MajVer),?BYTE(MinVer), ?UINT16(Epoch), ?UINT48(SequenceNumber), - ?UINT16(Length), Data:Length/binary, Rest/binary>>, - Acc) -> + ?UINT16(Length), Data:Length/binary, Rest/binary>> = RawDTLSRecord, + Acc, SslOpts) -> + ssl_logger:debug(SslOpts#ssl_options.log_level, inbound, 'record', [RawDTLSRecord]), get_dtls_records_aux(Rest, [#ssl_tls{type = ?APPLICATION_DATA, version = {MajVer, MinVer}, epoch = Epoch, sequence_number = SequenceNumber, - fragment = Data} | Acc]); + fragment = Data} | Acc], SslOpts); get_dtls_records_aux(<<?BYTE(?HANDSHAKE),?BYTE(MajVer),?BYTE(MinVer), ?UINT16(Epoch), ?UINT48(SequenceNumber), ?UINT16(Length), - Data:Length/binary, Rest/binary>>, Acc) when MajVer >= 128 -> + Data:Length/binary, Rest/binary>> = RawDTLSRecord, + Acc, SslOpts) when MajVer >= 128 -> + ssl_logger:debug(SslOpts#ssl_options.log_level, inbound, 'record', [RawDTLSRecord]), get_dtls_records_aux(Rest, [#ssl_tls{type = ?HANDSHAKE, version = {MajVer, MinVer}, epoch = Epoch, sequence_number = SequenceNumber, - fragment = Data} | Acc]); + fragment = Data} | Acc], SslOpts); get_dtls_records_aux(<<?BYTE(?ALERT),?BYTE(MajVer),?BYTE(MinVer), ?UINT16(Epoch), ?UINT48(SequenceNumber), ?UINT16(Length), Data:Length/binary, - Rest/binary>>, Acc) -> + Rest/binary>> = RawDTLSRecord, Acc, SslOpts) -> + ssl_logger:debug(SslOpts#ssl_options.log_level, inbound, 'record', [RawDTLSRecord]), get_dtls_records_aux(Rest, [#ssl_tls{type = ?ALERT, version = {MajVer, MinVer}, epoch = Epoch, sequence_number = SequenceNumber, - fragment = Data} | Acc]); + fragment = Data} | Acc], SslOpts); get_dtls_records_aux(<<?BYTE(?CHANGE_CIPHER_SPEC),?BYTE(MajVer),?BYTE(MinVer), ?UINT16(Epoch), ?UINT48(SequenceNumber), - ?UINT16(Length), Data:Length/binary, Rest/binary>>, - Acc) -> + ?UINT16(Length), Data:Length/binary, Rest/binary>> = RawDTLSRecord, + Acc, SslOpts) -> + ssl_logger:debug(SslOpts#ssl_options.log_level, inbound, 'record', [RawDTLSRecord]), get_dtls_records_aux(Rest, [#ssl_tls{type = ?CHANGE_CIPHER_SPEC, version = {MajVer, MinVer}, epoch = Epoch, sequence_number = SequenceNumber, - fragment = Data} | Acc]); + fragment = Data} | Acc], SslOpts); get_dtls_records_aux(<<?BYTE(_), ?BYTE(_MajVer), ?BYTE(_MinVer), ?UINT16(Length), _/binary>>, - _Acc) when Length > ?MAX_CIPHER_TEXT_LENGTH -> + _Acc, _) when Length > ?MAX_CIPHER_TEXT_LENGTH -> ?ALERT_REC(?FATAL, ?RECORD_OVERFLOW); -get_dtls_records_aux(Data, Acc) -> +get_dtls_records_aux(Data, Acc, _) -> case size(Data) =< ?MAX_CIPHER_TEXT_LENGTH + ?INITIAL_BYTES of true -> {lists:reverse(Acc), Data}; diff --git a/lib/ssl/src/dtls_socket.erl b/lib/ssl/src/dtls_socket.erl index 4d07372e31..b305d08f70 100644 --- a/lib/ssl/src/dtls_socket.erl +++ b/lib/ssl/src/dtls_socket.erl @@ -45,7 +45,7 @@ listen(Port, #config{transport_info = TransportInfo, Err end. -accept(dtls, #config{transport_info = {Transport,_,_,_}, +accept(dtls, #config{transport_info = {Transport,_,_,_,_}, connection_cb = ConnectionCb, dtls_handler = {Listner, _}}, _Timeout) -> case dtls_packet_demux:accept(Listner, self()) of @@ -55,7 +55,7 @@ accept(dtls, #config{transport_info = {Transport,_,_,_}, {error, Reason} end. -connect(Address, Port, #config{transport_info = {Transport, _, _, _} = CbInfo, +connect(Address, Port, #config{transport_info = {Transport, _, _, _, _} = CbInfo, connection_cb = ConnectionCb, ssl = SslOpts, emulated = EmOpts, @@ -174,7 +174,7 @@ default_inet_values() -> [{active, true}, {mode, list}, {packet, 0}, {packet_size, 0}]. default_cb_info() -> - {gen_udp, udp, udp_closed, udp_error}. + {gen_udp, udp, udp_closed, udp_error, udp_passive}. get_emulated_opts(EmOpts, EmOptNames) -> lists:map(fun(Name) -> {value, Value} = lists:keysearch(Name, 1, EmOpts), diff --git a/lib/ssl/src/dtls_v1.erl b/lib/ssl/src/dtls_v1.erl index b365961a6a..fc9dce02ce 100644 --- a/lib/ssl/src/dtls_v1.erl +++ b/lib/ssl/src/dtls_v1.erl @@ -31,18 +31,18 @@ suites(Minor) -> lists:filter(fun(Cipher) -> - is_acceptable_cipher(ssl_cipher_format:suite_definition(Cipher)) + is_acceptable_cipher(ssl_cipher_format:suite_bin_to_map(Cipher)) end, tls_v1:suites(corresponding_minor_tls_version(Minor))). all_suites(Version) -> lists:filter(fun(Cipher) -> - is_acceptable_cipher(ssl_cipher_format:suite_definition(Cipher)) + is_acceptable_cipher(ssl_cipher_format:suite_bin_to_map(Cipher)) end, ssl_cipher:all_suites(corresponding_tls_version(Version))). anonymous_suites(Version) -> lists:filter(fun(Cipher) -> - is_acceptable_cipher(ssl_cipher_format:suite_definition(Cipher)) + is_acceptable_cipher(ssl_cipher_format:suite_bin_to_map(Cipher)) end, ssl_cipher:anonymous_suites(corresponding_tls_version(Version))). diff --git a/lib/ssl/src/inet_tls_dist.erl b/lib/ssl/src/inet_tls_dist.erl index 5cab35fd4b..8d9b92361b 100644 --- a/lib/ssl/src/inet_tls_dist.erl +++ b/lib/ssl/src/inet_tls_dist.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011-2018. All Rights Reserved. +%% Copyright Ericsson AB 2011-2019. 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. @@ -41,6 +41,7 @@ -include_lib("public_key/include/public_key.hrl"). -include("ssl_api.hrl"). +-include_lib("kernel/include/logger.hrl"). %% ------------------------------------------------------------------------- @@ -131,8 +132,8 @@ f_recv(SslSocket, Length, Timeout) -> f_setopts_pre_nodeup(_SslSocket) -> ok. -f_setopts_post_nodeup(_SslSocket) -> - ok. +f_setopts_post_nodeup(SslSocket) -> + ssl:setopts(SslSocket, [nodelay()]). f_getll(DistCtrl) -> {ok, DistCtrl}. @@ -198,7 +199,7 @@ listen(Name) -> gen_listen(Driver, Name) -> case inet_tcp_dist:gen_listen(Driver, Name) of {ok, {Socket, Address, Creation}} -> - inet:setopts(Socket, [{packet, 4}]), + inet:setopts(Socket, [{packet, 4}, {nodelay, true}]), {ok, {Socket, Address#net_address{protocol=tls}, Creation}}; Other -> Other @@ -225,7 +226,7 @@ accept_loop(Driver, Listen, Kernel) -> true -> accept_loop(Driver, Listen, Kernel, Socket); {false,IP} -> - error_logger:error_msg( + ?LOG_ERROR( "** Connection attempt from " "disallowed IP ~w ** ~n", [IP]), ?shutdown2(no_node, trace({disallowed, IP})) @@ -260,7 +261,7 @@ accept_loop(Driver, Listen, Kernel, Socket) -> {error, {options, _}} = Error -> %% Bad options: that's probably our fault. %% Let's log that. - error_logger:error_msg( + ?LOG_ERROR( "Cannot accept TLS distribution connection: ~s~n", [ssl:format_error(Error)]), gen_tcp:close(Socket), @@ -436,7 +437,7 @@ allowed_nodes(SslSocket, Allowed) -> PeerCert, allowed_hosts(Allowed), PeerIP) of [] -> - error_logger:error_msg( + ?LOG_ERROR( "** Connection attempt from " "disallowed node(s) ~p ** ~n", [PeerIP]), ?shutdown2( @@ -531,7 +532,7 @@ do_setup_connect(Driver, Kernel, Node, Address, Ip, TcpPort, Version, Type, MyNo case ssl:connect( Address, TcpPort, [binary, {active, false}, {packet, 4}, - Driver:family(), nodelay()] ++ Opts, + Driver:family(), {nodelay, true}] ++ Opts, net_kernel:connecttime()) of {ok, #sslsocket{pid = [_, DistCtrl| _]} = SslSocket} -> _ = monitor_pid(DistCtrl), @@ -695,12 +696,12 @@ split_node(Driver, Node, LongOrShortNames) -> {node, Name, Host} -> check_node(Driver, Node, Name, Host, LongOrShortNames); {host, _} -> - error_logger:error_msg( + ?LOG_ERROR( "** Nodename ~p illegal, no '@' character **~n", [Node]), ?shutdown2(Node, trace({illegal_node_n@me, Node})); _ -> - error_logger:error_msg( + ?LOG_ERROR( "** Nodename ~p illegal **~n", [Node]), ?shutdown2(Node, trace({illegal_node_name, Node})) end. @@ -712,7 +713,7 @@ check_node(Driver, Node, Name, Host, LongOrShortNames) -> {ok, _} -> {Name, Host}; _ -> - error_logger:error_msg( + ?LOG_ERROR( "** System running to use " "fully qualified hostnames **~n" "** Hostname ~s is illegal **~n", @@ -720,7 +721,7 @@ check_node(Driver, Node, Name, Host, LongOrShortNames) -> ?shutdown2(Node, trace({not_longnames, Host})) end; [_,_|_] when LongOrShortNames =:= shortnames -> - error_logger:error_msg( + ?LOG_ERROR( "** System NOT running to use " "fully qualified hostnames **~n" "** Hostname ~s is illegal **~n", @@ -850,13 +851,13 @@ monitor_pid(Pid) -> %% MRef = erlang:monitor(process, Pid), %% receive %% {'DOWN', MRef, _, _, normal} -> - %% error_logger:error_report( - %% [dist_proc_died, + %% ?LOG_ERROR( + %% [{slogan, dist_proc_died}, %% {reason, normal}, %% {pid, Pid}]); %% {'DOWN', MRef, _, _, Reason} -> - %% error_logger:info_report( - %% [dist_proc_died, + %% ?LOG_NOTICE( + %% [{slogan, dist_proc_died}, %% {reason, Reason}, %% {pid, Pid}]) %% end diff --git a/lib/ssl/src/ssl.app.src b/lib/ssl/src/ssl.app.src index 936df12e70..e7a4d73ec4 100644 --- a/lib/ssl/src/ssl.app.src +++ b/lib/ssl/src/ssl.app.src @@ -4,13 +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, @@ -51,6 +55,8 @@ ssl_crl_cache, ssl_crl_cache_api, ssl_crl_hash_dir, + %% Logging + ssl_logger, %% App structure ssl_app, ssl_sup, diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 7f8f1ec71c..e3bb4df1ac 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, groups/1, format_error/1, renegotiate/1, prf/5, negotiated_protocol/1, connection_information/1, connection_information/2]). %% Misc @@ -74,6 +74,7 @@ ciphers/0, cipher/0, hash/0, + key/0, kex_algo/0, prf_random/0, cipher_filters/0, @@ -85,40 +86,46 @@ srp_param_type/0]). %% ------------------------------------------------------------------------------------------------------- --type socket() :: gen_tcp:socket(). --type socket_option() :: gen_tcp:connect_option() | gen_tcp:listen_option() | gen_udp:option(). --type sslsocket() :: any(). --type tls_option() :: tls_client_option() | tls_server_option(). --type tls_client_option() :: client_option() | common_option() | socket_option() | transport_option(). --type tls_server_option() :: server_option() | common_option() | socket_option() | transport_option(). + +-type socket() :: gen_tcp:socket(). % exported +-type socket_option() :: gen_tcp:connect_option() | gen_tcp:listen_option() | gen_udp:option(). % exported +-type sslsocket() :: any(). % exported +-type tls_option() :: tls_client_option() | tls_server_option(). % exported +-type tls_client_option() :: client_option() | common_option() | socket_option() | transport_option(). % exported +-type tls_server_option() :: server_option() | common_option() | socket_option() | transport_option(). % exported -type active_msgs() :: {ssl, sslsocket(), Data::binary() | list()} | {ssl_closed, sslsocket()} | - {ssl_error, sslsocket(), Reason::term()} | {ssl_passive, sslsocket()}. + {ssl_error, sslsocket(), Reason::any()} | {ssl_passive, sslsocket()}. % exported -type transport_option() :: {cb_info, {CallbackModule::atom(), DataTag::atom(), ClosedTag::atom(), ErrTag::atom()}} | {cb_info, {CallbackModule::atom(), DataTag::atom(), ClosedTag::atom(), ErrTag::atom(), PassiveTag::atom()}}. --type host() :: hostname() | ip_address(). +-type host() :: hostname() | ip_address(). % exported -type hostname() :: string(). -type ip_address() :: inet:ip_address(). --type session_id() :: binary(). --type protocol_version() :: tls_version() | dtls_version(). --type tls_version() :: tlsv1 | 'tlsv1.1' | 'tlsv1.2' | 'tlsv1.3' | legacy_version(). --type dtls_version() :: 'dtlsv1' | 'dtlsv1.2'. --type legacy_version() :: sslv3. +-type session_id() :: binary(). % exported +-type protocol_version() :: tls_version() | dtls_version(). % exported +-type tls_version() :: 'tlsv1.2' | 'tlsv1.3' | tls_legacy_version(). +-type dtls_version() :: 'dtlsv1.2' | dtls_legacy_version(). +-type tls_legacy_version() :: tlsv1 | 'tlsv1.1' | sslv3. +-type dtls_legacy_version() :: 'dtlsv1'. -type verify_type() :: verify_none | verify_peer. -type cipher() :: aes_128_cbc | aes_256_cbc | aes_128_gcm | aes_256_gcm | + aes_128_ccm | + aes_256_ccm | + aes_128_ccm_8 | + aes_256_ccm_8 | chacha20_poly1305 | - legacy_cipher(). + legacy_cipher(). % exported -type legacy_cipher() :: rc4_128 | des_cbc | '3des_ede_cbc'. -type hash() :: sha | sha2() | - legacy_hash(). + legacy_hash(). % exported -type sha2() :: sha224 | sha256 | @@ -127,14 +134,29 @@ -type legacy_hash() :: md5. --type sign_algo() :: rsa | dsa | ecdsa. +-type sign_algo() :: rsa | dsa | ecdsa. % exported + +-type sign_scheme() :: rsa_pkcs1_sha256 + | rsa_pkcs1_sha384 + | rsa_pkcs1_sha512 + | ecdsa_secp256r1_sha256 + | ecdsa_secp384r1_sha384 + | ecdsa_secp521r1_sha512 + | rsa_pss_rsae_sha256 + | rsa_pss_rsae_sha384 + | rsa_pss_rsae_sha512 + | rsa_pss_pss_sha256 + | rsa_pss_pss_sha384 + | rsa_pss_pss_sha512 + | rsa_pkcs1_sha1 + | ecdsa_sha1. -type kex_algo() :: rsa | dhe_rsa | dhe_dss | ecdhe_ecdsa | ecdh_ecdsa | ecdh_rsa | srp_rsa| srp_dss | psk | dhe_psk | rsa_psk | dh_anon | ecdh_anon | srp_anon | - any. %% TLS 1.3 + any. %% TLS 1.3 , exported -type erl_cipher_suite() :: #{key_exchange := kex_algo(), cipher := cipher(), mac := hash() | aead, @@ -174,15 +196,18 @@ secp160r1 | secp160r2. +-type group() :: secp256r1 | secp384r1 | secp521r1 | ffdhe2048 | + ffdhe3072 | ffdhe4096 | ffdhe6144 | ffdhe8192. + -type srp_param_type() :: srp_1024 | srp_1536 | srp_2048 | srp_3072 | srp_4096 | srp_6144 | - srp_8192. + srp_8192. % exported --type error_alert() :: {tls_alert, {tls_alert(), Description::string()}}. +-type error_alert() :: {tls_alert, {tls_alert(), Description::string()}}. % exported -type tls_alert() :: close_notify | unexpected_message | @@ -213,6 +238,7 @@ bad_certificate_hash_value | unknown_psk_identity | no_application_protocol. + %% ------------------------------------------------------------------------------------------------------- -type common_option() :: {protocol, protocol()} | {handshake, handshake_completion()} | @@ -222,7 +248,8 @@ {keyfile, key_pem()} | {password, key_password()} | {ciphers, cipher_suites()} | - {eccs, eccs()} | + {eccs, [named_curve()]} | + {signature_algs_cert, signature_schemes()} | {secure_renegotiate, secure_renegotiation()} | {depth, allowed_cert_chain_length()} | {verify_fun, custom_verify()} | @@ -232,6 +259,7 @@ {partial_chain, root_fun()} | {versions, protocol_versions()} | {user_lookup_fun, custom_user_lookup()} | + {log_level, logging_level()} | {log_alert, log_alert()} | {hibernate_after, hibernate_after()} | {padding_check, padding_check()} | @@ -247,32 +275,34 @@ #{algorithm := rsa | dss | ecdsa, engine := crypto:engine_ref(), key_id := crypto:key_id(), - password => crypto:password()}. + password => crypto:password()}. % exported -type key_pem() :: file:filename(). -type key_password() :: string(). -type cipher_suites() :: ciphers(). -type ciphers() :: [erl_cipher_suite()] | - string(). % (according to old API) + string(). % (according to old API) exported -type cipher_filters() :: list({key_exchange | cipher | mac | prf, - algo_filter()}). + algo_filter()}). % exported -type algo_filter() :: fun((kex_algo()|cipher()|hash()|aead|default_prf) -> true | false). --type eccs() :: [named_curve()]. -type secure_renegotiation() :: boolean(). -type allowed_cert_chain_length() :: integer(). --type custom_verify() :: {Verifyfun :: fun(), InitialUserState :: term()}. --type crl_check() :: boolean() | peer | best_effort. --type crl_cache_opts() :: [term()]. --type handshake_size() :: integer(). --type hibernate_after() :: timeout(). --type root_fun() :: fun(). --type protocol_versions() :: [protocol_version()]. --type signature_algs() :: [{hash(), sign_algo()}]. --type custom_user_lookup() :: {Lookupfun :: fun(), UserState :: term()}. --type padding_check() :: boolean(). --type beast_mitigation() :: one_n_minus_one | zero_n | disabled. --type srp_identity() :: {Username :: string(), Password :: string()}. --type psk_identity() :: string(). --type log_alert() :: boolean(). + +-type custom_verify() :: {Verifyfun :: fun(), InitialUserState :: any()}. +-type crl_check() :: boolean() | peer | best_effort. +-type crl_cache_opts() :: [any()]. +-type handshake_size() :: integer(). +-type hibernate_after() :: timeout(). +-type root_fun() :: fun(). +-type protocol_versions() :: [protocol_version()]. +-type signature_algs() :: [{hash(), sign_algo()}]. +-type signature_schemes() :: [sign_scheme()]. +-type custom_user_lookup() :: {Lookupfun :: fun(), UserState :: any()}. +-type padding_check() :: boolean(). +-type beast_mitigation() :: one_n_minus_one | zero_n | disabled. +-type srp_identity() :: {Username :: string(), Password :: string()}. +-type psk_identity() :: string(). +-type log_alert() :: boolean(). +-type logging_level() :: logger:level(). %% ------------------------------------------------------------------------------------------------------- @@ -349,7 +379,7 @@ -type honor_ecc_order() :: boolean(). -type client_renegotiation() :: boolean(). %% ------------------------------------------------------------------------------------------------------- --type prf_random() :: client_random | server_random. +-type prf_random() :: client_random | server_random. % exported -type protocol_extensions() :: #{renegotiation_info => binary(), signature_algs => signature_algs(), alpn => app_level_protocol(), @@ -357,7 +387,7 @@ next_protocol => app_level_protocol(), ec_point_formats => [0..2], elliptic_curves => [public_key:oid()], - sni => hostname()}. + sni => hostname()}. % exported %% ------------------------------------------------------------------------------------------------------- %%%-------------------------------------------------------------------- @@ -393,14 +423,31 @@ stop() -> %% %% Description: Connect to an ssl server. %%-------------------------------------------------------------------- --spec connect(host() | port(), [client_option()]) -> {ok, #sslsocket{}} | - {error, reason()}. + +-spec connect(TCPSocket, TLSOptions) -> + {ok, sslsocket()} | + {error, reason()} | + {option_not_a_key_value_tuple, any()} when + TCPSocket :: socket(), + TLSOptions :: [tls_client_option()]. + connect(Socket, SslOptions) when is_port(Socket) -> connect(Socket, SslOptions, infinity). --spec connect(host() | port(), [client_option()] | inet:port_number(), - timeout() | list()) -> - {ok, #sslsocket{}} | {error, reason()}. +-spec connect(TCPSocket, TLSOptions, Timeout) -> + {ok, sslsocket()} | {error, reason()} when + TCPSocket :: socket(), + TLSOptions :: [tls_client_option()], + Timeout :: timeout(); + (Host, Port, TLSOptions) -> + {ok, sslsocket()} | + {ok, sslsocket(),Ext :: protocol_extensions()} | + {error, reason()} | + {option_not_a_key_value_tuple, any()} when + Host :: host(), + Port :: inet:port_number(), + TLSOptions :: [tls_client_option()]. + connect(Socket, SslOptions0, Timeout) when is_port(Socket), (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) -> CbInfo = handle_option(cb_info, SslOptions0, default_cb_info(tls)), @@ -417,8 +464,17 @@ connect(Socket, SslOptions0, Timeout) when is_port(Socket), connect(Host, Port, Options) -> connect(Host, Port, Options, infinity). --spec connect(host() | port(), inet:port_number(), [client_option()], timeout()) -> - {ok, #sslsocket{}} | {error, reason()}. + +-spec connect(Host, Port, TLSOptions, Timeout) -> + {ok, sslsocket()} | + {ok, sslsocket(),Ext :: protocol_extensions()} | + {error, reason()} | + {option_not_a_key_value_tuple, any()} when + Host :: host(), + Port :: inet:port_number(), + TLSOptions :: [tls_client_option()], + Timeout :: timeout(). + connect(Host, Port, Options, Timeout) when (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) -> try {ok, Config} = handle_options(Options, client, Host), @@ -434,7 +490,10 @@ connect(Host, Port, Options, Timeout) when (is_integer(Timeout) andalso Timeout end. %%-------------------------------------------------------------------- --spec listen(inet:port_number(), [tls_server_option()]) ->{ok, #sslsocket{}} | {error, reason()}. +-spec listen(Port, Options) -> {ok, ListenSocket} | {error, reason()} when + Port::inet:port_number(), + Options::[tls_server_option()], + ListenSocket :: sslsocket(). %% %% Description: Creates an ssl listen socket. @@ -453,13 +512,20 @@ listen(Port, Options0) -> %% %% Description: Performs transport accept on an ssl listen socket %%-------------------------------------------------------------------- --spec transport_accept(#sslsocket{}) -> {ok, #sslsocket{}} | - {error, reason()}. +-spec transport_accept(ListenSocket) -> {ok, SslSocket} | + {error, reason()} when + ListenSocket :: sslsocket(), + SslSocket :: sslsocket(). + transport_accept(ListenSocket) -> transport_accept(ListenSocket, infinity). --spec transport_accept(#sslsocket{}, timeout()) -> {ok, #sslsocket{}} | - {error, reason()}. +-spec transport_accept(ListenSocket, Timeout) -> {ok, SslSocket} | + {error, reason()} when + ListenSocket :: sslsocket(), + Timeout :: timeout(), + SslSocket :: sslsocket(). + transport_accept(#sslsocket{pid = {ListenSocket, #config{connection_cb = ConnectionCb} = Config}}, Timeout) when (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) -> @@ -475,12 +541,22 @@ transport_accept(#sslsocket{pid = {ListenSocket, %% Description: Performs accept on an ssl listen socket. e.i. performs %% ssl handshake. %%-------------------------------------------------------------------- --spec ssl_accept(#sslsocket{}) -> ok | {error, timeout | closed | {options, any()}| error_alert()}. +-spec ssl_accept(SslSocket) -> + ok | + {error, Reason} when + SslSocket :: sslsocket(), + Reason :: closed | timeout | error_alert(). + ssl_accept(ListenSocket) -> ssl_accept(ListenSocket, [], infinity). --spec ssl_accept(#sslsocket{} | port(), timeout()| [tls_server_option()]) -> - ok | {ok, #sslsocket{}} | {error, timeout | closed | {options, any()}| error_alert()}. +-spec ssl_accept(Socket, TimeoutOrOptions) -> + ok | + {ok, sslsocket()} | {error, Reason} when + Socket :: sslsocket() | socket(), + TimeoutOrOptions :: timeout() | [tls_server_option()], + Reason :: timeout | closed | {options, any()} | error_alert(). + ssl_accept(Socket, Timeout) when (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) -> ssl_accept(Socket, [], Timeout); ssl_accept(ListenSocket, SslOptions) when is_port(ListenSocket) -> @@ -488,8 +564,13 @@ ssl_accept(ListenSocket, SslOptions) when is_port(ListenSocket) -> ssl_accept(Socket, Timeout) -> ssl_accept(Socket, [], Timeout). --spec ssl_accept(#sslsocket{} | port(), [tls_server_option()], timeout()) -> - ok | {ok, #sslsocket{}} | {error, timeout | closed | {options, any()}| error_alert()}. +-spec ssl_accept(Socket, Options, Timeout) -> + ok | {ok, sslsocket()} | {error, Reason} when + Socket :: sslsocket() | socket(), + Options :: [tls_server_option()], + Timeout :: timeout(), + Reason :: timeout | closed | {options, any()} | error_alert(). + ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket) -> handshake(Socket, SslOptions, Timeout); ssl_accept(Socket, SslOptions, Timeout) -> @@ -506,21 +587,52 @@ ssl_accept(Socket, SslOptions, Timeout) -> %%-------------------------------------------------------------------- %% Performs the SSL/TLS/DTLS server-side handshake. --spec handshake(#sslsocket{}) -> {ok, #sslsocket{}} | {error, timeout | closed | {options, any()} | error_alert()}. +-spec handshake(HsSocket) -> {ok, SslSocket} | {ok, SslSocket, Ext} | {error, Reason} when + HsSocket :: sslsocket(), + SslSocket :: sslsocket(), + Ext :: protocol_extensions(), + Reason :: closed | timeout | error_alert(). + handshake(ListenSocket) -> handshake(ListenSocket, infinity). --spec handshake(#sslsocket{} | port(), timeout()| [tls_server_option()]) -> - {ok, #sslsocket{}} | {error, timeout | closed | {options, any()} | error_alert()}. +-spec handshake(HsSocket, Timeout) -> {ok, SslSocket} | {ok, SslSocket, Ext} | {error, Reason} when + HsSocket :: sslsocket(), + Timeout :: timeout(), + SslSocket :: sslsocket(), + Ext :: protocol_extensions(), + Reason :: closed | timeout | error_alert(); + (Socket, Options) -> {ok, SslSocket} | {ok, SslSocket, Ext} | {error, Reason} when + Socket :: socket() | sslsocket(), + SslSocket :: sslsocket(), + Options :: [server_option()], + Ext :: protocol_extensions(), + Reason :: closed | timeout | error_alert(). + handshake(#sslsocket{} = Socket, Timeout) when (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) -> ssl_connection:handshake(Socket, Timeout); +%% If Socket is a ordinary socket(): upgrades a gen_tcp, or equivalent, socket to +%% an SSL socket, that is, performs the SSL/TLS server-side handshake and returns +%% the SSL socket. +%% +%% If Socket is an sslsocket(): provides extra SSL/TLS/DTLS options to those +%% specified in ssl:listen/2 and then performs the SSL/TLS/DTLS handshake. handshake(ListenSocket, SslOptions) when is_port(ListenSocket) -> handshake(ListenSocket, SslOptions, infinity). --spec handshake(#sslsocket{} | port(), [tls_server_option()], timeout()) -> - {ok, #sslsocket{}} | {error, timeout | closed | {options, any()} | error_alert()}. +-spec handshake(Socket, Options, Timeout) -> + {ok, SslSocket} | + {ok, SslSocket, Ext} | + {error, Reason} when + Socket :: socket() | sslsocket(), + SslSocket :: sslsocket(), + Options :: [server_option()], + Timeout :: timeout(), + Ext :: protocol_extensions(), + Reason :: closed | timeout | {options, any()} | error_alert(). + handshake(#sslsocket{} = Socket, [], Timeout) when (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity)-> handshake(Socket, Timeout); @@ -563,8 +675,12 @@ handshake(Socket, SslOptions, Timeout) when is_port(Socket), %%-------------------------------------------------------------------- --spec handshake_continue(#sslsocket{}, [tls_client_option() | tls_server_option()]) -> - {ok, #sslsocket{}} | {error, reason()}. +-spec handshake_continue(HsSocket, Options) -> + {ok, SslSocket} | {error, Reason} when + HsSocket :: sslsocket(), + Options :: [tls_client_option() | tls_server_option()], + SslSocket :: sslsocket(), + Reason :: closed | timeout | error_alert(). %% %% %% Description: Continues the handshke possible with newly supplied options. @@ -572,8 +688,13 @@ handshake(Socket, SslOptions, Timeout) when is_port(Socket), handshake_continue(Socket, SSLOptions) -> handshake_continue(Socket, SSLOptions, infinity). %%-------------------------------------------------------------------- --spec handshake_continue(#sslsocket{}, [tls_client_option() | tls_server_option()], timeout()) -> - {ok, #sslsocket{}} | {error, reason()}. +-spec handshake_continue(HsSocket, Options, Timeout) -> + {ok, SslSocket} | {error, Reason} when + HsSocket :: sslsocket(), + Options :: [tls_client_option() | tls_server_option()], + Timeout :: timeout(), + SslSocket :: sslsocket(), + Reason :: closed | timeout | error_alert(). %% %% %% Description: Continues the handshke possible with newly supplied options. @@ -581,7 +702,7 @@ handshake_continue(Socket, SSLOptions) -> handshake_continue(Socket, SSLOptions, Timeout) -> ssl_connection:handshake_continue(Socket, SSLOptions, Timeout). %%-------------------------------------------------------------------- --spec handshake_cancel(#sslsocket{}) -> term(). +-spec handshake_cancel(#sslsocket{}) -> any(). %% %% Description: Cancels the handshakes sending a close alert. %%-------------------------------------------------------------------- @@ -589,7 +710,9 @@ handshake_cancel(Socket) -> ssl_connection:handshake_cancel(Socket). %%-------------------------------------------------------------------- --spec close(#sslsocket{}) -> term(). +-spec close(SslSocket) -> ok | {error, Reason} when + SslSocket :: sslsocket(), + Reason :: any(). %% %% Description: Close an ssl connection %%-------------------------------------------------------------------- @@ -601,7 +724,10 @@ close(#sslsocket{pid = {ListenSocket, #config{transport_info={Transport,_,_,_,_} Transport:close(ListenSocket). %%-------------------------------------------------------------------- --spec close(#sslsocket{}, timeout() | {pid(), integer()}) -> term(). +-spec close(SslSocket, How) -> ok | {ok, port()} | {error,Reason} when + SslSocket :: sslsocket(), + How :: timeout() | {NewController::pid(), timeout()}, + Reason :: any(). %% %% Description: Close an ssl connection %%-------------------------------------------------------------------- @@ -617,7 +743,9 @@ close(#sslsocket{pid = {ListenSocket, #config{transport_info={Transport,_,_,_,_} Transport:close(ListenSocket). %%-------------------------------------------------------------------- --spec send(#sslsocket{}, iodata()) -> ok | {error, reason()}. +-spec send(SslSocket, Data) -> ok | {error, reason()} when + SslSocket :: sslsocket(), + Data :: iodata(). %% %% Description: Sends data over the ssl connection %%-------------------------------------------------------------------- @@ -637,11 +765,22 @@ send(#sslsocket{pid = {ListenSocket, #config{transport_info = Info}}}, Data) -> %% %% Description: Receives data when active = false %%-------------------------------------------------------------------- --spec recv(#sslsocket{}, integer()) -> {ok, binary()| list()} | {error, reason()}. +-spec recv(SslSocket, Length) -> {ok, Data} | {error, reason()} when + SslSocket :: sslsocket(), + Length :: integer(), + Data :: binary() | list() | HttpPacket, + HttpPacket :: any(). + recv(Socket, Length) -> recv(Socket, Length, infinity). --spec recv(#sslsocket{}, integer(), timeout()) -> {ok, binary()| list()} | {error, reason()}. +-spec recv(SslSocket, Length, Timeout) -> {ok, Data} | {error, reason()} when + SslSocket :: sslsocket(), + Length :: integer(), + Data :: binary() | list() | HttpPacket, + Timeout :: timeout(), + HttpPacket :: any(). + recv(#sslsocket{pid = [Pid|_]}, Length, Timeout) when is_pid(Pid), (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity)-> ssl_connection:recv(Pid, Length, Timeout); @@ -653,7 +792,10 @@ recv(#sslsocket{pid = {Listen, Transport:recv(Listen, 0). %% {error,enotconn} %%-------------------------------------------------------------------- --spec controlling_process(#sslsocket{}, pid()) -> ok | {error, reason()}. +-spec controlling_process(SslSocket, NewOwner) -> ok | {error, Reason} when + SslSocket :: sslsocket(), + NewOwner :: pid(), + Reason :: any(). %% %% Description: Changes process that receives the messages when active = true %% or once. @@ -672,7 +814,11 @@ controlling_process(#sslsocket{pid = {Listen, %%-------------------------------------------------------------------- --spec connection_information(#sslsocket{}) -> {ok, list()} | {error, reason()}. +-spec connection_information(SslSocket) -> {ok, Result} | {error, reason()} when + SslSocket :: sslsocket(), + Result :: [{OptionName, OptionValue}], + OptionName :: atom(), + OptionValue :: any(). %% %% Description: Return SSL information for the connection %%-------------------------------------------------------------------- @@ -689,7 +835,12 @@ connection_information(#sslsocket{pid = {dtls,_}}) -> {error,enotconn}. %%-------------------------------------------------------------------- --spec connection_information(#sslsocket{}, [atom()]) -> {ok, list()} | {error, reason()}. +-spec connection_information(SslSocket, Items) -> {ok, Result} | {error, reason()} when + SslSocket :: sslsocket(), + Items :: [OptionName], + Result :: [{OptionName, OptionValue}], + OptionName :: atom(), + OptionValue :: any(). %% %% Description: Return SSL information for the connection %%-------------------------------------------------------------------- @@ -703,7 +854,11 @@ connection_information(#sslsocket{pid = [Pid|_]}, Items) when is_pid(Pid) -> end. %%-------------------------------------------------------------------- --spec peername(#sslsocket{}) -> {ok, {inet:ip_address(), inet:port_number()}} | {error, reason()}. +-spec peername(SslSocket) -> {ok, {Address, Port}} | + {error, reason()} when + SslSocket :: sslsocket(), + Address :: inet:ip_address(), + Port :: inet:port_number(). %% %% Description: same as inet:peername/1. %%-------------------------------------------------------------------- @@ -719,7 +874,9 @@ peername(#sslsocket{pid = {dtls,_}}) -> {error,enotconn}. %%-------------------------------------------------------------------- --spec peercert(#sslsocket{}) ->{ok, DerCert::binary()} | {error, reason()}. +-spec peercert(SslSocket) -> {ok, Cert} | {error, reason()} when + SslSocket :: sslsocket(), + Cert :: binary(). %% %% Description: Returns the peercert. %%-------------------------------------------------------------------- @@ -736,7 +893,10 @@ peercert(#sslsocket{pid = {Listen, _}}) when is_port(Listen) -> {error, enotconn}. %%-------------------------------------------------------------------- --spec negotiated_protocol(#sslsocket{}) -> {ok, binary()} | {error, reason()}. +-spec negotiated_protocol(SslSocket) -> {ok, Protocol} | {error, Reason} when + SslSocket :: sslsocket(), + Protocol :: binary(), + Reason :: protocol_not_negotiated. %% %% Description: Returns the protocol that has been negotiated. If no %% protocol has been negotiated will return {error, protocol_not_negotiated} @@ -750,24 +910,26 @@ negotiated_protocol(#sslsocket{pid = [Pid|_]}) when is_pid(Pid) -> cipher_suites() -> cipher_suites(erlang). %%-------------------------------------------------------------------- --spec cipher_suites(erlang | openssl | all) -> - [old_cipher_suite() | string()]. +-spec cipher_suites(Type) -> [old_cipher_suite() | string()] when + Type :: erlang | openssl | all. + %% Description: Returns all supported cipher suites. %%-------------------------------------------------------------------- cipher_suites(erlang) -> - [ssl_cipher_format:erl_suite_definition(Suite) || Suite <- available_suites(default)]; + [ssl_cipher_format:suite_legacy(Suite) || Suite <- available_suites(default)]; cipher_suites(openssl) -> - [ssl_cipher_format:openssl_suite_name(Suite) || + [ssl_cipher_format:suite_map_to_openssl_str(ssl_cipher_format:suite_bin_to_map(Suite)) || Suite <- available_suites(default)]; cipher_suites(all) -> - [ssl_cipher_format:erl_suite_definition(Suite) || Suite <- available_suites(all)]. + [ssl_cipher_format:suite_legacy(Suite) || Suite <- available_suites(all)]. %%-------------------------------------------------------------------- --spec cipher_suites(default | all | anonymous, ssl_record:ssl_version() | - tls_record:tls_atom_version() | dtls_record:dtls_atom_version()) -> - [erl_cipher_suite()]. +-spec cipher_suites(Supported, Version) -> ciphers() when + Supported :: default | all | anonymous, + Version :: protocol_version(). + %% Description: Returns all default and all supported cipher suites for a %% TLS/DTLS version %%-------------------------------------------------------------------- @@ -780,12 +942,14 @@ cipher_suites(Base, Version) when Version == 'dtlsv1.2'; Version == 'dtlsv1'-> cipher_suites(Base, dtls_record:protocol_version(Version)); cipher_suites(Base, Version) -> - [ssl_cipher_format:suite_definition(Suite) || Suite <- supported_suites(Base, Version)]. + [ssl_cipher_format:suite_bin_to_map(Suite) || Suite <- supported_suites(Base, Version)]. %%-------------------------------------------------------------------- --spec filter_cipher_suites([erl_cipher_suite()], - [{key_exchange | cipher | mac | prf, fun()}] | []) -> - [erl_cipher_suite()]. +-spec filter_cipher_suites(Suites, Filters) -> Ciphers when + Suites :: ciphers(), + Filters :: cipher_filters(), + Ciphers :: ciphers(). + %% Description: Removes cipher suites if any of the filter functions returns false %% for any part of the cipher suite. This function also calls default filter functions %% to make sure the cipher suite are supported by crypto. @@ -802,10 +966,10 @@ filter_cipher_suites(Suites, Filters0) -> prf_filters => add_filter(proplists:get_value(prf, Filters0), PrfF)}, ssl_cipher:filter_suites(Suites, Filters). %%-------------------------------------------------------------------- --spec prepend_cipher_suites([erl_cipher_suite()] | - [{key_exchange | cipher | mac | prf, fun()}], - [erl_cipher_suite()]) -> - [erl_cipher_suite()]. +-spec prepend_cipher_suites(Preferred, Suites) -> ciphers() when + Preferred :: ciphers() | cipher_filters(), + Suites :: ciphers(). + %% Description: Make <Preferred> suites become the most prefered %% suites that is put them at the head of the cipher suite list %% and remove them from <Suites> if present. <Preferred> may be a @@ -820,10 +984,10 @@ prepend_cipher_suites(Filters, Suites) -> Preferred = filter_cipher_suites(Suites, Filters), Preferred ++ (Suites -- Preferred). %%-------------------------------------------------------------------- --spec append_cipher_suites(Deferred :: [erl_cipher_suite()] | - [{key_exchange | cipher | mac | prf, fun()}], - [erl_cipher_suite()]) -> - [erl_cipher_suite()]. +-spec append_cipher_suites(Deferred, Suites) -> ciphers() when + Deferred :: ciphers() | cipher_filters(), + Suites :: ciphers(). + %% Description: Make <Deferred> suites suites become the %% least prefered suites that is put them at the end of the cipher suite list %% and removed them from <Suites> if present. @@ -837,7 +1001,9 @@ append_cipher_suites(Filters, Suites) -> (Suites -- Deferred) ++ Deferred. %%-------------------------------------------------------------------- --spec eccs() -> tls_v1:curves(). +-spec eccs() -> NamedCurves when + NamedCurves :: [named_curve()]. + %% Description: returns all supported curves across all versions %%-------------------------------------------------------------------- eccs() -> @@ -845,27 +1011,24 @@ eccs() -> eccs_filter_supported(Curves). %%-------------------------------------------------------------------- --spec eccs(tls_record:tls_atom_version() | - ssl_record:ssl_version() | dtls_record:dtls_atom_version()) -> - tls_v1:curves(). +-spec eccs(Version) -> NamedCurves when + Version :: protocol_version(), + NamedCurves :: [named_curve()]. + %% Description: returns the curves supported for a given version of %% ssl/tls. %%-------------------------------------------------------------------- -eccs({3,0}) -> +eccs(sslv3) -> []; -eccs({3,_}) -> - Curves = tls_v1:ecc_curves(all), - eccs_filter_supported(Curves); -eccs({254,_} = Version) -> - eccs(dtls_v1:corresponding_tls_version(Version)); +eccs('dtlsv1') -> + eccs('tlsv1.1'); +eccs('dtlsv1.2') -> + eccs('tlsv1.2'); eccs(Version) when Version == 'tlsv1.2'; Version == 'tlsv1.1'; - Version == tlsv1; - Version == sslv3 -> - eccs(tls_record:protocol_version(Version)); -eccs(Version) when Version == 'dtlsv1.2'; - Version == 'dtlsv1'-> - eccs(dtls_v1:corresponding_tls_version(dtls_record:protocol_version(Version))). + Version == tlsv1 -> + Curves = tls_v1:ecc_curves(all), + eccs_filter_supported(Curves). eccs_filter_supported(Curves) -> CryptoCurves = crypto:ec_curves(), @@ -873,14 +1036,30 @@ eccs_filter_supported(Curves) -> Curves). %%-------------------------------------------------------------------- --spec getopts(#sslsocket{}, [gen_tcp:option_name()]) -> - {ok, [gen_tcp:option()]} | {error, reason()}. +-spec groups() -> [group()]. +%% Description: returns all supported groups (TLS 1.3 and later) +%%-------------------------------------------------------------------- +groups() -> + tls_v1:groups(4). + +%%-------------------------------------------------------------------- +-spec groups(default) -> [group()]. +%% Description: returns the default groups (TLS 1.3 and later) +%%-------------------------------------------------------------------- +groups(default) -> + tls_v1:default_groups(4). + +%%-------------------------------------------------------------------- +-spec getopts(SslSocket, OptionNames) -> + {ok, [gen_tcp:option()]} | {error, reason()} when + SslSocket :: sslsocket(), + OptionNames :: [gen_tcp:option_name()]. %% %% Description: Gets options %%-------------------------------------------------------------------- getopts(#sslsocket{pid = [Pid|_]}, OptionTags) when is_pid(Pid), is_list(OptionTags) -> ssl_connection:get_opts(Pid, OptionTags); -getopts(#sslsocket{pid = {dtls, #config{transport_info = {Transport,_,_,_}}}} = ListenSocket, OptionTags) when is_list(OptionTags) -> +getopts(#sslsocket{pid = {dtls, #config{transport_info = {Transport,_,_,_,_}}}} = ListenSocket, OptionTags) when is_list(OptionTags) -> try dtls_socket:getopts(Transport, ListenSocket, OptionTags) of {ok, _} = Result -> Result; @@ -905,7 +1084,9 @@ getopts(#sslsocket{}, OptionTags) -> {error, {options, {socket_options, OptionTags}}}. %%-------------------------------------------------------------------- --spec setopts(#sslsocket{}, [gen_tcp:option()]) -> ok | {error, reason()}. +-spec setopts(SslSocket, Options) -> ok | {error, reason()} when + SslSocket :: sslsocket(), + Options :: [gen_tcp:option()]. %% %% Description: Sets options %%-------------------------------------------------------------------- @@ -937,7 +1118,7 @@ setopts(#sslsocket{pid = [Pid|_]}, Options0) when is_pid(Pid), is_list(Options0) _:_ -> {error, {options, {not_a_proplist, Options0}}} end; -setopts(#sslsocket{pid = {dtls, #config{transport_info = {Transport,_,_,_}}}} = ListenSocket, Options) when is_list(Options) -> +setopts(#sslsocket{pid = {dtls, #config{transport_info = {Transport,_,_,_,_}}}} = ListenSocket, Options) when is_list(Options) -> try dtls_socket:setopts(Transport, ListenSocket, Options) of ok -> ok; @@ -961,9 +1142,9 @@ setopts(#sslsocket{}, Options) -> {error, {options,{not_a_proplist, Options}}}. %%--------------------------------------------------------------- --spec getstat(Socket) -> - {ok, OptionValues} | {error, inet:posix()} when - Socket :: #sslsocket{}, +-spec getstat(SslSocket) -> + {ok, OptionValues} | {error, inet:posix()} when + SslSocket :: sslsocket(), OptionValues :: [{inet:stat_option(), integer()}]. %% %% Description: Get all statistic options for a socket. @@ -972,22 +1153,24 @@ getstat(Socket) -> getstat(Socket, inet:stats()). %%--------------------------------------------------------------- --spec getstat(Socket, Options) -> - {ok, OptionValues} | {error, inet:posix()} when - Socket :: #sslsocket{}, +-spec getstat(SslSocket, Options) -> + {ok, OptionValues} | {error, inet:posix()} when + SslSocket :: sslsocket(), Options :: [inet:stat_option()], OptionValues :: [{inet:stat_option(), integer()}]. %% %% Description: Get one or more statistic options for a socket. %%-------------------------------------------------------------------- -getstat(#sslsocket{pid = {Listen, #config{transport_info = {Transport, _, _, _}}}}, Options) when is_port(Listen), is_list(Options) -> +getstat(#sslsocket{pid = {Listen, #config{transport_info = {Transport, _, _, _, _}}}}, Options) when is_port(Listen), is_list(Options) -> tls_socket:getstat(Transport, Listen, Options); getstat(#sslsocket{pid = [Pid|_], fd = {Transport, Socket, _, _}}, Options) when is_pid(Pid), is_list(Options) -> tls_socket:getstat(Transport, Socket, Options). %%--------------------------------------------------------------- --spec shutdown(#sslsocket{}, read | write | read_write) -> ok | {error, reason()}. +-spec shutdown(SslSocket, How) -> ok | {error, reason()} when + SslSocket :: sslsocket(), + How :: read | write | read_write. %% %% Description: Same as gen_tcp:shutdown/2 %%-------------------------------------------------------------------- @@ -1001,7 +1184,11 @@ shutdown(#sslsocket{pid = [Pid|_]}, How) when is_pid(Pid) -> ssl_connection:shutdown(Pid, How). %%-------------------------------------------------------------------- --spec sockname(#sslsocket{}) -> {ok, {inet:ip_address(), inet:port_number()}} | {error, reason()}. +-spec sockname(SslSocket) -> + {ok, {Address, Port}} | {error, reason()} when + SslSocket :: sslsocket(), + Address :: inet:ip_address(), + Port :: inet:port_number(). %% %% Description: Same as inet:sockname/1 %%-------------------------------------------------------------------- @@ -1015,10 +1202,10 @@ sockname(#sslsocket{pid = [Pid| _], fd = {Transport, Socket,_,_}}) when is_pid(P tls_socket:sockname(Transport, Socket). %%--------------------------------------------------------------- --spec versions() -> [{ssl_app, string()} | {supported, [tls_record:tls_atom_version()]} | - {supported_dtls, [dtls_record:dtls_atom_version()]} | - {available, [tls_record:tls_atom_version()]} | - {available_dtls, [dtls_record:dtls_atom_version()]}]. +-spec versions() -> [VersionInfo] when + VersionInfo :: {ssl_app, string()} | + {supported | available, [tls_version()]} | + {supported_dtls | available_dtls, [dtls_version()]}. %% %% Description: Returns a list of relevant versions. %%-------------------------------------------------------------------- @@ -1036,7 +1223,8 @@ versions() -> %%--------------------------------------------------------------- --spec renegotiate(#sslsocket{}) -> ok | {error, reason()}. +-spec renegotiate(SslSocket) -> ok | {error, reason()} when + SslSocket :: sslsocket(). %% %% Description: Initiates a renegotiation. %%-------------------------------------------------------------------- @@ -1056,9 +1244,13 @@ renegotiate(#sslsocket{pid = {Listen,_}}) when is_port(Listen) -> {error, enotconn}. %%-------------------------------------------------------------------- --spec prf(#sslsocket{}, binary() | 'master_secret', binary(), - [binary() | prf_random()], non_neg_integer()) -> - {ok, binary()} | {error, reason()}. +-spec prf(SslSocket, Secret, Label, Seed, WantedLength) -> + {ok, binary()} | {error, reason()} when + SslSocket :: sslsocket(), + Secret :: binary() | 'master_secret', + Label::binary(), + Seed :: [binary() | prf_random()], + WantedLength :: non_neg_integer(). %% %% Description: use a ssl sessions TLS PRF to generate key material %%-------------------------------------------------------------------- @@ -1079,7 +1271,8 @@ clear_pem_cache() -> ssl_pem_cache:clear(). %%--------------------------------------------------------------- --spec format_error({error, term()}) -> list(). +-spec format_error({error, Reason}) -> string() when + Reason :: any(). %% %% Description: Creates error string. %%-------------------------------------------------------------------- @@ -1117,15 +1310,20 @@ tls_version({3, _} = Version) -> tls_version({254, _} = Version) -> dtls_v1:corresponding_tls_version(Version). - %%-------------------------------------------------------------------- --spec suite_to_str(erl_cipher_suite()) -> string(). +-spec suite_to_str(CipherSuite) -> string() when + CipherSuite :: erl_cipher_suite(); + (CipherSuite) -> string() when + %% For internal use! + CipherSuite :: #{key_exchange := null, + cipher := null, + mac := null, + prf := null}. %% %% Description: Return the string representation of a cipher suite. %%-------------------------------------------------------------------- suite_to_str(Cipher) -> - ssl_cipher_format:suite_to_str(Cipher). - + ssl_cipher_format:suite_map_to_str(Cipher). %%%-------------------------------------------------------------- %%% Internal functions @@ -1186,9 +1384,10 @@ handle_options(Opts0, #ssl_options{protocol = Protocol, cacerts = CaCerts0, [] -> new_ssl_options(SslOpts1, NewVerifyOpts, RecordCB); Value -> - Versions = [RecordCB:protocol_version(Vsn) || Vsn <- Value], + Versions0 = [RecordCB:protocol_version(Vsn) || Vsn <- Value], + Versions1 = lists:sort(fun RecordCB:is_higher/2, Versions0), new_ssl_options(proplists:delete(versions, SslOpts1), - NewVerifyOpts#ssl_options{versions = Versions}, record_cb(Protocol)) + NewVerifyOpts#ssl_options{versions = Versions1}, record_cb(Protocol)) end; %% Handle all options in listen and connect @@ -1204,12 +1403,14 @@ handle_options(Opts0, Role, Host) -> CertFile = handle_option(certfile, Opts, <<>>), - Versions = case handle_option(versions, Opts, []) of - [] -> - RecordCb:supported_protocol_versions(); - Vsns -> - [RecordCb:protocol_version(Vsn) || Vsn <- Vsns] - end, + [HighestVersion|_] = Versions = + case handle_option(versions, Opts, []) of + [] -> + RecordCb:supported_protocol_versions(); + Vsns -> + Versions0 = [RecordCb:protocol_version(Vsn) || Vsn <- Vsns], + lists:sort(fun RecordCb:is_higher/2, Versions0) + end, Protocol = handle_option(protocol, Opts, tls), @@ -1220,7 +1421,7 @@ handle_options(Opts0, Role, Host) -> ok end, - SSLOptions = #ssl_options{ + SSLOptions0 = #ssl_options{ versions = Versions, verify = validate_option(verify, Verify), verify_fun = VerifyFun, @@ -1241,13 +1442,29 @@ handle_options(Opts0, Role, Host) -> psk_identity = handle_option(psk_identity, Opts, undefined), srp_identity = handle_option(srp_identity, Opts, undefined), ciphers = handle_cipher_option(proplists:get_value(ciphers, Opts, []), - RecordCb:highest_protocol_version(Versions)), + HighestVersion), eccs = handle_eccs_option(proplists:get_value(eccs, Opts, eccs()), - RecordCb:highest_protocol_version(Versions)), - signature_algs = handle_hashsigns_option(proplists:get_value(signature_algs, Opts, - default_option_role(server, - tls_v1:default_signature_algs(Versions), Role)), - tls_version(RecordCb:highest_protocol_version(Versions))), + HighestVersion), + supported_groups = handle_supported_groups_option( + proplists:get_value(supported_groups, Opts, groups(default)), + HighestVersion), + signature_algs = + handle_hashsigns_option( + proplists:get_value( + signature_algs, + Opts, + default_option_role_sign_algs(server, + tls_v1:default_signature_algs(HighestVersion), + Role, + HighestVersion)), + tls_version(HighestVersion)), + signature_algs_cert = + handle_signature_algorithms_option( + proplists:get_value( + signature_algs_cert, + Opts, + undefined), %% Do not send by default + tls_version(HighestVersion)), reuse_sessions = handle_reuse_sessions_option(reuse_sessions, Opts, Role), reuse_session = handle_reuse_session_option(reuse_session, Opts, Role), secure_renegotiate = handle_option(secure_renegotiate, Opts, true), @@ -1266,7 +1483,6 @@ handle_options(Opts0, Role, Host) -> next_protocol_selector = make_next_protocol_selector( handle_option(client_preferred_next_protocols, Opts, undefined)), - log_alert = handle_option(log_alert, Opts, true), server_name_indication = handle_option(server_name_indication, Opts, default_option_role(client, server_name_indication_default(Host), Role)), @@ -1292,6 +1508,10 @@ handle_options(Opts0, Role, Host) -> handshake = handle_option(handshake, Opts, full), customize_hostname_check = handle_option(customize_hostname_check, Opts, []) }, + LogLevel = handle_option(log_alert, Opts, true), + SSLOptions = SSLOptions0#ssl_options{ + log_level = handle_option(log_level, Opts, LogLevel) + }, CbInfo = handle_option(cb_info, Opts, default_cb_info(Protocol)), SslOptions = [protocol, versions, verify, verify_fun, partial_chain, @@ -1303,10 +1523,12 @@ handle_options(Opts0, Role, Host) -> cb_info, renegotiate_at, secure_renegotiate, hibernate_after, erl_dist, alpn_advertised_protocols, sni_hosts, sni_fun, alpn_preferred_protocols, next_protocols_advertised, - client_preferred_next_protocols, log_alert, + 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), @@ -1491,7 +1713,20 @@ validate_option(client_preferred_next_protocols, {Precedence, PreferredProtocols Value; validate_option(client_preferred_next_protocols, undefined) -> undefined; -validate_option(log_alert, Value) when is_boolean(Value) -> +validate_option(log_alert, true) -> + notice; +validate_option(log_alert, false) -> + warning; +validate_option(log_level, Value) when + is_atom(Value) andalso + (Value =:= emergency orelse + Value =:= alert orelse + Value =:= critical orelse + Value =:= error orelse + Value =:= warning orelse + Value =:= notice orelse + Value =:= info orelse + Value =:= debug) -> Value; validate_option(next_protocols_advertised, Value) when is_list(Value) -> validate_binary_list(next_protocols_advertised, Value), @@ -1583,19 +1818,42 @@ handle_cb_info({V1, V2, V3, V4}, {_,_,_,_,_}) -> handle_cb_info(CbInfo, _) -> CbInfo. +handle_hashsigns_option(Value, Version) when is_list(Value) + andalso Version >= {3, 4} -> + case tls_v1:signature_schemes(Version, Value) of + [] -> + throw({error, {options, + no_supported_signature_schemes, + {signature_algs, Value}}}); + _ -> + Value + end; handle_hashsigns_option(Value, Version) when is_list(Value) - andalso Version >= {3, 3} -> + andalso Version =:= {3, 3} -> case tls_v1:signature_algs(Version, Value) of [] -> throw({error, {options, no_supported_algorithms, {signature_algs, Value}}}); _ -> Value end; -handle_hashsigns_option(_, Version) when Version >= {3, 3} -> +handle_hashsigns_option(_, Version) when Version =:= {3, 3} -> handle_hashsigns_option(tls_v1:default_signature_algs(Version), Version); handle_hashsigns_option(_, _Version) -> undefined. +handle_signature_algorithms_option(Value, Version) when is_list(Value) + andalso Version >= {3, 4} -> + case tls_v1:signature_schemes(Version, Value) of + [] -> + throw({error, {options, + no_supported_signature_schemes, + {signature_algs_cert, Value}}}); + _ -> + Value + end; +handle_signature_algorithms_option(_, _Version) -> + undefined. + handle_reuse_sessions_option(Key, Opts, client) -> Value = proplists:get_value(Key, Opts, true), validate_option(Key, Value), @@ -1639,7 +1897,8 @@ validate_binary_list(Opt, List) -> end, List). validate_versions([], Versions) -> Versions; -validate_versions([Version | Rest], Versions) when Version == 'tlsv1.2'; +validate_versions([Version | Rest], Versions) when Version == 'tlsv1.3'; + Version == 'tlsv1.2'; Version == 'tlsv1.1'; Version == tlsv1; Version == sslv3 -> @@ -1652,10 +1911,11 @@ validate_versions([Ver| _], Versions) -> tls_validate_versions([], Versions) -> Versions; -tls_validate_versions([Version | Rest], Versions) when Version == 'tlsv1.2'; - Version == 'tlsv1.1'; - Version == tlsv1; - Version == sslv3 -> +tls_validate_versions([Version | Rest], Versions) when Version == 'tlsv1.3'; + Version == 'tlsv1.2'; + Version == 'tlsv1.1'; + Version == tlsv1; + Version == sslv3 -> tls_validate_versions(Rest, Versions); tls_validate_versions([Ver| _], Versions) -> throw({error, {options, {Ver, {versions, Versions}}}}). @@ -1703,10 +1963,10 @@ binary_cipher_suites(Version, []) -> %% not require explicit configuration default_binary_suites(Version); binary_cipher_suites(Version, [Map|_] = Ciphers0) when is_map(Map) -> - Ciphers = [ssl_cipher_format:suite(C) || C <- Ciphers0], + Ciphers = [ssl_cipher_format:suite_map_to_bin(C) || C <- Ciphers0], binary_cipher_suites(Version, Ciphers); binary_cipher_suites(Version, [Tuple|_] = Ciphers0) when is_tuple(Tuple) -> - Ciphers = [ssl_cipher_format:suite(tuple_to_map(C)) || C <- Ciphers0], + Ciphers = [ssl_cipher_format:suite_map_to_bin(tuple_to_map(C)) || C <- Ciphers0], binary_cipher_suites(Version, Ciphers); binary_cipher_suites(Version, [Cipher0 | _] = Ciphers0) when is_binary(Cipher0) -> All = ssl_cipher:all_suites(Version) ++ @@ -1721,11 +1981,11 @@ binary_cipher_suites(Version, [Cipher0 | _] = Ciphers0) when is_binary(Cipher0) end; binary_cipher_suites(Version, [Head | _] = Ciphers0) when is_list(Head) -> %% Format: ["RC4-SHA","RC4-MD5"] - Ciphers = [ssl_cipher_format:openssl_suite(C) || C <- Ciphers0], + Ciphers = [ssl_cipher_format:suite_openssl_str_to_map(C) || C <- Ciphers0], binary_cipher_suites(Version, Ciphers); binary_cipher_suites(Version, Ciphers0) -> %% Format: "RC4-SHA:RC4-MD5" - Ciphers = [ssl_cipher_format:openssl_suite(C) || C <- string:lexemes(Ciphers0, ":")], + Ciphers = [ssl_cipher_format:suite_openssl_str_to_map(C) || C <- string:lexemes(Ciphers0, ":")], binary_cipher_suites(Version, Ciphers). default_binary_suites(Version) -> @@ -1761,6 +2021,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])). @@ -1906,8 +2176,10 @@ new_ssl_options([{next_protocols_advertised, Value} | Rest], #ssl_options{} = Op new_ssl_options([{client_preferred_next_protocols, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> new_ssl_options(Rest, Opts#ssl_options{next_protocol_selector = make_next_protocol_selector(validate_option(client_preferred_next_protocols, Value))}, RecordCB); -new_ssl_options([{log_alert, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> - new_ssl_options(Rest, Opts#ssl_options{log_alert = validate_option(log_alert, Value)}, RecordCB); +new_ssl_options([{log_alert, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> + new_ssl_options(Rest, Opts#ssl_options{log_level = validate_option(log_alert, Value)}, RecordCB); +new_ssl_options([{log_level, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> + new_ssl_options(Rest, Opts#ssl_options{log_level = validate_option(log_level, Value)}, RecordCB); new_ssl_options([{server_name_indication, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> new_ssl_options(Rest, Opts#ssl_options{server_name_indication = validate_option(server_name_indication, Value)}, RecordCB); new_ssl_options([{honor_cipher_order, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> @@ -1920,12 +2192,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) -> @@ -1987,15 +2273,24 @@ handle_verify_options(Opts, CaCerts) -> throw({error, {options, {verify, Value}}}) end. +%% Added to handle default values for signature_algs in TLS 1.3 +default_option_role_sign_algs(_, Value, _, Version) when Version >= {3,4} -> + Value; +default_option_role_sign_algs(Role, Value, Role, _) -> + Value; +default_option_role_sign_algs(_, _, _, _) -> + undefined. + default_option_role(Role, Value, Role) -> Value; default_option_role(_,_,_) -> undefined. + default_cb_info(tls) -> {gen_tcp, tcp, tcp_closed, tcp_error, tcp_passive}; default_cb_info(dtls) -> - {gen_udp, udp, udp_closed, udp_error}. + {gen_udp, udp, udp_closed, udp_error, udp_passive}. include_security_info([]) -> false; diff --git a/lib/ssl/src/ssl_alert.erl b/lib/ssl/src/ssl_alert.erl index 2a20d13cd5..06b1b005a5 100644 --- a/lib/ssl/src/ssl_alert.erl +++ b/lib/ssl/src/ssl_alert.erl @@ -161,10 +161,14 @@ description_txt(?INSUFFICIENT_SECURITY) -> "Insufficient Security"; description_txt(?INTERNAL_ERROR) -> "Internal Error"; +description_txt(?INAPPROPRIATE_FALLBACK) -> + "Inappropriate Fallback"; description_txt(?USER_CANCELED) -> "User Canceled"; description_txt(?NO_RENEGOTIATION) -> "No Renegotiation"; +description_txt(?MISSING_EXTENSION) -> + "Missing extension"; description_txt(?UNSUPPORTED_EXTENSION) -> "Unsupported Extension"; description_txt(?CERTIFICATE_UNOBTAINABLE) -> @@ -177,8 +181,8 @@ description_txt(?BAD_CERTIFICATE_HASH_VALUE) -> "Bad Certificate Hash Value"; description_txt(?UNKNOWN_PSK_IDENTITY) -> "Unknown Psk Identity"; -description_txt(?INAPPROPRIATE_FALLBACK) -> - "Inappropriate Fallback"; +description_txt(?CERTIFICATE_REQUIRED) -> + "Certificate required"; description_txt(?NO_APPLICATION_PROTOCOL) -> "No application protocol"; description_txt(Enum) -> @@ -228,10 +232,14 @@ description_atom(?INSUFFICIENT_SECURITY) -> insufficient_security; description_atom(?INTERNAL_ERROR) -> internal_error; +description_atom(?INAPPROPRIATE_FALLBACK) -> + inappropriate_fallback; description_atom(?USER_CANCELED) -> user_canceled; description_atom(?NO_RENEGOTIATION) -> no_renegotiation; +description_atom(?MISSING_EXTENSION) -> + missing_extension; description_atom(?UNSUPPORTED_EXTENSION) -> unsupported_extension; description_atom(?CERTIFICATE_UNOBTAINABLE) -> @@ -244,9 +252,9 @@ description_atom(?BAD_CERTIFICATE_HASH_VALUE) -> bad_certificate_hash_value; description_atom(?UNKNOWN_PSK_IDENTITY) -> unknown_psk_identity; -description_atom(?INAPPROPRIATE_FALLBACK) -> - inappropriate_fallback; +description_atom(?CERTIFICATE_REQUIRED) -> + certificate_required; description_atom(?NO_APPLICATION_PROTOCOL) -> no_application_protocol; description_atom(_) -> - 'unsupported/unkonwn_alert'. + 'unsupported/unknown_alert'. diff --git a/lib/ssl/src/ssl_alert.hrl b/lib/ssl/src/ssl_alert.hrl index b23123905e..9b2322da17 100644 --- a/lib/ssl/src/ssl_alert.hrl +++ b/lib/ssl/src/ssl_alert.hrl @@ -29,6 +29,9 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Alert protocol - RFC 2246 section 7.2 +%%% updated by RFC 8486 with +%%% missing_extension(109), +%%% certificate_required(116), %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% AlertLevel @@ -100,12 +103,14 @@ -define(INAPPROPRIATE_FALLBACK, 86). -define(USER_CANCELED, 90). -define(NO_RENEGOTIATION, 100). +-define(MISSING_EXTENSION, 109). -define(UNSUPPORTED_EXTENSION, 110). -define(CERTIFICATE_UNOBTAINABLE, 111). -define(UNRECOGNISED_NAME, 112). -define(BAD_CERTIFICATE_STATUS_RESPONSE, 113). -define(BAD_CERTIFICATE_HASH_VALUE, 114). -define(UNKNOWN_PSK_IDENTITY, 115). +-define(CERTIFICATE_REQUIRED, 116). -define(NO_APPLICATION_PROTOCOL, 120). -define(ALERT_REC(Level,Desc), #alert{level=Level,description=Desc,where={?FILE, ?LINE}}). diff --git a/lib/ssl/src/ssl_app.erl b/lib/ssl/src/ssl_app.erl index 62e8765d4a..9e6d676bef 100644 --- a/lib/ssl/src/ssl_app.erl +++ b/lib/ssl/src/ssl_app.erl @@ -29,9 +29,26 @@ -export([start/2, stop/1]). start(_Type, _StartArgs) -> + start_logger(), ssl_sup:start_link(). stop(_State) -> + stop_logger(), ok. +%% +%% Description: Start SSL logger +start_logger() -> + Config = #{level => debug, + filter_default => stop, + formatter => {ssl_logger, #{}}}, + Filter = {fun logger_filters:domain/2,{log,sub,[otp,ssl]}}, + logger:add_handler(ssl_handler, logger_std_h, Config), + logger:add_handler_filter(ssl_handler, filter_non_ssl, Filter), + logger:set_application_level(ssl, debug). +%% +%% Description: Stop SSL logger +stop_logger() -> + logger:unset_application_level(ssl), + logger:remove_handler(ssl_handler). diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl index 549e557beb..9997f5e0c8 100644 --- a/lib/ssl/src/ssl_certificate.erl +++ b/lib/ssl/src/ssl_certificate.erl @@ -71,7 +71,7 @@ trusted_cert_and_path(CertChain, CertDbHandle, CertDbRef, PartialChainHandler) - case SignedAndIssuerID of {error, issuer_not_found} -> - %% The root CA was not sent and can not be found. + %% The root CA was not sent and cannot be found. handle_incomplete_chain(Path, PartialChainHandler); {self, _} when length(Path) == 1 -> {selfsigned_peer, Path}; diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl index fce48d1678..21db887bb5 100644 --- a/lib/ssl/src/ssl_cipher.erl +++ b/lib/ssl/src/ssl_cipher.erl @@ -31,10 +31,11 @@ -include("ssl_cipher.hrl"). -include("ssl_handshake.hrl"). -include("ssl_alert.hrl"). +-include("tls_handshake_1_3.hrl"). -include_lib("public_key/include/public_key.hrl"). --export([security_parameters/2, security_parameters/3, - cipher_init/3, nonce_seed/2, decipher/6, cipher/5, aead_encrypt/5, aead_decrypt/6, +-export([security_parameters/2, security_parameters/3, security_parameters_1_3/2, + cipher_init/3, nonce_seed/2, decipher/6, cipher/5, aead_encrypt/6, 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, @@ -42,7 +43,12 @@ filter/3, filter_suites/1, filter_suites/2, hash_algorithm/1, sign_algorithm/1, is_acceptable_hash/2, is_fallback/1, random_bytes/1, calc_mac_hash/4, calc_mac_hash/6, - is_stream_ciphersuite/1]). + is_stream_ciphersuite/1, signature_scheme/1, + scheme_to_components/1, hash_size/1, effective_key_bits/1, + key_material/1, signature_algorithm_to_scheme/1]). + +%% RFC 8446 TLS 1.3 +-export([generate_client_shares/1, generate_server_share/1, add_zero_padding/2]). -compile(inline). @@ -70,7 +76,7 @@ security_parameters(?TLS_NULL_WITH_NULL_NULL = CipherSuite, SecParams) -> %%------------------------------------------------------------------- security_parameters(Version, CipherSuite, SecParams) -> #{cipher := Cipher, mac := Hash, - prf := PrfHashAlg} = ssl_cipher_format:suite_definition(CipherSuite), + prf := PrfHashAlg} = ssl_cipher_format:suite_bin_to_map(CipherSuite), SecParams#security_parameters{ cipher_suite = CipherSuite, bulk_cipher_algorithm = bulk_cipher_algorithm(Cipher), @@ -83,6 +89,15 @@ security_parameters(Version, CipherSuite, SecParams) -> prf_algorithm = prf_algorithm(PrfHashAlg, Version), hash_size = hash_size(Hash)}. +security_parameters_1_3(SecParams, CipherSuite) -> + #{cipher := Cipher, prf := PrfHashAlg} = + ssl_cipher_format:suite_bin_to_map(CipherSuite), + SecParams#security_parameters{ + cipher_suite = CipherSuite, + bulk_cipher_algorithm = bulk_cipher_algorithm(Cipher), + prf_algorithm = PrfHashAlg, %% HKDF hash algorithm + cipher_type = ?AEAD}. + %%-------------------------------------------------------------------- -spec cipher_init(cipher_enum(), binary(), binary()) -> #cipher_state{}. %% @@ -91,9 +106,13 @@ security_parameters(Version, CipherSuite, SecParams) -> cipher_init(?RC4, IV, Key) -> State = crypto:stream_init(rc4, Key), #cipher_state{iv = IV, key = Key, state = State}; -cipher_init(?AES_GCM, IV, Key) -> +cipher_init(Type, IV, Key) when Type == ?AES_GCM; + Type == ?AES_CCM -> <<Nonce:64>> = random_bytes(8), #cipher_state{iv = IV, key = Key, nonce = Nonce, tag_len = 16}; +cipher_init(?AES_CCM_8, IV, Key) -> + <<Nonce:64>> = random_bytes(8), + #cipher_state{iv = IV, key = Key, nonce = Nonce, tag_len = 8}; cipher_init(?CHACHA20_POLY1305, IV, Key) -> #cipher_state{iv = IV, key = Key, tag_len = 16}; cipher_init(_BCA, IV, Key) -> @@ -133,14 +152,18 @@ 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). -aead_encrypt(Type, Key, Nonce, Fragment, AdditionalData) -> - crypto:block_encrypt(aead_type(Type), Key, Nonce, {AdditionalData, Fragment}). +aead_encrypt(Type, Key, Nonce, Fragment, AdditionalData, TagLen) -> + crypto:block_encrypt(aead_type(Type), Key, Nonce, {AdditionalData, Fragment, TagLen}). 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(?AES_CCM) -> + aes_ccm; +aead_type(?AES_CCM_8) -> + aes_ccm; aead_type(?CHACHA20_POLY1305) -> chacha20_poly1305. @@ -158,7 +181,7 @@ block_cipher(Fun, BlockSz, #cipher_state{key=Key, iv=IV} = CS0, block_cipher(Fun, BlockSz, #cipher_state{key=Key, iv=IV, state = IV_Cache0} = CS0, Mac, Fragment, {3, N}) - when N == 2; N == 3 -> + when N == 2; N == 3; N == 4 -> IV_Size = byte_size(IV), <<NextIV:IV_Size/binary, IV_Cache/binary>> = case IV_Cache0 of @@ -294,8 +317,9 @@ anonymous_suites({3, N}) -> srp_suites_anon() ++ anonymous_suites(N); anonymous_suites({254, _} = Version) -> dtls_v1:anonymous_suites(Version); -anonymous_suites(N) - when N >= 3 -> +anonymous_suites(4) -> + []; %% Raw public key negotiation may be used instead +anonymous_suites( 3 = N) -> psk_suites_anon(N) ++ [?TLS_DH_anon_WITH_AES_128_GCM_SHA256, ?TLS_DH_anon_WITH_AES_256_GCM_SHA384, @@ -328,8 +352,9 @@ anonymous_suites(N) when N == 0; %%-------------------------------------------------------------------- psk_suites({3, N}) -> psk_suites(N); -psk_suites(N) - when N >= 3 -> +psk_suites(4) -> + []; %% TODO Add new PSK, PSK_(EC)DHE suites +psk_suites(3) -> [ ?TLS_RSA_PSK_WITH_AES_256_GCM_SHA384, ?TLS_RSA_PSK_WITH_AES_256_CBC_SHA384, @@ -350,20 +375,32 @@ psk_suites(_) -> %%-------------------------------------------------------------------- psk_suites_anon({3, N}) -> psk_suites_anon(N); -psk_suites_anon(N) - when N >= 3 -> +psk_suites_anon(3) -> [ ?TLS_DHE_PSK_WITH_AES_256_GCM_SHA384, ?TLS_PSK_WITH_AES_256_GCM_SHA384, ?TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384, ?TLS_DHE_PSK_WITH_AES_256_CBC_SHA384, ?TLS_PSK_WITH_AES_256_CBC_SHA384, + ?TLS_DHE_PSK_WITH_AES_256_CCM, + ?TLS_PSK_DHE_WITH_AES_256_CCM_8, + ?TLS_PSK_WITH_AES_256_CCM, + ?TLS_PSK_WITH_AES_256_CCM_8, ?TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256, + ?TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256, + ?TLS_ECDHE_PSK_WITH_AES_128_CCM_8_SHA256, ?TLS_DHE_PSK_WITH_AES_128_GCM_SHA256, ?TLS_PSK_WITH_AES_128_GCM_SHA256, + ?TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256, + ?TLS_ECDHE_PSK_WITH_AES_128_CCM_8_SHA256, ?TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, ?TLS_DHE_PSK_WITH_AES_128_CBC_SHA256, - ?TLS_PSK_WITH_AES_128_CBC_SHA256 + ?TLS_PSK_WITH_AES_128_CBC_SHA256, + ?TLS_DHE_PSK_WITH_AES_128_CCM, + ?TLS_PSK_DHE_WITH_AES_128_CCM_8, + ?TLS_PSK_WITH_AES_128_CCM, + ?TLS_PSK_WITH_AES_128_CCM_8, + ?TLS_ECDHE_PSK_WITH_RC4_128_SHA ] ++ psk_suites_anon(0); psk_suites_anon(_) -> [?TLS_DHE_PSK_WITH_AES_256_CBC_SHA, @@ -418,11 +455,12 @@ rc4_suites({3, Minor}) -> rc4_suites(0) -> [?TLS_RSA_WITH_RC4_128_SHA, ?TLS_RSA_WITH_RC4_128_MD5]; -rc4_suites(N) when N =< 3 -> +rc4_suites(N) when N =< 4 -> [?TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, ?TLS_ECDHE_RSA_WITH_RC4_128_SHA, ?TLS_ECDH_ECDSA_WITH_RC4_128_SHA, ?TLS_ECDH_RSA_WITH_RC4_128_SHA]. + %%-------------------------------------------------------------------- -spec des_suites(Version::ssl_record:ssl_version()) -> [ssl_cipher_format:cipher_suite()]. %% @@ -457,7 +495,7 @@ rsa_suites(0) -> ?TLS_RSA_WITH_AES_128_CBC_SHA, ?TLS_RSA_WITH_3DES_EDE_CBC_SHA ]; -rsa_suites(N) when N =< 3 -> +rsa_suites(N) when N =< 4 -> [ ?TLS_RSA_WITH_AES_256_GCM_SHA384, ?TLS_RSA_WITH_AES_256_CBC_SHA256, @@ -511,7 +549,7 @@ filter_suite(#{key_exchange := KeyExchange, all_filters(Hash, HashFilters) andalso all_filters(Prf, PrfFilters); filter_suite(Suite, Filters) -> - filter_suite(ssl_cipher_format:suite_definition(Suite), Filters). + filter_suite(ssl_cipher_format:suite_bin_to_map(Suite), Filters). %%-------------------------------------------------------------------- -spec filter_suites([ssl:erl_cipher_suite()] | [ssl_cipher_format:cipher_suite()]) -> @@ -556,7 +594,8 @@ crypto_support_filters() -> end]}. is_acceptable_keyexchange(KeyExchange, _Algos) when KeyExchange == psk; - KeyExchange == null -> + KeyExchange == null; + KeyExchange == any -> true; is_acceptable_keyexchange(KeyExchange, Algos) when KeyExchange == dh_anon; KeyExchange == dhe_psk -> @@ -568,7 +607,7 @@ is_acceptable_keyexchange(dhe_rsa, Algos) -> proplists:get_bool(dh, Algos) andalso proplists:get_bool(rsa, Algos); is_acceptable_keyexchange(KeyExchange, Algos) when KeyExchange == ecdh_anon; - KeyExchange == ecdhe_psk -> + KeyExchange == ecdhe_psk -> proplists:get_bool(ecdh, Algos); is_acceptable_keyexchange(KeyExchange, Algos) when KeyExchange == ecdh_ecdsa; KeyExchange == ecdhe_ecdsa -> @@ -608,6 +647,12 @@ is_acceptable_cipher(Cipher, Algos) when Cipher == aes_128_gcm; Cipher == aes_256_gcm -> proplists:get_bool(aes_gcm, Algos); +is_acceptable_cipher(Cipher, Algos) + when Cipher == aes_128_ccm; + Cipher == aes_256_ccm; + Cipher == aes_128_ccm_8; + Cipher == aes_256_ccm_8 -> + proplists:get_bool(aes_ccm, Algos); is_acceptable_cipher(Cipher, Algos) -> proplists:get_bool(Cipher, Algos). @@ -651,6 +696,28 @@ is_stream_ciphersuite(#{cipher := rc4_128}) -> true; is_stream_ciphersuite(_) -> false. + +-spec hash_size(atom()) -> integer(). +hash_size(null) -> + 0; +%% The AEAD MAC hash size is not used in the context +%% of calculating the master secret. See RFC 5246 Section 6.2.3.3. +hash_size(aead) -> + 0; +hash_size(md5) -> + 16; +hash_size(sha) -> + 20; +%% Uncomment when adding cipher suite that needs it +%hash_size(sha224) -> +% 28; +hash_size(sha256) -> + 32; +hash_size(sha384) -> + 48; +hash_size(sha512) -> + 64. + %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- @@ -660,7 +727,7 @@ mac_hash({_,_}, ?NULL, _MacSecret, _SeqNo, _Type, mac_hash({3, 0}, MacAlg, MacSecret, SeqNo, Type, Length, Fragment) -> ssl_v3:mac_hash(MacAlg, MacSecret, SeqNo, Type, Length, Fragment); mac_hash({3, N} = Version, MacAlg, MacSecret, SeqNo, Type, Length, Fragment) - when N =:= 1; N =:= 2; N =:= 3 -> + when N =:= 1; N =:= 2; N =:= 3; N =:= 4 -> tls_v1:mac_hash(MacAlg, MacSecret, SeqNo, Type, Version, Length, Fragment). @@ -678,6 +745,12 @@ bulk_cipher_algorithm(Cipher) when Cipher == aes_128_cbc; bulk_cipher_algorithm(Cipher) when Cipher == aes_128_gcm; Cipher == aes_256_gcm -> ?AES_GCM; +bulk_cipher_algorithm(Cipher) when Cipher == aes_128_ccm; + Cipher == aes_256_ccm -> + ?AES_CCM; +bulk_cipher_algorithm(Cipher) when Cipher == aes_128_ccm_8; + Cipher == aes_256_ccm_8 -> + ?AES_CCM_8; bulk_cipher_algorithm(chacha20_poly1305) -> ?CHACHA20_POLY1305. @@ -692,6 +765,10 @@ type(Cipher) when Cipher == des_cbc; ?BLOCK; type(Cipher) when Cipher == aes_128_gcm; Cipher == aes_256_gcm; + Cipher == aes_128_ccm; + Cipher == aes_256_ccm; + Cipher == aes_128_ccm_8; + Cipher == aes_256_ccm_8; Cipher == chacha20_poly1305 -> ?AEAD. @@ -709,8 +786,16 @@ key_material(aes_256_cbc) -> 32; key_material(aes_128_gcm) -> 16; +key_material(aes_128_ccm) -> + 16; +key_material(aes_128_ccm_8) -> + 16; key_material(aes_256_gcm) -> 32; +key_material(aes_256_ccm_8) -> + 32; +key_material(aes_256_ccm) -> + 32; key_material(chacha20_poly1305) -> 32. @@ -726,6 +811,10 @@ expanded_key_material(Cipher) when Cipher == aes_128_cbc; Cipher == aes_256_cbc; Cipher == aes_128_gcm; Cipher == aes_256_gcm; + Cipher == aes_128_ccm; + Cipher == aes_256_ccm; + Cipher == aes_128_ccm_8; + Cipher == aes_256_ccm_8; Cipher == chacha20_poly1305 -> unknown. @@ -735,22 +824,31 @@ effective_key_bits(des_cbc) -> 56; effective_key_bits(Cipher) when Cipher == rc4_128; Cipher == aes_128_cbc; - Cipher == aes_128_gcm -> + Cipher == aes_128_gcm; + Cipher == aes_128_ccm; + Cipher == aes_128_ccm_8 -> 128; effective_key_bits('3des_ede_cbc') -> 168; effective_key_bits(Cipher) when Cipher == aes_256_cbc; Cipher == aes_256_gcm; + Cipher == aes_256_ccm; + Cipher == aes_256_ccm_8; Cipher == chacha20_poly1305 -> 256. iv_size(Cipher) when Cipher == null; - Cipher == rc4_128; - Cipher == chacha20_poly1305-> + Cipher == rc4_128 -> 0; iv_size(Cipher) when Cipher == aes_128_gcm; - Cipher == aes_256_gcm -> + Cipher == aes_256_gcm; + Cipher == aes_128_ccm; + Cipher == aes_256_ccm; + Cipher == aes_128_ccm_8; + Cipher == aes_256_ccm_8 -> 4; +iv_size(chacha20_poly1305) -> + 12; iv_size(Cipher) -> block_size(Cipher). @@ -761,6 +859,10 @@ block_size(Cipher) when Cipher == aes_128_cbc; Cipher == aes_256_cbc; Cipher == aes_128_gcm; Cipher == aes_256_gcm; + Cipher == aes_128_ccm; + Cipher == aes_256_ccm; + Cipher == aes_128_ccm_8; + Cipher == aes_256_ccm_8; Cipher == chacha20_poly1305 -> 16. @@ -804,26 +906,75 @@ sign_algorithm(?ECDSA) -> ecdsa; sign_algorithm(Other) when is_integer(Other) andalso ((Other >= 4) and (Other =< 223)) -> unassigned; sign_algorithm(Other) when is_integer(Other) andalso ((Other >= 224) and (Other =< 255)) -> Other. -hash_size(null) -> - 0; -%% The AEAD MAC hash size is not used in the context -%% of calculating the master secret. See RFC 5246 Section 6.2.3.3. -hash_size(aead) -> - 0; -hash_size(md5) -> - 16; -hash_size(sha) -> - 20; -%% Uncomment when adding cipher suite that needs it -%hash_size(sha224) -> -% 28; -hash_size(sha256) -> - 32; -hash_size(sha384) -> - 48. -%% Uncomment when adding cipher suite that needs it -%hash_size(sha512) -> -% 64. + +signature_scheme(rsa_pkcs1_sha256) -> ?RSA_PKCS1_SHA256; +signature_scheme(rsa_pkcs1_sha384) -> ?RSA_PKCS1_SHA384; +signature_scheme(rsa_pkcs1_sha512) -> ?RSA_PKCS1_SHA512; +signature_scheme(ecdsa_secp256r1_sha256) -> ?ECDSA_SECP256R1_SHA256; +signature_scheme(ecdsa_secp384r1_sha384) -> ?ECDSA_SECP384R1_SHA384; +signature_scheme(ecdsa_secp521r1_sha512) -> ?ECDSA_SECP521R1_SHA512; +signature_scheme(rsa_pss_rsae_sha256) -> ?RSA_PSS_RSAE_SHA256; +signature_scheme(rsa_pss_rsae_sha384) -> ?RSA_PSS_RSAE_SHA384; +signature_scheme(rsa_pss_rsae_sha512) -> ?RSA_PSS_RSAE_SHA512; +signature_scheme(ed25519) -> ?ED25519; +signature_scheme(ed448) -> ?ED448; +signature_scheme(rsa_pss_pss_sha256) -> ?RSA_PSS_PSS_SHA256; +signature_scheme(rsa_pss_pss_sha384) -> ?RSA_PSS_PSS_SHA384; +signature_scheme(rsa_pss_pss_sha512) -> ?RSA_PSS_PSS_SHA512; +signature_scheme(rsa_pkcs1_sha1) -> ?RSA_PKCS1_SHA1; +signature_scheme(ecdsa_sha1) -> ?ECDSA_SHA1; +signature_scheme(?RSA_PKCS1_SHA256) -> rsa_pkcs1_sha256; +signature_scheme(?RSA_PKCS1_SHA384) -> rsa_pkcs1_sha384; +signature_scheme(?RSA_PKCS1_SHA512) -> rsa_pkcs1_sha512; +signature_scheme(?ECDSA_SECP256R1_SHA256) -> ecdsa_secp256r1_sha256; +signature_scheme(?ECDSA_SECP384R1_SHA384) -> ecdsa_secp384r1_sha384; +signature_scheme(?ECDSA_SECP521R1_SHA512) -> ecdsa_secp521r1_sha512; +signature_scheme(?RSA_PSS_RSAE_SHA256) -> rsa_pss_rsae_sha256; +signature_scheme(?RSA_PSS_RSAE_SHA384) -> rsa_pss_rsae_sha384; +signature_scheme(?RSA_PSS_RSAE_SHA512) -> rsa_pss_rsae_sha512; +signature_scheme(?ED25519) -> ed25519; +signature_scheme(?ED448) -> ed448; +signature_scheme(?RSA_PSS_PSS_SHA256) -> rsa_pss_pss_sha256; +signature_scheme(?RSA_PSS_PSS_SHA384) -> rsa_pss_pss_sha384; +signature_scheme(?RSA_PSS_PSS_SHA512) -> rsa_pss_pss_sha512; +signature_scheme(?RSA_PKCS1_SHA1) -> rsa_pkcs1_sha1; +signature_scheme(?ECDSA_SHA1) -> ecdsa_sha1; +%% Handling legacy signature algorithms for logging purposes. These algorithms +%% cannot be used in TLS 1.3 handshakes. +signature_scheme(SignAlgo) when is_integer(SignAlgo) -> + <<?BYTE(Hash),?BYTE(Sign)>> = <<?UINT16(SignAlgo)>>, + {ssl_cipher:hash_algorithm(Hash), ssl_cipher:sign_algorithm(Sign)}; +signature_scheme(_) -> unassigned. +%% TODO: reserved code points? + +scheme_to_components(rsa_pkcs1_sha256) -> {sha256, rsa_pkcs1, undefined}; +scheme_to_components(rsa_pkcs1_sha384) -> {sha384, rsa_pkcs1, undefined}; +scheme_to_components(rsa_pkcs1_sha512) -> {sha512, rsa_pkcs1, undefined}; +scheme_to_components(ecdsa_secp256r1_sha256) -> {sha256, ecdsa, secp256r1}; +scheme_to_components(ecdsa_secp384r1_sha384) -> {sha384, ecdsa, secp384r1}; +scheme_to_components(ecdsa_secp521r1_sha512) -> {sha512, ecdsa, secp521r1}; +scheme_to_components(rsa_pss_rsae_sha256) -> {sha256, rsa_pss_rsae, undefined}; +scheme_to_components(rsa_pss_rsae_sha384) -> {sha384, rsa_pss_rsae, undefined}; +scheme_to_components(rsa_pss_rsae_sha512) -> {sha512, rsa_pss_rsae, undefined}; +scheme_to_components(ed25519) -> {undefined, undefined, undefined}; +scheme_to_components(ed448) -> {undefined, undefined, undefined}; +scheme_to_components(rsa_pss_pss_sha256) -> {sha256, rsa_pss_pss, undefined}; +scheme_to_components(rsa_pss_pss_sha384) -> {sha384, rsa_pss_pss, undefined}; +scheme_to_components(rsa_pss_pss_sha512) -> {sha512, rsa_pss_pss, undefined}; +scheme_to_components(rsa_pkcs1_sha1) -> {sha1, rsa_pkcs1, undefined}; +scheme_to_components(ecdsa_sha1) -> {sha1, ecdsa, undefined}. + + +%% TODO: Add support for EC and RSA-SSA signatures +signature_algorithm_to_scheme(#'SignatureAlgorithm'{algorithm = ?sha1WithRSAEncryption}) -> + rsa_pkcs1_sha1; +signature_algorithm_to_scheme(#'SignatureAlgorithm'{algorithm = ?sha256WithRSAEncryption}) -> + rsa_pkcs1_sha256; +signature_algorithm_to_scheme(#'SignatureAlgorithm'{algorithm = ?sha384WithRSAEncryption}) -> + rsa_pkcs1_sha384; +signature_algorithm_to_scheme(#'SignatureAlgorithm'{algorithm = ?sha512WithRSAEncryption}) -> + rsa_pkcs1_sha512. + %% RFC 5246: 6.2.3.2. CBC Block Cipher %% @@ -861,7 +1012,7 @@ generic_block_cipher_from_bin({3, N}, T, IV, HashSize) next_iv = IV}; generic_block_cipher_from_bin({3, N}, T, IV, HashSize) - when N == 2; N == 3 -> + when N == 2; N == 3; N == 4 -> Sz1 = byte_size(T) - 1, <<_:Sz1/binary, ?BYTE(PadLength)>> = T, IVLength = byte_size(IV), @@ -1159,3 +1310,55 @@ filter_keyuse_suites(Use, KeyUse, CipherSuits, Suites) -> false -> CipherSuits -- Suites end. + +generate_server_share(Group) -> + Key = generate_key_exchange(Group), + #key_share_server_hello{ + server_share = #key_share_entry{ + group = Group, + key_exchange = Key + }}. + +generate_client_shares([]) -> + #key_share_client_hello{client_shares = []}; +generate_client_shares(Groups) -> + generate_client_shares(Groups, []). +%% +generate_client_shares([], Acc) -> + #key_share_client_hello{client_shares = lists:reverse(Acc)}; +generate_client_shares([Group|Groups], Acc) -> + Key = generate_key_exchange(Group), + KeyShareEntry = #key_share_entry{ + group = Group, + key_exchange = Key + }, + generate_client_shares(Groups, [KeyShareEntry|Acc]). + + +generate_key_exchange(secp256r1) -> + public_key:generate_key({namedCurve, secp256r1}); +generate_key_exchange(secp384r1) -> + public_key:generate_key({namedCurve, secp384r1}); +generate_key_exchange(secp521r1) -> + public_key:generate_key({namedCurve, secp521r1}); +generate_key_exchange(x25519) -> + crypto:generate_key(ecdh, x25519); +generate_key_exchange(x448) -> + crypto:generate_key(ecdh, x448); +generate_key_exchange(FFDHE) -> + public_key:generate_key(ssl_dh_groups:dh_params(FFDHE)). + + +%% TODO: Move this functionality to crypto! +%% 7.4.1. Finite Field Diffie-Hellman +%% +%% For finite field groups, a conventional Diffie-Hellman [DH76] +%% computation is performed. The negotiated key (Z) is converted to a +%% byte string by encoding in big-endian form and left-padded with zeros +%% up to the size of the prime. This byte string is used as the shared +%% secret in the key schedule as specified above. +add_zero_padding(Bin, PrimeSize) + when byte_size (Bin) =:= PrimeSize -> + Bin; +add_zero_padding(Bin, PrimeSize) -> + add_zero_padding(<<0, Bin/binary>>, PrimeSize). diff --git a/lib/ssl/src/ssl_cipher.hrl b/lib/ssl/src/ssl_cipher.hrl index 2371e8bd32..9c5e2f80a9 100644 --- a/lib/ssl/src/ssl_cipher.hrl +++ b/lib/ssl/src/ssl_cipher.hrl @@ -47,6 +47,7 @@ -record(cipher_state, { iv, key, + finished_key, state, nonce, tag_len @@ -600,15 +601,98 @@ %% TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 = {0xC0,0x32}; -define(TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, <<?BYTE(16#C0), ?BYTE(16#32)>>). -%%% Chacha20/Poly1305 Suites draft-agl-tls-chacha20poly1305-04 -%% TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = {0xcc, 0x13} --define(TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, <<?BYTE(16#CC), ?BYTE(16#13)>>). +%%% ChaCha20-Poly1305 Cipher Suites for Transport Layer Security (TLS) RFC7905 -%% TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = {0xcc, 0x14} --define(TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, <<?BYTE(16#CC), ?BYTE(16#14)>>). +%% TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = {0xCC, 0xA8} +-define(TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, <<?BYTE(16#CC), ?BYTE(16#A8)>>). -%% TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = {0xcc, 0x15} --define(TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, <<?BYTE(16#CC), ?BYTE(16#15)>>). +%% TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = {0xCC, 0xA9} +-define(TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, <<?BYTE(16#CC), ?BYTE(16#A9)>>). + +%% TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = {0xCC, 0xAA} +-define(TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, <<?BYTE(16#CC), ?BYTE(16#AA)>>). + +%% TLS_PSK_WITH_CHACHA20_POLY1305_SHA256 = {0xCC, 0xAB} +-define(TLS_PSK_WITH_CHACHA20_POLY1305_SHA256, <<?BYTE(16#CC), ?BYTE(16#AB)>>). + +%% TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 = {0xCC, 0xAC} +-define(TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256, <<?BYTE(16#CC), ?BYTE(16#AC)>>). + +%% TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256 = {0xCC, 0xAD} +-define(TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256, <<?BYTE(16#CC), ?BYTE(16#AD)>>). + +%% TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256 = {0xCC, 0xAE} +-define(TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256, <<?BYTE(16#CC), ?BYTE(16#AE)>>). + + + +%% RFC 6655 - TLS-1.2 cipher suites + +%% TLS_RSA_WITH_AES_128_CCM = {0xC0,0x9C} +-define(TLS_RSA_WITH_AES_128_CCM, <<?BYTE(16#C0), ?BYTE(16#9C)>>). + +%% TLS_RSA_WITH_AES_256_CCM = {0xC0,0x9D} +-define(TLS_RSA_WITH_AES_256_CCM, <<?BYTE(16#C0), ?BYTE(16#9D)>>). + +%% TLS_DHE_RSA_WITH_AES_256_CCM = {0xC0,0x9E} +-define(TLS_DHE_RSA_WITH_AES_256_CCM, <<?BYTE(16#C0), ?BYTE(16#9E)>>). + +%% TLS_DHE_RSA_WITH_AES_128_CCM = {0xC0,0x9F} +-define(TLS_DHE_RSA_WITH_AES_128_CCM, <<?BYTE(16#C0), ?BYTE(16#9F)>>). + +%% TLS_RSA_WITH_AES_256_CCM_8 = {0xC0,0x9A0} +-define(TLS_RSA_WITH_AES_256_CCM_8, <<?BYTE(16#C0), ?BYTE(16#A0)>>). + +%% TLS_RSA_WITH_AES_128_CCM_8 = {0xC0,0xA1} +-define(TLS_RSA_WITH_AES_128_CCM_8, <<?BYTE(16#C0), ?BYTE(16#A1)>>). + +%% TLS_DHE_RSA_WITH_AES_128_CCM_8 = {0xC0,0xA2} +-define(TLS_DHE_RSA_WITH_AES_128_CCM_8, <<?BYTE(16#C0), ?BYTE(16#A2)>>). + +%% TLS_DHE_RSA_WITH_AES_256_CCM_8 = {0xC0,0xA3} +-define(TLS_DHE_RSA_WITH_AES_256_CCM_8, <<?BYTE(16#C0), ?BYTE(16#A3)>>). + +%% TLS_PSK_WITH_AES_128_CCM = {0xC0,0xA4} +-define(TLS_PSK_WITH_AES_128_CCM, <<?BYTE(16#C0), ?BYTE(16#A4)>>). + +%% TLS_PSK_WITH_AES_256_CCM = {0xC0,0xA5) +-define(TLS_PSK_WITH_AES_256_CCM, <<?BYTE(16#C0), ?BYTE(16#A5)>>). + +%% TLS_DHE_PSK_WITH_AES_128_CCM = {0xC0,0xA6} +-define(TLS_DHE_PSK_WITH_AES_128_CCM, <<?BYTE(16#C0), ?BYTE(16#A6)>>). + +%% TLS_DHE_PSK_WITH_AES_256_CCM = {0xC0,0xA7} +-define(TLS_DHE_PSK_WITH_AES_256_CCM, <<?BYTE(16#C0), ?BYTE(16#A7)>>). + +%% TLS_PSK_WITH_AES_128_CCM_8 = {0xC0,0xA8} +-define(TLS_PSK_WITH_AES_128_CCM_8, <<?BYTE(16#C0), ?BYTE(16#A8)>>). + +%% TLS_PSK_WITH_AES_256_CCM_8 = {0xC0,0xA9) +-define(TLS_PSK_WITH_AES_256_CCM_8, <<?BYTE(16#C0), ?BYTE(16#A9)>>). + +%% TLS_PSK_DHE_WITH_AES_128_CCM_8 = {0xC0,0xAA} +-define(TLS_PSK_DHE_WITH_AES_128_CCM_8, <<?BYTE(16#C0), ?BYTE(16#AA)>>). + +%% TLS_PSK_DHE_WITH_AES_256_CCM_8 = << ?BYTE(0xC0,0xAB} +-define(TLS_PSK_DHE_WITH_AES_256_CCM_8, <<?BYTE(16#C0),?BYTE(16#AB)>>). + + +%%% TLS 1.3 cipher suites RFC8446 + +%% TLS_AES_128_GCM_SHA256 = {0x13,0x01} +-define(TLS_AES_128_GCM_SHA256, <<?BYTE(16#13), ?BYTE(16#01)>>). + +%% TLS_AES_256_GCM_SHA384 = {0x13,0x02} +-define(TLS_AES_256_GCM_SHA384, <<?BYTE(16#13),?BYTE(16#02)>>). + +%% TLS_CHACHA20_POLY1305_SHA256 = {0x13,0x03} +-define(TLS_CHACHA20_POLY1305_SHA256, <<?BYTE(16#13),?BYTE(16#03)>>). + +%% %% TLS_AES_128_CCM_SHA256 = {0x13,0x04} +%% -define(TLS_AES_128_CCM_SHA256, <<?BYTE(16#13), ?BYTE(16#04)>>). + +%% %% TLS_AES_128_CCM_8_SHA256 = {0x13,0x05} +%% -define(TLS_AES_128_CCM_8_SHA256, <<?BYTE(16#13),?BYTE(16#05)>>). -endif. % -ifdef(ssl_cipher). diff --git a/lib/ssl/src/ssl_cipher_format.erl b/lib/ssl/src/ssl_cipher_format.erl index 1d28e1e3b4..887eb6c653 100644 --- a/lib/ssl/src/ssl_cipher_format.erl +++ b/lib/ssl/src/ssl_cipher_format.erl @@ -48,42 +48,134 @@ -type openssl_cipher_suite() :: string(). --export([suite_to_str/1, suite_definition/1, suite/1, erl_suite_definition/1, - openssl_suite/1, openssl_suite_name/1]). +-export([suite_map_to_bin/1, %% Binary format + suite_bin_to_map/1, %% Erlang API format + suite_map_to_str/1, %% RFC string + suite_str_to_map/1, + suite_map_to_openssl_str/1, %% OpenSSL name + suite_openssl_str_to_map/1, + suite_legacy/1 %% Erlang legacy format + ]). %%-------------------------------------------------------------------- --spec suite_to_str(internal_erl_cipher_suite()) -> string(). +-spec suite_map_to_str(internal_erl_cipher_suite()) -> string(). %% %% Description: Return the string representation of a cipher suite. %%-------------------------------------------------------------------- -suite_to_str(#{key_exchange := null, +suite_map_to_str(#{key_exchange := null, cipher := null, mac := null, prf := null}) -> "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"; -suite_to_str(#{key_exchange := Kex, +suite_map_to_str(#{key_exchange := any, + cipher := Cipher, + mac := aead, + prf := PRF}) -> + "TLS_" ++ string:to_upper(atom_to_list(Cipher)) ++ + "_" ++ string:to_upper(atom_to_list(PRF)); +suite_map_to_str(#{key_exchange := Kex, cipher := Cipher, mac := aead, prf := PRF}) -> "TLS_" ++ string:to_upper(atom_to_list(Kex)) ++ "_WITH_" ++ string:to_upper(atom_to_list(Cipher)) ++ "_" ++ string:to_upper(atom_to_list(PRF)); -suite_to_str(#{key_exchange := Kex, +suite_map_to_str(#{key_exchange := Kex, cipher := Cipher, mac := Mac}) -> "TLS_" ++ string:to_upper(atom_to_list(Kex)) ++ "_WITH_" ++ string:to_upper(atom_to_list(Cipher)) ++ "_" ++ string:to_upper(atom_to_list(Mac)). +suite_str_to_map("TLS_EMPTY_RENEGOTIATION_INFO_SCSV") -> + #{key_exchange => null, + cipher => null, + mac => null, + prf => null}; +suite_str_to_map(SuiteStr)-> + Str0 = string:trim(SuiteStr, leading, "TLS_"), + case string:split(Str0, "_WITH_") of + [Rest] -> + tls_1_3_suite_str_to_map(Rest); + [Kex| Rest] -> + pre_tls_1_3_suite_str_to_map(Kex, Rest) + end. + +suite_map_to_openssl_str(#{key_exchange := any, + mac := aead} = Suite) -> + %% TLS 1.3 OpenSSL finally use RFC names + suite_map_to_str(Suite); +suite_map_to_openssl_str(#{key_exchange := null} = Suite) -> + %% TLS_EMPTY_RENEGOTIATION_INFO_SCSV + suite_map_to_str(Suite); +suite_map_to_openssl_str(#{key_exchange := Kex, + cipher := chacha20_poly1305 = Cipher, + mac := aead}) -> + openssl_suite_start(string:to_upper(atom_to_list(Kex))) + ++ openssl_cipher_name(Kex, string:to_upper(atom_to_list(Cipher))); +suite_map_to_openssl_str(#{key_exchange := Kex, + cipher := Cipher, + mac := aead, + prf := PRF}) -> + openssl_suite_start(string:to_upper(atom_to_list(Kex))) + ++ openssl_cipher_name(Kex, string:to_upper(atom_to_list(Cipher))) ++ + "-" ++ string:to_upper(atom_to_list(PRF)); +suite_map_to_openssl_str(#{key_exchange := Kex, + cipher := Cipher, + mac := Mac}) -> + openssl_suite_start(string:to_upper(atom_to_list(Kex))) + ++ openssl_cipher_name(Kex, string:to_upper(atom_to_list(Cipher))) ++ + "-" ++ string:to_upper(atom_to_list(Mac)). + + +suite_openssl_str_to_map("TLS_" ++ _ = SuiteStr) -> + suite_str_to_map(SuiteStr); +suite_openssl_str_to_map("DHE-RSA-" ++ Rest) -> + suite_openssl_str_to_map("DHE-RSA", Rest); +suite_openssl_str_to_map("DHE-DSS-" ++ Rest) -> + suite_openssl_str_to_map("DHE-DSS", Rest); +suite_openssl_str_to_map("EDH-RSA-" ++ Rest) -> + suite_openssl_str_to_map("DHE-RSA", Rest); +suite_openssl_str_to_map("EDH-DSS-" ++ Rest) -> + suite_openssl_str_to_map("DHE-DSS", Rest); +suite_openssl_str_to_map("DES" ++ _ = Rest) -> + suite_openssl_str_to_map("RSA", Rest); +suite_openssl_str_to_map("AES" ++ _ = Rest) -> + suite_openssl_str_to_map("RSA", Rest); +suite_openssl_str_to_map("RC4" ++ _ = Rest) -> + suite_openssl_str_to_map("RSA", Rest); +suite_openssl_str_to_map("ECDH-RSA-" ++ Rest) -> + suite_openssl_str_to_map("ECDH-RSA", Rest); +suite_openssl_str_to_map("ECDH-ECDSA-" ++ Rest) -> + suite_openssl_str_to_map("ECDH-ECDSA", Rest); +suite_openssl_str_to_map("ECDHE-RSA-" ++ Rest) -> + suite_openssl_str_to_map("ECDHE-RSA", Rest); +suite_openssl_str_to_map("ECDHE-ECDSA-" ++ Rest) -> + suite_openssl_str_to_map("ECDHE-ECDSA", Rest); +suite_openssl_str_to_map("RSA-PSK-" ++ Rest) -> + suite_openssl_str_to_map("RSA-PSK", Rest); +suite_openssl_str_to_map("RSA-" ++ Rest) -> + suite_openssl_str_to_map("RSA", Rest); +suite_openssl_str_to_map("DHE-PSK-" ++ Rest) -> + suite_openssl_str_to_map("DHE-PSK", Rest); +suite_openssl_str_to_map("ECDHE-PSK-" ++ Rest) -> + suite_openssl_str_to_map("ECDHE-PSK", Rest); +suite_openssl_str_to_map("PSK-" ++ Rest) -> + suite_openssl_str_to_map("PSK", Rest); +suite_openssl_str_to_map("SRP-RSA-" ++ Rest) -> + suite_openssl_str_to_map("SRP-RSA", Rest); +suite_openssl_str_to_map("SRP-" ++ Rest) -> + suite_openssl_str_to_map("SRP", Rest). + %%-------------------------------------------------------------------- --spec suite_definition(cipher_suite()) -> internal_erl_cipher_suite(). +-spec suite_bin_to_map(cipher_suite()) -> internal_erl_cipher_suite(). %% %% Description: Return erlang cipher suite definition. %% Note: Currently not supported suites are commented away. %% They should be supported or removed in the future. %%------------------------------------------------------------------- %% TLS v1.1 suites -suite_definition(?TLS_NULL_WITH_NULL_NULL) -> +suite_bin_to_map(?TLS_NULL_WITH_NULL_NULL) -> #{key_exchange => null, cipher => null, mac => null, @@ -91,111 +183,111 @@ suite_definition(?TLS_NULL_WITH_NULL_NULL) -> %% RFC 5746 - Not a real cipher suite used to signal empty "renegotiation_info" extension %% to avoid handshake failure from old servers that do not ignore %% hello extension data as they should. -suite_definition(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV) -> +suite_bin_to_map(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV) -> #{key_exchange => null, cipher => null, mac => null, prf => null}; -suite_definition(?TLS_RSA_WITH_RC4_128_MD5) -> +suite_bin_to_map(?TLS_RSA_WITH_RC4_128_MD5) -> #{key_exchange => rsa, cipher => rc4_128, mac => md5, prf => default_prf}; -suite_definition(?TLS_RSA_WITH_RC4_128_SHA) -> +suite_bin_to_map(?TLS_RSA_WITH_RC4_128_SHA) -> #{key_exchange => rsa, cipher => rc4_128, mac => sha, prf => default_prf}; -suite_definition(?TLS_RSA_WITH_DES_CBC_SHA) -> +suite_bin_to_map(?TLS_RSA_WITH_DES_CBC_SHA) -> #{key_exchange => rsa, cipher => des_cbc, mac => sha, prf => default_prf}; -suite_definition(?TLS_RSA_WITH_3DES_EDE_CBC_SHA) -> +suite_bin_to_map(?TLS_RSA_WITH_3DES_EDE_CBC_SHA) -> #{key_exchange => rsa, cipher => '3des_ede_cbc', mac => sha, prf => default_prf}; -suite_definition(?TLS_DHE_DSS_WITH_DES_CBC_SHA) -> +suite_bin_to_map(?TLS_DHE_DSS_WITH_DES_CBC_SHA) -> #{key_exchange => dhe_dss, cipher => des_cbc, mac => sha, prf => default_prf}; -suite_definition(?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA) -> +suite_bin_to_map(?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA) -> #{key_exchange => dhe_dss, cipher => '3des_ede_cbc', mac => sha, prf => default_prf}; -suite_definition(?TLS_DHE_RSA_WITH_DES_CBC_SHA) -> +suite_bin_to_map(?TLS_DHE_RSA_WITH_DES_CBC_SHA) -> #{key_exchange => dhe_rsa, cipher => des_cbc, mac => sha, prf => default_prf}; -suite_definition(?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA) -> +suite_bin_to_map(?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA) -> #{key_exchange => dhe_rsa, cipher => '3des_ede_cbc', mac => sha, prf => default_prf}; %%% TSL V1.1 AES suites -suite_definition(?TLS_RSA_WITH_AES_128_CBC_SHA) -> +suite_bin_to_map(?TLS_RSA_WITH_AES_128_CBC_SHA) -> #{key_exchange => rsa, cipher => aes_128_cbc, mac => sha, prf => default_prf}; -suite_definition(?TLS_DHE_DSS_WITH_AES_128_CBC_SHA) -> +suite_bin_to_map(?TLS_DHE_DSS_WITH_AES_128_CBC_SHA) -> #{key_exchange => dhe_dss, cipher => aes_128_cbc, mac => sha, prf => default_prf}; -suite_definition(?TLS_DHE_RSA_WITH_AES_128_CBC_SHA) -> +suite_bin_to_map(?TLS_DHE_RSA_WITH_AES_128_CBC_SHA) -> #{key_exchange => dhe_rsa, cipher => aes_128_cbc, mac => sha, prf => default_prf}; -suite_definition(?TLS_RSA_WITH_AES_256_CBC_SHA) -> +suite_bin_to_map(?TLS_RSA_WITH_AES_256_CBC_SHA) -> #{key_exchange => rsa, cipher => aes_256_cbc, mac => sha, prf => default_prf}; -suite_definition(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA) -> +suite_bin_to_map(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA) -> #{key_exchange => dhe_dss, cipher => aes_256_cbc, mac => sha, prf => default_prf}; -suite_definition(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA) -> +suite_bin_to_map(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA) -> #{key_exchange => dhe_rsa, cipher => aes_256_cbc, mac => sha, prf => default_prf}; %% TLS v1.2 suites -%% suite_definition(?TLS_RSA_WITH_NULL_SHA) -> +%% suite_bin_to_map(?TLS_RSA_WITH_NULL_SHA) -> %% {rsa, null, sha, default_prf}; -suite_definition(?TLS_RSA_WITH_AES_128_CBC_SHA256) -> +suite_bin_to_map(?TLS_RSA_WITH_AES_128_CBC_SHA256) -> #{key_exchange => rsa, cipher => aes_128_cbc, mac => sha256, prf => default_prf}; -suite_definition(?TLS_RSA_WITH_AES_256_CBC_SHA256) -> +suite_bin_to_map(?TLS_RSA_WITH_AES_256_CBC_SHA256) -> #{key_exchange => rsa, cipher => aes_256_cbc, mac => sha256, prf => default_prf}; -suite_definition(?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256) -> +suite_bin_to_map(?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256) -> #{key_exchange => dhe_dss, cipher => aes_128_cbc, mac => sha256, prf => default_prf}; -suite_definition(?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256) -> +suite_bin_to_map(?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256) -> #{key_exchange => dhe_rsa, cipher => aes_128_cbc, mac => sha256, prf => default_prf}; -suite_definition(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256) -> +suite_bin_to_map(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256) -> #{key_exchange => dhe_dss, cipher => aes_256_cbc, mac => sha256, prf => default_prf}; -suite_definition(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256) -> +suite_bin_to_map(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256) -> #{key_exchange => dhe_rsa, cipher => aes_256_cbc, mac => sha256, @@ -207,611 +299,683 @@ suite_definition(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256) -> %% TLS_DH_RSA_WITH_AES_256_CBC_SHA256 DH_RSA AES_256_CBC SHA256 %%% DH-ANON deprecated by TLS spec and not available %%% by default, but good for testing purposes. -suite_definition(?TLS_DH_anon_WITH_RC4_128_MD5) -> +suite_bin_to_map(?TLS_DH_anon_WITH_RC4_128_MD5) -> #{key_exchange => dh_anon, cipher => rc4_128, mac => md5, prf => default_prf}; -suite_definition(?TLS_DH_anon_WITH_DES_CBC_SHA) -> +suite_bin_to_map(?TLS_DH_anon_WITH_DES_CBC_SHA) -> #{key_exchange => dh_anon, cipher => des_cbc, mac => sha, prf => default_prf}; -suite_definition(?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA) -> +suite_bin_to_map(?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA) -> #{key_exchange => dh_anon, cipher => '3des_ede_cbc', mac => sha, prf => default_prf}; -suite_definition(?TLS_DH_anon_WITH_AES_128_CBC_SHA) -> +suite_bin_to_map(?TLS_DH_anon_WITH_AES_128_CBC_SHA) -> #{key_exchange => dh_anon, cipher => aes_128_cbc, mac => sha, prf => default_prf}; -suite_definition(?TLS_DH_anon_WITH_AES_256_CBC_SHA) -> +suite_bin_to_map(?TLS_DH_anon_WITH_AES_256_CBC_SHA) -> #{key_exchange => dh_anon, cipher => aes_256_cbc, mac => sha, prf => default_prf}; -suite_definition(?TLS_DH_anon_WITH_AES_128_CBC_SHA256) -> +suite_bin_to_map(?TLS_DH_anon_WITH_AES_128_CBC_SHA256) -> #{key_exchange => dh_anon, cipher => aes_128_cbc, mac => sha256, prf => default_prf}; -suite_definition(?TLS_DH_anon_WITH_AES_256_CBC_SHA256) -> +suite_bin_to_map(?TLS_DH_anon_WITH_AES_256_CBC_SHA256) -> #{key_exchange => dh_anon, cipher => aes_256_cbc, mac => sha256, prf => default_prf}; %%% PSK Cipher Suites RFC 4279 -suite_definition(?TLS_PSK_WITH_RC4_128_SHA) -> +suite_bin_to_map(?TLS_PSK_WITH_RC4_128_SHA) -> #{key_exchange => psk, cipher => rc4_128, mac => sha, prf => default_prf}; -suite_definition(?TLS_PSK_WITH_3DES_EDE_CBC_SHA) -> +suite_bin_to_map(?TLS_PSK_WITH_3DES_EDE_CBC_SHA) -> #{key_exchange => psk, cipher => '3des_ede_cbc', mac => sha, prf => default_prf}; -suite_definition(?TLS_PSK_WITH_AES_128_CBC_SHA) -> +suite_bin_to_map(?TLS_PSK_WITH_AES_128_CBC_SHA) -> #{key_exchange => psk, cipher => aes_128_cbc, mac => sha, prf => default_prf}; -suite_definition(?TLS_PSK_WITH_AES_256_CBC_SHA) -> +suite_bin_to_map(?TLS_PSK_WITH_AES_256_CBC_SHA) -> #{key_exchange => psk, cipher => aes_256_cbc, mac => sha, prf => default_prf}; -suite_definition(?TLS_DHE_PSK_WITH_RC4_128_SHA) -> +suite_bin_to_map(?TLS_DHE_PSK_WITH_RC4_128_SHA) -> #{key_exchange => dhe_psk, cipher => rc4_128, mac => sha, prf => default_prf}; -suite_definition(?TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA) -> +suite_bin_to_map(?TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA) -> #{key_exchange => dhe_psk, cipher => '3des_ede_cbc', mac => sha, prf => default_prf}; -suite_definition(?TLS_DHE_PSK_WITH_AES_128_CBC_SHA) -> +suite_bin_to_map(?TLS_DHE_PSK_WITH_AES_128_CBC_SHA) -> #{key_exchange => dhe_psk, cipher => aes_128_cbc, mac => sha, prf => default_prf}; -suite_definition(?TLS_DHE_PSK_WITH_AES_256_CBC_SHA) -> +suite_bin_to_map(?TLS_DHE_PSK_WITH_AES_256_CBC_SHA) -> #{key_exchange => dhe_psk, cipher => aes_256_cbc, mac => sha, prf => default_prf}; -suite_definition(?TLS_RSA_PSK_WITH_RC4_128_SHA) -> +suite_bin_to_map(?TLS_RSA_PSK_WITH_RC4_128_SHA) -> #{key_exchange => rsa_psk, cipher => rc4_128, mac => sha, prf => default_prf}; -suite_definition(?TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA) -> +suite_bin_to_map(?TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA) -> #{key_exchange => rsa_psk, cipher => '3des_ede_cbc', mac => sha, prf => default_prf}; -suite_definition(?TLS_RSA_PSK_WITH_AES_128_CBC_SHA) -> +suite_bin_to_map(?TLS_RSA_PSK_WITH_AES_128_CBC_SHA) -> #{key_exchange => rsa_psk, cipher => aes_128_cbc, mac => sha, prf => default_prf}; -suite_definition(?TLS_RSA_PSK_WITH_AES_256_CBC_SHA) -> +suite_bin_to_map(?TLS_RSA_PSK_WITH_AES_256_CBC_SHA) -> #{key_exchange => rsa_psk, cipher => aes_256_cbc, mac => sha, prf => default_prf}; %%% PSK NULL Cipher Suites RFC 4785 -suite_definition(?TLS_PSK_WITH_NULL_SHA) -> +suite_bin_to_map(?TLS_PSK_WITH_NULL_SHA) -> #{key_exchange => psk, cipher => null, mac => sha, prf => default_prf}; -suite_definition(?TLS_DHE_PSK_WITH_NULL_SHA) -> +suite_bin_to_map(?TLS_DHE_PSK_WITH_NULL_SHA) -> #{key_exchange => dhe_psk, cipher => null, mac => sha, prf => default_prf}; -suite_definition(?TLS_RSA_PSK_WITH_NULL_SHA) -> +suite_bin_to_map(?TLS_RSA_PSK_WITH_NULL_SHA) -> #{key_exchange => rsa_psk, cipher => null, mac => sha, prf => default_prf}; %%% TLS 1.2 PSK Cipher Suites RFC 5487 -suite_definition(?TLS_PSK_WITH_AES_128_GCM_SHA256) -> +suite_bin_to_map(?TLS_PSK_WITH_AES_128_GCM_SHA256) -> #{key_exchange => psk, cipher => aes_128_gcm, mac => aead, prf => sha256}; -suite_definition(?TLS_PSK_WITH_AES_256_GCM_SHA384) -> +suite_bin_to_map(?TLS_PSK_WITH_AES_256_GCM_SHA384) -> #{key_exchange => psk, cipher => aes_256_gcm, mac => aead, prf => sha384}; -suite_definition(?TLS_DHE_PSK_WITH_AES_128_GCM_SHA256) -> +suite_bin_to_map(?TLS_DHE_PSK_WITH_AES_128_GCM_SHA256) -> #{key_exchange => dhe_psk, cipher => aes_128_gcm, mac => aead, prf => sha256}; -suite_definition(?TLS_DHE_PSK_WITH_AES_256_GCM_SHA384) -> +suite_bin_to_map(?TLS_DHE_PSK_WITH_AES_256_GCM_SHA384) -> #{key_exchange => dhe_psk, cipher => aes_256_gcm, mac => aead, prf => sha384}; -suite_definition(?TLS_RSA_PSK_WITH_AES_128_GCM_SHA256) -> +suite_bin_to_map(?TLS_RSA_PSK_WITH_AES_128_GCM_SHA256) -> #{key_exchange => rsa_psk, cipher => aes_128_gcm, mac => aead, prf => sha256}; -suite_definition(?TLS_RSA_PSK_WITH_AES_256_GCM_SHA384) -> +suite_bin_to_map(?TLS_RSA_PSK_WITH_AES_256_GCM_SHA384) -> #{key_exchange => rsa_psk, cipher => aes_256_gcm, mac => aead, prf => sha384}; -suite_definition(?TLS_PSK_WITH_AES_128_CBC_SHA256) -> +suite_bin_to_map(?TLS_PSK_WITH_AES_128_CBC_SHA256) -> #{key_exchange => psk, cipher => aes_128_cbc, mac => sha256, prf => default_prf}; -suite_definition(?TLS_PSK_WITH_AES_256_CBC_SHA384) -> +suite_bin_to_map(?TLS_PSK_WITH_AES_256_CBC_SHA384) -> #{key_exchange => psk, cipher => aes_256_cbc, mac => sha384, prf => default_prf}; -suite_definition(?TLS_DHE_PSK_WITH_AES_128_CBC_SHA256) -> +suite_bin_to_map(?TLS_DHE_PSK_WITH_AES_128_CBC_SHA256) -> #{key_exchange => dhe_psk, cipher => aes_128_cbc, mac => sha256, prf => default_prf}; -suite_definition(?TLS_DHE_PSK_WITH_AES_256_CBC_SHA384) -> +suite_bin_to_map(?TLS_DHE_PSK_WITH_AES_256_CBC_SHA384) -> #{key_exchange => dhe_psk, cipher => aes_256_cbc, mac => sha384, prf => default_prf}; -suite_definition(?TLS_RSA_PSK_WITH_AES_128_CBC_SHA256) -> +suite_bin_to_map(?TLS_RSA_PSK_WITH_AES_128_CBC_SHA256) -> #{key_exchange => rsa_psk, cipher => aes_128_cbc, mac => sha256, prf => default_prf}; -suite_definition(?TLS_RSA_PSK_WITH_AES_256_CBC_SHA384) -> +suite_bin_to_map(?TLS_RSA_PSK_WITH_AES_256_CBC_SHA384) -> #{key_exchange => rsa_psk, cipher => aes_256_cbc, mac => sha384, prf => default_prf}; -suite_definition(?TLS_PSK_WITH_NULL_SHA256) -> +suite_bin_to_map(?TLS_PSK_WITH_NULL_SHA256) -> #{key_exchange => psk, cipher => null, mac => sha256, prf => default_prf}; -suite_definition(?TLS_PSK_WITH_NULL_SHA384) -> +suite_bin_to_map(?TLS_PSK_WITH_NULL_SHA384) -> #{key_exchange => psk, cipher => null, mac => sha384, prf => default_prf}; -suite_definition(?TLS_DHE_PSK_WITH_NULL_SHA256) -> +suite_bin_to_map(?TLS_DHE_PSK_WITH_NULL_SHA256) -> #{key_exchange => dhe_psk, cipher => null, mac => sha256, prf => default_prf}; -suite_definition(?TLS_DHE_PSK_WITH_NULL_SHA384) -> +suite_bin_to_map(?TLS_DHE_PSK_WITH_NULL_SHA384) -> #{key_exchange => dhe_psk, cipher => null, mac => sha384, prf => default_prf}; -suite_definition(?TLS_RSA_PSK_WITH_NULL_SHA256) -> +suite_bin_to_map(?TLS_RSA_PSK_WITH_NULL_SHA256) -> #{key_exchange => rsa_psk, cipher => null, mac => sha256, prf => default_prf}; -suite_definition(?TLS_RSA_PSK_WITH_NULL_SHA384) -> +suite_bin_to_map(?TLS_RSA_PSK_WITH_NULL_SHA384) -> #{key_exchange => rsa_psk, cipher => null, mac => sha384, prf => default_prf}; %%% ECDHE PSK Cipher Suites RFC 5489 -suite_definition(?TLS_ECDHE_PSK_WITH_RC4_128_SHA) -> +suite_bin_to_map(?TLS_ECDHE_PSK_WITH_RC4_128_SHA) -> #{key_exchange => ecdhe_psk, cipher => rc4_128, mac => sha, prf => default_prf}; -suite_definition(?TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA) -> +suite_bin_to_map(?TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA) -> #{key_exchange => ecdhe_psk, cipher => '3des_ede_cbc', mac => sha, prf => default_prf}; -suite_definition(?TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA) -> +suite_bin_to_map(?TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA) -> #{key_exchange => ecdhe_psk, cipher => aes_128_cbc, mac => sha, prf => default_prf}; -suite_definition(?TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA) -> +suite_bin_to_map(?TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA) -> #{key_exchange => ecdhe_psk, cipher => aes_256_cbc, mac => sha, prf => default_prf}; -suite_definition(?TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256) -> +suite_bin_to_map(?TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256) -> #{key_exchange => ecdhe_psk, cipher => aes_128_cbc, mac => sha256, prf => default_prf}; -suite_definition(?TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384) -> +suite_bin_to_map(?TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384) -> #{key_exchange => ecdhe_psk, cipher => aes_256_cbc, mac => sha384, prf => default_prf}; -suite_definition(?TLS_ECDHE_PSK_WITH_NULL_SHA256) -> +suite_bin_to_map(?TLS_ECDHE_PSK_WITH_NULL_SHA256) -> #{key_exchange => ecdhe_psk, cipher => null, mac => sha256, prf => default_prf}; -suite_definition(?TLS_ECDHE_PSK_WITH_NULL_SHA384) -> +suite_bin_to_map(?TLS_ECDHE_PSK_WITH_NULL_SHA384) -> #{key_exchange => ecdhe_psk, cipher => null, mac => sha384, prf => default_prf}; %%% ECDHE_PSK with AES-GCM and AES-CCM Cipher Suites, draft-ietf-tls-ecdhe-psk-aead-05 -suite_definition(?TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256) -> +suite_bin_to_map(?TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256) -> #{key_exchange => ecdhe_psk, cipher => aes_128_gcm, mac => null, prf => sha256}; -suite_definition(?TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384) -> +suite_bin_to_map(?TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384) -> #{key_exchange => ecdhe_psk, cipher => aes_256_gcm, mac => null, prf => sha384}; -%% suite_definition(?TLS_ECDHE_PSK_WITH_AES_128_CCM_8_SHA256) -> -%% #{key_exchange => ecdhe_psk, -%% cipher => aes_128_ccm, -%% mac => null, -%% prf =>sha256}; -%% suite_definition(?TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256) -> -%% #{key_exchange => ecdhe_psk, -%% cipher => aes_256_ccm, -%% mac => null, -%% prf => sha256}; +suite_bin_to_map(?TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256) -> + #{key_exchange => ecdhe_psk, + cipher => aes_128_ccm, + mac => null, + prf =>sha256}; +suite_bin_to_map(?TLS_ECDHE_PSK_WITH_AES_128_CCM_8_SHA256) -> + #{key_exchange => ecdhe_psk, + cipher => aes_128_ccm_8, + mac => null, + prf =>sha256}; %%% SRP Cipher Suites RFC 5054 -suite_definition(?TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA) -> +suite_bin_to_map(?TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA) -> #{key_exchange => srp_anon, cipher => '3des_ede_cbc', mac => sha, prf => default_prf}; -suite_definition(?TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA) -> +suite_bin_to_map(?TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA) -> #{key_exchange => srp_rsa, cipher => '3des_ede_cbc', mac => sha, prf => default_prf}; -suite_definition(?TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA) -> +suite_bin_to_map(?TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA) -> #{key_exchange => srp_dss, cipher => '3des_ede_cbc', mac => sha, prf => default_prf}; -suite_definition(?TLS_SRP_SHA_WITH_AES_128_CBC_SHA) -> +suite_bin_to_map(?TLS_SRP_SHA_WITH_AES_128_CBC_SHA) -> #{key_exchange => srp_anon, cipher => aes_128_cbc, mac => sha, prf => default_prf}; -suite_definition(?TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA) -> +suite_bin_to_map(?TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA) -> #{key_exchange => srp_rsa, cipher => aes_128_cbc, mac => sha, prf => default_prf}; -suite_definition(?TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA) -> +suite_bin_to_map(?TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA) -> #{key_exchange => srp_dss, cipher => aes_128_cbc, mac => sha, prf => default_prf}; -suite_definition(?TLS_SRP_SHA_WITH_AES_256_CBC_SHA) -> +suite_bin_to_map(?TLS_SRP_SHA_WITH_AES_256_CBC_SHA) -> #{key_exchange => srp_anon, cipher => aes_256_cbc, mac => sha, prf => default_prf}; -suite_definition(?TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA) -> +suite_bin_to_map(?TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA) -> #{key_exchange => srp_rsa, cipher => aes_256_cbc, mac => sha, prf => default_prf}; -suite_definition(?TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA) -> +suite_bin_to_map(?TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA) -> #{key_exchange => srp_dss, cipher => aes_256_cbc, mac => sha, prf => default_prf}; %% RFC 4492 EC TLS suites -suite_definition(?TLS_ECDH_ECDSA_WITH_NULL_SHA) -> +suite_bin_to_map(?TLS_ECDH_ECDSA_WITH_NULL_SHA) -> #{key_exchange => ecdh_ecdsa, cipher => null, mac => sha, prf => default_prf}; -suite_definition(?TLS_ECDH_ECDSA_WITH_RC4_128_SHA) -> +suite_bin_to_map(?TLS_ECDH_ECDSA_WITH_RC4_128_SHA) -> #{key_exchange => ecdh_ecdsa, cipher => rc4_128, mac => sha, prf => default_prf}; -suite_definition(?TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA) -> +suite_bin_to_map(?TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA) -> #{key_exchange => ecdh_ecdsa, cipher => '3des_ede_cbc', mac => sha, prf => default_prf}; -suite_definition(?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA) -> +suite_bin_to_map(?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA) -> #{key_exchange => ecdh_ecdsa, cipher => aes_128_cbc, mac => sha, prf => default_prf}; -suite_definition(?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA) -> +suite_bin_to_map(?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA) -> #{key_exchange => ecdh_ecdsa, cipher => aes_256_cbc, mac => sha, prf => default_prf}; -suite_definition(?TLS_ECDHE_ECDSA_WITH_NULL_SHA) -> +suite_bin_to_map(?TLS_ECDHE_ECDSA_WITH_NULL_SHA) -> #{key_exchange => ecdhe_ecdsa, cipher => null, mac => sha, prf => default_prf}; -suite_definition(?TLS_ECDHE_ECDSA_WITH_RC4_128_SHA) -> +suite_bin_to_map(?TLS_ECDHE_ECDSA_WITH_RC4_128_SHA) -> #{key_exchange => ecdhe_ecdsa, cipher => rc4_128, mac => sha, prf => default_prf}; -suite_definition(?TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA) -> +suite_bin_to_map(?TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA) -> #{key_exchange => ecdhe_ecdsa, cipher => '3des_ede_cbc', mac => sha, prf => default_prf}; -suite_definition(?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA) -> +suite_bin_to_map(?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA) -> #{key_exchange => ecdhe_ecdsa, cipher => aes_128_cbc, mac => sha, prf => default_prf}; -suite_definition(?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA) -> +suite_bin_to_map(?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA) -> #{key_exchange => ecdhe_ecdsa, cipher => aes_256_cbc, mac => sha, prf => default_prf}; -suite_definition(?TLS_ECDH_RSA_WITH_NULL_SHA) -> +suite_bin_to_map(?TLS_ECDH_RSA_WITH_NULL_SHA) -> #{key_exchange => ecdh_rsa, cipher => null, mac => sha, prf => default_prf}; -suite_definition(?TLS_ECDH_RSA_WITH_RC4_128_SHA) -> +suite_bin_to_map(?TLS_ECDH_RSA_WITH_RC4_128_SHA) -> #{key_exchange => ecdh_rsa, cipher => rc4_128, mac => sha, prf => default_prf}; -suite_definition(?TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA) -> +suite_bin_to_map(?TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA) -> #{key_exchange => ecdh_rsa, cipher => '3des_ede_cbc', mac => sha, prf => default_prf}; -suite_definition(?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA) -> +suite_bin_to_map(?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA) -> #{key_exchange => ecdh_rsa, cipher => aes_128_cbc, mac => sha, prf => default_prf}; -suite_definition(?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA) -> +suite_bin_to_map(?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA) -> #{key_exchange => ecdh_rsa, cipher => aes_256_cbc, mac => sha, prf => default_prf}; -suite_definition(?TLS_ECDHE_RSA_WITH_NULL_SHA) -> +suite_bin_to_map(?TLS_ECDHE_RSA_WITH_NULL_SHA) -> #{key_exchange => ecdhe_rsa, cipher => null, mac => sha, prf => default_prf}; -suite_definition(?TLS_ECDHE_RSA_WITH_RC4_128_SHA) -> +suite_bin_to_map(?TLS_ECDHE_RSA_WITH_RC4_128_SHA) -> #{key_exchange => ecdhe_rsa, cipher => rc4_128, mac => sha, prf => default_prf}; -suite_definition(?TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA) -> +suite_bin_to_map(?TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA) -> #{key_exchange => ecdhe_rsa, cipher => '3des_ede_cbc', mac => sha, prf => default_prf}; -suite_definition(?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA) -> +suite_bin_to_map(?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA) -> #{key_exchange => ecdhe_rsa, cipher => aes_128_cbc, mac => sha, prf => default_prf}; -suite_definition(?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA) -> +suite_bin_to_map(?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA) -> #{key_exchange => ecdhe_rsa, cipher => aes_256_cbc, mac => sha, prf => default_prf}; -suite_definition(?TLS_ECDH_anon_WITH_NULL_SHA) -> +suite_bin_to_map(?TLS_ECDH_anon_WITH_NULL_SHA) -> #{key_exchange => ecdh_anon, cipher => null, mac => sha, prf => default_prf}; -suite_definition(?TLS_ECDH_anon_WITH_RC4_128_SHA) -> +suite_bin_to_map(?TLS_ECDH_anon_WITH_RC4_128_SHA) -> #{key_exchange => ecdh_anon, cipher => rc4_128, mac => sha, prf => default_prf}; -suite_definition(?TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA) -> +suite_bin_to_map(?TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA) -> #{key_exchange => ecdh_anon, cipher => '3des_ede_cbc', mac => sha, prf => default_prf}; -suite_definition(?TLS_ECDH_anon_WITH_AES_128_CBC_SHA) -> +suite_bin_to_map(?TLS_ECDH_anon_WITH_AES_128_CBC_SHA) -> #{key_exchange => ecdh_anon, cipher => aes_128_cbc, mac => sha, prf => default_prf}; -suite_definition(?TLS_ECDH_anon_WITH_AES_256_CBC_SHA) -> +suite_bin_to_map(?TLS_ECDH_anon_WITH_AES_256_CBC_SHA) -> #{key_exchange => ecdh_anon, cipher => aes_256_cbc, mac => sha, prf => default_prf}; %% RFC 5289 EC TLS suites -suite_definition(?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256) -> +suite_bin_to_map(?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256) -> #{key_exchange => ecdhe_ecdsa, cipher => aes_128_cbc, mac => sha256, prf => sha256}; -suite_definition(?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384) -> +suite_bin_to_map(?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384) -> #{key_exchange => ecdhe_ecdsa, cipher => aes_256_cbc, mac => sha384, prf => sha384}; -suite_definition(?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256) -> +suite_bin_to_map(?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256) -> #{key_exchange => ecdh_ecdsa, cipher => aes_128_cbc, mac => sha256, prf => sha256}; -suite_definition(?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384) -> +suite_bin_to_map(?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384) -> #{key_exchange => ecdh_ecdsa, cipher => aes_256_cbc, mac => sha384, prf => sha384}; -suite_definition(?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256) -> +suite_bin_to_map(?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256) -> #{key_exchange => ecdhe_rsa, cipher => aes_128_cbc, mac => sha256, prf => sha256}; -suite_definition(?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384) -> +suite_bin_to_map(?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384) -> #{key_exchange => ecdhe_rsa, cipher => aes_256_cbc, mac => sha384, prf => sha384}; -suite_definition(?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256) -> +suite_bin_to_map(?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256) -> #{key_exchange => ecdh_rsa, cipher => aes_128_cbc, mac => sha256, prf => sha256}; -suite_definition(?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384) -> +suite_bin_to_map(?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384) -> #{key_exchange => ecdh_rsa, cipher => aes_256_cbc, mac => sha384, prf => sha384}; %% RFC 5288 AES-GCM Cipher Suites -suite_definition(?TLS_RSA_WITH_AES_128_GCM_SHA256) -> +suite_bin_to_map(?TLS_RSA_WITH_AES_128_GCM_SHA256) -> #{key_exchange => rsa, cipher => aes_128_gcm, mac => aead, prf => sha256}; -suite_definition(?TLS_RSA_WITH_AES_256_GCM_SHA384) -> +suite_bin_to_map(?TLS_RSA_WITH_AES_256_GCM_SHA384) -> #{key_exchange => rsa, cipher => aes_256_gcm, mac => aead, prf => sha384}; -suite_definition(?TLS_DHE_RSA_WITH_AES_128_GCM_SHA256) -> +suite_bin_to_map(?TLS_DHE_RSA_WITH_AES_128_GCM_SHA256) -> #{key_exchange => dhe_rsa, cipher => aes_128_gcm, mac => aead, prf => sha256}; -suite_definition(?TLS_DHE_RSA_WITH_AES_256_GCM_SHA384) -> +suite_bin_to_map(?TLS_DHE_RSA_WITH_AES_256_GCM_SHA384) -> #{key_exchange => dhe_rsa, cipher => aes_256_gcm, mac => aead, prf => sha384}; -suite_definition(?TLS_DH_RSA_WITH_AES_128_GCM_SHA256) -> +suite_bin_to_map(?TLS_DH_RSA_WITH_AES_128_GCM_SHA256) -> #{key_exchange => dh_rsa, cipher => aes_128_gcm, mac => aead, prf => sha256}; -suite_definition(?TLS_DH_RSA_WITH_AES_256_GCM_SHA384) -> +suite_bin_to_map(?TLS_DH_RSA_WITH_AES_256_GCM_SHA384) -> #{key_exchange => dh_rsa, cipher => aes_256_gcm, mac => aead, prf => sha384}; -suite_definition(?TLS_DHE_DSS_WITH_AES_128_GCM_SHA256) -> +suite_bin_to_map(?TLS_DHE_DSS_WITH_AES_128_GCM_SHA256) -> #{key_exchange => dhe_dss, cipher => aes_128_gcm, mac => aead, prf => sha256}; -suite_definition(?TLS_DHE_DSS_WITH_AES_256_GCM_SHA384) -> +suite_bin_to_map(?TLS_DHE_DSS_WITH_AES_256_GCM_SHA384) -> #{key_exchange => dhe_dss, cipher => aes_256_gcm, mac => aead, prf => sha384}; -suite_definition(?TLS_DH_DSS_WITH_AES_128_GCM_SHA256) -> +suite_bin_to_map(?TLS_DH_DSS_WITH_AES_128_GCM_SHA256) -> #{key_exchange => dh_dss, cipher => aes_128_gcm, mac => null, prf => sha256}; -suite_definition(?TLS_DH_DSS_WITH_AES_256_GCM_SHA384) -> +suite_bin_to_map(?TLS_DH_DSS_WITH_AES_256_GCM_SHA384) -> #{key_exchange => dh_dss, cipher => aes_256_gcm, mac => aead, prf => sha384}; -suite_definition(?TLS_DH_anon_WITH_AES_128_GCM_SHA256) -> +suite_bin_to_map(?TLS_DH_anon_WITH_AES_128_GCM_SHA256) -> #{key_exchange => dh_anon, cipher => aes_128_gcm, mac => aead, prf => sha256}; -suite_definition(?TLS_DH_anon_WITH_AES_256_GCM_SHA384) -> +suite_bin_to_map(?TLS_DH_anon_WITH_AES_256_GCM_SHA384) -> #{key_exchange => dh_anon, cipher => aes_256_gcm, mac => aead, prf => sha384}; %% RFC 5289 ECC AES-GCM Cipher Suites -suite_definition(?TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256) -> +suite_bin_to_map(?TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256) -> #{key_exchange => ecdhe_ecdsa, cipher => aes_128_gcm, mac => aead, prf => sha256}; -suite_definition(?TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384) -> +suite_bin_to_map(?TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384) -> #{key_exchange => ecdhe_ecdsa, cipher => aes_256_gcm, mac => aead, prf => sha384}; -suite_definition(?TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256) -> +suite_bin_to_map(?TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256) -> #{key_exchange => ecdh_ecdsa, cipher => aes_128_gcm, mac => aead, prf => sha256}; -suite_definition(?TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384) -> +suite_bin_to_map(?TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384) -> #{key_exchange => ecdh_ecdsa, cipher => aes_256_gcm, mac => aead, prf => sha384}; -suite_definition(?TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) -> +suite_bin_to_map(?TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) -> #{key_exchange => ecdhe_rsa, cipher => aes_128_gcm, mac => aead, prf => sha256}; -suite_definition(?TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) -> +suite_bin_to_map(?TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) -> #{key_exchange => ecdhe_rsa, cipher => aes_256_gcm, mac => aead, prf => sha384}; -suite_definition(?TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256) -> +suite_bin_to_map(?TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256) -> #{key_exchange => ecdh_rsa, cipher => aes_128_gcm, mac => aead, prf => sha256}; -suite_definition(?TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384) -> +suite_bin_to_map(?TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384) -> #{key_exchange => ecdh_rsa, cipher => aes_256_gcm, mac => aead, prf => sha384}; -%% draft-agl-tls-chacha20poly1305-04 Chacha20/Poly1305 Suites -suite_definition(?TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256) -> +suite_bin_to_map(?TLS_PSK_WITH_AES_128_CCM) -> + #{key_exchange => psk, + cipher => aes_128_ccm, + mac => aead, + prf => sha256}; +suite_bin_to_map(?TLS_PSK_WITH_AES_256_CCM) -> + #{key_exchange => psk, + cipher => aes_256_ccm, + mac => aead, + prf => sha256}; +suite_bin_to_map(?TLS_DHE_PSK_WITH_AES_128_CCM) -> + #{key_exchange => dhe_psk, + cipher => aes_128_ccm, + mac => aead, + prf => sha256}; +suite_bin_to_map(?TLS_DHE_PSK_WITH_AES_256_CCM) -> + #{key_exchange => dhe_psk, + cipher => aes_256_ccm, + mac => aead, + prf => sha256}; +suite_bin_to_map(?TLS_PSK_WITH_AES_128_CCM_8) -> + #{key_exchange => psk, + cipher => aes_128_ccm_8, + mac => aead, + prf => sha256}; +suite_bin_to_map(?TLS_PSK_WITH_AES_256_CCM_8) -> + #{key_exchange => psk, + cipher => aes_256_ccm_8, + mac => aead, + prf => sha256}; +suite_bin_to_map(?TLS_PSK_DHE_WITH_AES_128_CCM_8) -> + #{key_exchange => dhe_psk, + cipher => aes_128_ccm_8, + mac => aead, + prf => sha256}; +suite_bin_to_map(?TLS_PSK_DHE_WITH_AES_256_CCM_8) -> + #{key_exchange => dhe_psk, + cipher => aes_256_ccm_8, + mac => aead, + prf => sha256}; +suite_bin_to_map(#{key_exchange := psk_dhe, + cipher := aes_256_ccm_8, + mac := aead, + prf := sha256}) -> + ?TLS_PSK_DHE_WITH_AES_256_CCM_8; + +% draft-agl-tls-chacha20poly1305-04 Chacha20/Poly1305 Suites +suite_bin_to_map(?TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256) -> #{key_exchange => ecdhe_rsa, cipher => chacha20_poly1305, mac => aead, prf => sha256}; -suite_definition(?TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256) -> +suite_bin_to_map(?TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256) -> #{key_exchange => ecdhe_ecdsa, cipher => chacha20_poly1305, mac => aead, prf => sha256}; -suite_definition(?TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256) -> +suite_bin_to_map(?TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256) -> #{key_exchange => dhe_rsa, cipher => chacha20_poly1305, mac => aead, + prf => sha256}; +%% TLS 1.3 Cipher Suites RFC8446 +suite_bin_to_map(?TLS_AES_128_GCM_SHA256) -> + #{key_exchange => any, + cipher => aes_128_gcm, + mac => aead, + prf => sha256}; +suite_bin_to_map(?TLS_AES_256_GCM_SHA384) -> + #{key_exchange => any, + cipher => aes_256_gcm, + mac => aead, + prf => sha384}; +suite_bin_to_map(?TLS_CHACHA20_POLY1305_SHA256) -> + #{key_exchange => any, + cipher => chacha20_poly1305, + mac => aead, prf => sha256}. +%% suite_bin_to_map(?TLS_AES_128_CCM_SHA256) -> +%% #{key_exchange => any, +%% cipher => aes_128_ccm, +%% mac => aead +%% prf => sha256}; +%% suite_bin_to_map(?TLS_AES_128_CCM_8_SHA256) -> +%% #{key_exchange => any, +%% cipher => aes_128_ccm_8, +%% mac => aead, +%% prf => sha256}. %%-------------------------------------------------------------------- --spec erl_suite_definition(cipher_suite() | internal_erl_cipher_suite()) -> old_erl_cipher_suite(). +-spec suite_legacy(cipher_suite() | internal_erl_cipher_suite()) -> old_erl_cipher_suite(). %% %% Description: Return erlang cipher suite definition. Filters last value %% for now (compatibility reasons). %%-------------------------------------------------------------------- -erl_suite_definition(Bin) when is_binary(Bin) -> - erl_suite_definition(suite_definition(Bin)); -erl_suite_definition(#{key_exchange := KeyExchange, cipher := Cipher, +suite_legacy(Bin) when is_binary(Bin) -> + suite_legacy(suite_bin_to_map(Bin)); +suite_legacy(#{key_exchange := KeyExchange, cipher := Cipher, mac := Hash, prf := Prf}) -> case Prf of default_prf -> @@ -821,943 +985,896 @@ erl_suite_definition(#{key_exchange := KeyExchange, cipher := Cipher, end. %%-------------------------------------------------------------------- --spec suite(internal_erl_cipher_suite()) -> cipher_suite(). +-spec suite_map_to_bin(internal_erl_cipher_suite()) -> cipher_suite(). %% %% Description: Return TLS cipher suite definition. %%-------------------------------------------------------------------- %% TLS v1.1 suites -suite(#{key_exchange := rsa, +suite_map_to_bin(#{key_exchange := rsa, cipher := rc4_128, mac := md5}) -> ?TLS_RSA_WITH_RC4_128_MD5; -suite(#{key_exchange := rsa, +suite_map_to_bin(#{key_exchange := rsa, cipher := rc4_128, mac := sha}) -> ?TLS_RSA_WITH_RC4_128_SHA; -suite(#{key_exchange := rsa, +suite_map_to_bin(#{key_exchange := rsa, cipher := des_cbc, mac := sha}) -> ?TLS_RSA_WITH_DES_CBC_SHA; -suite(#{key_exchange := rsa, +suite_map_to_bin(#{key_exchange := rsa, cipher :='3des_ede_cbc', mac := sha}) -> ?TLS_RSA_WITH_3DES_EDE_CBC_SHA; -suite(#{key_exchange := dhe_dss, +suite_map_to_bin(#{key_exchange := dhe_dss, cipher:= des_cbc, mac := sha}) -> ?TLS_DHE_DSS_WITH_DES_CBC_SHA; -suite(#{key_exchange := dhe_dss, +suite_map_to_bin(#{key_exchange := dhe_dss, cipher:= '3des_ede_cbc', mac := sha}) -> ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA; -suite(#{key_exchange := dhe_rsa, +suite_map_to_bin(#{key_exchange := dhe_rsa, cipher:= des_cbc, mac := sha}) -> ?TLS_DHE_RSA_WITH_DES_CBC_SHA; -suite(#{key_exchange := dhe_rsa, +suite_map_to_bin(#{key_exchange := dhe_rsa, cipher:= '3des_ede_cbc', mac := sha}) -> ?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA; -suite(#{key_exchange := dh_anon, +suite_map_to_bin(#{key_exchange := dh_anon, cipher:= rc4_128, mac := md5}) -> ?TLS_DH_anon_WITH_RC4_128_MD5; -suite(#{key_exchange := dh_anon, +suite_map_to_bin(#{key_exchange := dh_anon, cipher:= des_cbc, mac := sha}) -> ?TLS_DH_anon_WITH_DES_CBC_SHA; -suite(#{key_exchange := dh_anon, +suite_map_to_bin(#{key_exchange := dh_anon, cipher:= '3des_ede_cbc', mac := sha}) -> ?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA; %%% TSL V1.1 AES suites -suite(#{key_exchange := rsa, +suite_map_to_bin(#{key_exchange := rsa, cipher := aes_128_cbc, mac := sha}) -> ?TLS_RSA_WITH_AES_128_CBC_SHA; -suite(#{key_exchange := dhe_dss, +suite_map_to_bin(#{key_exchange := dhe_dss, cipher := aes_128_cbc, mac := sha}) -> ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA; -suite(#{key_exchange := dhe_rsa, +suite_map_to_bin(#{key_exchange := dhe_rsa, cipher := aes_128_cbc, mac := sha}) -> ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA; -suite(#{key_exchange := dh_anon, +suite_map_to_bin(#{key_exchange := dh_anon, cipher := aes_128_cbc, mac := sha}) -> ?TLS_DH_anon_WITH_AES_128_CBC_SHA; -suite(#{key_exchange := rsa, +suite_map_to_bin(#{key_exchange := rsa, cipher := aes_256_cbc, mac := sha}) -> ?TLS_RSA_WITH_AES_256_CBC_SHA; -suite(#{key_exchange := dhe_dss, +suite_map_to_bin(#{key_exchange := dhe_dss, cipher := aes_256_cbc, mac := sha}) -> ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA; -suite(#{key_exchange := dhe_rsa, +suite_map_to_bin(#{key_exchange := dhe_rsa, cipher := aes_256_cbc, mac := sha}) -> ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA; -suite(#{key_exchange := dh_anon, +suite_map_to_bin(#{key_exchange := dh_anon, cipher := aes_256_cbc, mac := sha}) -> ?TLS_DH_anon_WITH_AES_256_CBC_SHA; %% TLS v1.2 suites -suite(#{key_exchange := rsa, +suite_map_to_bin(#{key_exchange := rsa, cipher := aes_128_cbc, mac := sha256}) -> ?TLS_RSA_WITH_AES_128_CBC_SHA256; -suite(#{key_exchange := rsa, +suite_map_to_bin(#{key_exchange := rsa, cipher := aes_256_cbc, mac := sha256}) -> ?TLS_RSA_WITH_AES_256_CBC_SHA256; -suite(#{key_exchange := dhe_dss, +suite_map_to_bin(#{key_exchange := dhe_dss, cipher := aes_128_cbc, mac := sha256}) -> ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256; -suite(#{key_exchange := dhe_rsa, +suite_map_to_bin(#{key_exchange := dhe_rsa, cipher := aes_128_cbc, mac := sha256}) -> ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256; -suite(#{key_exchange := dhe_dss, +suite_map_to_bin(#{key_exchange := dhe_dss, cipher := aes_256_cbc, mac := sha256}) -> ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256; -suite(#{key_exchange := dhe_rsa, +suite_map_to_bin(#{key_exchange := dhe_rsa, cipher := aes_256_cbc, mac := sha256}) -> ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256; -suite(#{key_exchange := dh_anon, +suite_map_to_bin(#{key_exchange := dh_anon, cipher := aes_128_cbc, mac := sha256}) -> ?TLS_DH_anon_WITH_AES_128_CBC_SHA256; -suite(#{key_exchange := dh_anon, +suite_map_to_bin(#{key_exchange := dh_anon, cipher := aes_256_cbc, mac := sha256}) -> ?TLS_DH_anon_WITH_AES_256_CBC_SHA256; %%% PSK Cipher Suites RFC 4279 -suite(#{key_exchange := psk, +suite_map_to_bin(#{key_exchange := psk, cipher := rc4_128, mac := sha}) -> ?TLS_PSK_WITH_RC4_128_SHA; -suite(#{key_exchange := psk, +suite_map_to_bin(#{key_exchange := psk, cipher := '3des_ede_cbc', mac := sha}) -> ?TLS_PSK_WITH_3DES_EDE_CBC_SHA; -suite(#{key_exchange := psk, +suite_map_to_bin(#{key_exchange := psk, cipher := aes_128_cbc, mac := sha}) -> ?TLS_PSK_WITH_AES_128_CBC_SHA; -suite(#{key_exchange := psk, +suite_map_to_bin(#{key_exchange := psk, cipher := aes_256_cbc, mac := sha}) -> ?TLS_PSK_WITH_AES_256_CBC_SHA; -suite(#{key_exchange := dhe_psk, +suite_map_to_bin(#{key_exchange := dhe_psk, cipher := rc4_128, mac := sha}) -> ?TLS_DHE_PSK_WITH_RC4_128_SHA; -suite(#{key_exchange := dhe_psk, +suite_map_to_bin(#{key_exchange := dhe_psk, cipher := '3des_ede_cbc', mac := sha}) -> ?TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA; -suite(#{key_exchange := dhe_psk, +suite_map_to_bin(#{key_exchange := dhe_psk, cipher := aes_128_cbc, mac := sha}) -> ?TLS_DHE_PSK_WITH_AES_128_CBC_SHA; -suite(#{key_exchange := dhe_psk, +suite_map_to_bin(#{key_exchange := dhe_psk, cipher := aes_256_cbc, mac := sha}) -> ?TLS_DHE_PSK_WITH_AES_256_CBC_SHA; -suite(#{key_exchange := rsa_psk, +suite_map_to_bin(#{key_exchange := rsa_psk, cipher := rc4_128, mac := sha}) -> ?TLS_RSA_PSK_WITH_RC4_128_SHA; -suite(#{key_exchange := rsa_psk, +suite_map_to_bin(#{key_exchange := rsa_psk, cipher := '3des_ede_cbc', mac := sha}) -> ?TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA; -suite(#{key_exchange := rsa_psk, +suite_map_to_bin(#{key_exchange := rsa_psk, cipher := aes_128_cbc, mac := sha}) -> ?TLS_RSA_PSK_WITH_AES_128_CBC_SHA; -suite(#{key_exchange := rsa_psk, +suite_map_to_bin(#{key_exchange := rsa_psk, cipher := aes_256_cbc, mac := sha}) -> ?TLS_RSA_PSK_WITH_AES_256_CBC_SHA; %%% PSK NULL Cipher Suites RFC 4785 -suite(#{key_exchange := psk, +suite_map_to_bin(#{key_exchange := psk, cipher := null, mac := sha}) -> ?TLS_PSK_WITH_NULL_SHA; -suite(#{key_exchange := dhe_psk, +suite_map_to_bin(#{key_exchange := dhe_psk, cipher := null, mac := sha}) -> ?TLS_DHE_PSK_WITH_NULL_SHA; -suite(#{key_exchange := rsa_psk, +suite_map_to_bin(#{key_exchange := rsa_psk, cipher := null, mac := sha}) -> ?TLS_RSA_PSK_WITH_NULL_SHA; %%% TLS 1.2 PSK Cipher Suites RFC 5487 -suite(#{key_exchange := psk, +suite_map_to_bin(#{key_exchange := psk, cipher := aes_128_gcm, mac := aead, prf := sha256}) -> ?TLS_PSK_WITH_AES_128_GCM_SHA256; -suite(#{key_exchange := psk, +suite_map_to_bin(#{key_exchange := psk, cipher := aes_256_gcm, mac := aead, prf := sha384}) -> ?TLS_PSK_WITH_AES_256_GCM_SHA384; -suite(#{key_exchange := dhe_psk, +suite_map_to_bin(#{key_exchange := dhe_psk, cipher := aes_128_gcm, mac := aead, prf := sha256}) -> ?TLS_DHE_PSK_WITH_AES_128_GCM_SHA256; -suite(#{key_exchange := dhe_psk, +suite_map_to_bin(#{key_exchange := dhe_psk, cipher := aes_256_gcm, mac := aead, prf := sha384}) -> ?TLS_DHE_PSK_WITH_AES_256_GCM_SHA384; -suite(#{key_exchange := rsa_psk, +suite_map_to_bin(#{key_exchange := rsa_psk, cipher := aes_128_gcm, mac := aead, prf := sha256}) -> ?TLS_RSA_PSK_WITH_AES_128_GCM_SHA256; -suite(#{key_exchange := rsa_psk, +suite_map_to_bin(#{key_exchange := rsa_psk, cipher := aes_256_gcm, mac := aead, prf := sha384}) -> ?TLS_RSA_PSK_WITH_AES_256_GCM_SHA384; -suite(#{key_exchange := psk, +suite_map_to_bin(#{key_exchange := psk, cipher := aes_128_cbc, mac := sha256}) -> ?TLS_PSK_WITH_AES_128_CBC_SHA256; -suite(#{key_exchange := psk, +suite_map_to_bin(#{key_exchange := psk, cipher := aes_256_cbc, mac := sha384}) -> ?TLS_PSK_WITH_AES_256_CBC_SHA384; -suite(#{key_exchange := dhe_psk, +suite_map_to_bin(#{key_exchange := dhe_psk, cipher := aes_128_cbc, mac := sha256}) -> ?TLS_DHE_PSK_WITH_AES_128_CBC_SHA256; -suite(#{key_exchange := dhe_psk, +suite_map_to_bin(#{key_exchange := dhe_psk, cipher := aes_256_cbc, mac := sha384}) -> ?TLS_DHE_PSK_WITH_AES_256_CBC_SHA384; -suite(#{key_exchange := rsa_psk, +suite_map_to_bin(#{key_exchange := rsa_psk, cipher := aes_128_cbc, mac := sha256}) -> ?TLS_RSA_PSK_WITH_AES_128_CBC_SHA256; -suite(#{key_exchange := rsa_psk, +suite_map_to_bin(#{key_exchange := rsa_psk, cipher := aes_256_cbc, mac := sha384}) -> ?TLS_RSA_PSK_WITH_AES_256_CBC_SHA384; -suite(#{key_exchange := psk, +suite_map_to_bin(#{key_exchange := psk, cipher := null, mac := sha256}) -> ?TLS_PSK_WITH_NULL_SHA256; -suite(#{key_exchange := psk, +suite_map_to_bin(#{key_exchange := psk, cipher := null, mac := sha384}) -> ?TLS_PSK_WITH_NULL_SHA384; -suite(#{key_exchange := dhe_psk, +suite_map_to_bin(#{key_exchange := dhe_psk, cipher := null, mac := sha256}) -> ?TLS_DHE_PSK_WITH_NULL_SHA256; -suite(#{key_exchange := dhe_psk, +suite_map_to_bin(#{key_exchange := dhe_psk, cipher := null, mac := sha384}) -> ?TLS_DHE_PSK_WITH_NULL_SHA384; -suite(#{key_exchange := rsa_psk, +suite_map_to_bin(#{key_exchange := rsa_psk, cipher := null, mac := sha256}) -> ?TLS_RSA_PSK_WITH_NULL_SHA256; -suite(#{key_exchange := rsa_psk, +suite_map_to_bin(#{key_exchange := rsa_psk, cipher := null, mac := sha384}) -> ?TLS_RSA_PSK_WITH_NULL_SHA384; %%% ECDHE PSK Cipher Suites RFC 5489 -suite(#{key_exchange := ecdhe_psk, +suite_map_to_bin(#{key_exchange := ecdhe_psk, cipher := rc4_128, mac := sha}) -> ?TLS_ECDHE_PSK_WITH_RC4_128_SHA; -suite(#{key_exchange := ecdhe_psk, +suite_map_to_bin(#{key_exchange := ecdhe_psk, cipher :='3des_ede_cbc', mac := sha}) -> ?TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA; -suite(#{key_exchange := ecdhe_psk, +suite_map_to_bin(#{key_exchange := ecdhe_psk, cipher := aes_128_cbc, mac := sha}) -> ?TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA; -suite(#{key_exchange := ecdhe_psk, +suite_map_to_bin(#{key_exchange := ecdhe_psk, cipher := aes_256_cbc, mac := sha}) -> ?TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA; -suite(#{key_exchange := ecdhe_psk, +suite_map_to_bin(#{key_exchange := ecdhe_psk, cipher := aes_128_cbc, mac := sha256}) -> ?TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256; -suite(#{key_exchange := ecdhe_psk, +suite_map_to_bin(#{key_exchange := ecdhe_psk, cipher := aes_256_cbc, mac := sha384}) -> ?TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384; -suite(#{key_exchange := ecdhe_psk, +suite_map_to_bin(#{key_exchange := ecdhe_psk, cipher := null, mac := sha256}) -> ?TLS_ECDHE_PSK_WITH_NULL_SHA256; -suite(#{key_exchange := ecdhe_psk, +suite_map_to_bin(#{key_exchange := ecdhe_psk, cipher := null, mac := sha384}) -> ?TLS_ECDHE_PSK_WITH_NULL_SHA384; %%% ECDHE_PSK with AES-GCM and AES-CCM Cipher Suites, draft-ietf-tls-ecdhe-psk-aead-05 -suite(#{key_exchange := ecdhe_psk, +suite_map_to_bin(#{key_exchange := ecdhe_psk, cipher := aes_128_gcm, mac := null, prf := sha256}) -> ?TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256; -suite(#{key_exchange := ecdhe_psk, +suite_map_to_bin(#{key_exchange := ecdhe_psk, cipher := aes_256_gcm, mac := null, prf := sha384}) -> ?TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384; - %% suite(#{key_exchange := ecdhe_psk, - %% cipher := aes_128_ccm, - %% mac := null, - %% prf := sha256}) -> - %% ?TLS_ECDHE_PSK_WITH_AES_128_CCM_8_SHA256; - %% suite(#{key_exchange := ecdhe_psk, - %% cipher := aes_256_ccm, - %% mac := null, - %% prf := sha256}) -> - %% ?TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256; +suite_map_to_bin(#{key_exchange := ecdhe_psk, + cipher := aes_128_ccm_8, + mac := null, + prf := sha256}) -> + ?TLS_ECDHE_PSK_WITH_AES_128_CCM_8_SHA256; +suite_map_to_bin(#{key_exchange := ecdhe_psk, + cipher := aes_128_ccm, + mac := null, + prf := sha256}) -> + ?TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256; %%% SRP Cipher Suites RFC 5054 -suite(#{key_exchange := srp_anon, +suite_map_to_bin(#{key_exchange := srp_anon, cipher := '3des_ede_cbc', mac := sha}) -> ?TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA; -suite(#{key_exchange := srp_rsa, +suite_map_to_bin(#{key_exchange := srp_rsa, cipher := '3des_ede_cbc', mac := sha}) -> ?TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA; -suite(#{key_exchange := srp_dss, +suite_map_to_bin(#{key_exchange := srp_dss, cipher := '3des_ede_cbc', mac := sha}) -> ?TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA; -suite(#{key_exchange := srp_anon, +suite_map_to_bin(#{key_exchange := srp_anon, cipher := aes_128_cbc, mac := sha}) -> ?TLS_SRP_SHA_WITH_AES_128_CBC_SHA; -suite(#{key_exchange := srp_rsa, +suite_map_to_bin(#{key_exchange := srp_rsa, cipher := aes_128_cbc, mac := sha}) -> ?TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA; -suite(#{key_exchange := srp_dss, +suite_map_to_bin(#{key_exchange := srp_dss, cipher := aes_128_cbc, mac := sha}) -> ?TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA; -suite(#{key_exchange := srp_anon, +suite_map_to_bin(#{key_exchange := srp_anon, cipher := aes_256_cbc, mac := sha}) -> ?TLS_SRP_SHA_WITH_AES_256_CBC_SHA; -suite(#{key_exchange := srp_rsa, +suite_map_to_bin(#{key_exchange := srp_rsa, cipher := aes_256_cbc, mac := sha}) -> ?TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA; -suite(#{key_exchange := srp_dss, +suite_map_to_bin(#{key_exchange := srp_dss, cipher := aes_256_cbc, mac := sha}) -> ?TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA; %%% RFC 4492 EC TLS suites -suite(#{key_exchange := ecdh_ecdsa, +suite_map_to_bin(#{key_exchange := ecdh_ecdsa, cipher := null, mac := sha}) -> ?TLS_ECDH_ECDSA_WITH_NULL_SHA; -suite(#{key_exchange := ecdh_ecdsa, +suite_map_to_bin(#{key_exchange := ecdh_ecdsa, cipher := rc4_128, mac := sha}) -> ?TLS_ECDH_ECDSA_WITH_RC4_128_SHA; -suite(#{key_exchange := ecdh_ecdsa, +suite_map_to_bin(#{key_exchange := ecdh_ecdsa, cipher := '3des_ede_cbc', mac := sha}) -> ?TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA; -suite(#{key_exchange := ecdh_ecdsa, +suite_map_to_bin(#{key_exchange := ecdh_ecdsa, cipher := aes_128_cbc, mac := sha}) -> ?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA; -suite(#{key_exchange := ecdh_ecdsa, +suite_map_to_bin(#{key_exchange := ecdh_ecdsa, cipher := aes_256_cbc, mac := sha}) -> ?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA; -suite(#{key_exchange := ecdhe_ecdsa, +suite_map_to_bin(#{key_exchange := ecdhe_ecdsa, cipher := null, mac := sha}) -> ?TLS_ECDHE_ECDSA_WITH_NULL_SHA; -suite(#{key_exchange := ecdhe_ecdsa, +suite_map_to_bin(#{key_exchange := ecdhe_ecdsa, cipher := rc4_128, mac := sha}) -> ?TLS_ECDHE_ECDSA_WITH_RC4_128_SHA; -suite(#{key_exchange := ecdhe_ecdsa, +suite_map_to_bin(#{key_exchange := ecdhe_ecdsa, cipher := '3des_ede_cbc', mac := sha}) -> ?TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA; -suite(#{key_exchange := ecdhe_ecdsa, +suite_map_to_bin(#{key_exchange := ecdhe_ecdsa, cipher := aes_128_cbc, mac := sha}) -> ?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA; -suite(#{key_exchange := ecdhe_ecdsa, +suite_map_to_bin(#{key_exchange := ecdhe_ecdsa, cipher := aes_256_cbc, mac := sha}) -> ?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA; -suite(#{key_exchange := ecdh_rsa, +suite_map_to_bin(#{key_exchange := ecdh_rsa, cipher := null, mac := sha}) -> ?TLS_ECDH_RSA_WITH_NULL_SHA; -suite(#{key_exchange := ecdh_rsa, +suite_map_to_bin(#{key_exchange := ecdh_rsa, cipher := rc4_128, mac := sha}) -> ?TLS_ECDH_RSA_WITH_RC4_128_SHA; -suite(#{key_exchange := ecdh_rsa, +suite_map_to_bin(#{key_exchange := ecdh_rsa, cipher := '3des_ede_cbc', mac := sha}) -> ?TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA; -suite(#{key_exchange := ecdh_rsa, +suite_map_to_bin(#{key_exchange := ecdh_rsa, cipher := aes_128_cbc, mac := sha}) -> ?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA; -suite(#{key_exchange := ecdh_rsa, +suite_map_to_bin(#{key_exchange := ecdh_rsa, cipher := aes_256_cbc, mac := sha}) -> ?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA; -suite(#{key_exchange := ecdhe_rsa, +suite_map_to_bin(#{key_exchange := ecdhe_rsa, cipher := null, mac := sha}) -> ?TLS_ECDHE_RSA_WITH_NULL_SHA; -suite(#{key_exchange := ecdhe_rsa, +suite_map_to_bin(#{key_exchange := ecdhe_rsa, cipher := rc4_128, mac := sha}) -> ?TLS_ECDHE_RSA_WITH_RC4_128_SHA; -suite(#{key_exchange := ecdhe_rsa, +suite_map_to_bin(#{key_exchange := ecdhe_rsa, cipher := '3des_ede_cbc', mac := sha}) -> ?TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA; -suite(#{key_exchange := ecdhe_rsa, +suite_map_to_bin(#{key_exchange := ecdhe_rsa, cipher := aes_128_cbc, mac := sha}) -> ?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA; -suite(#{key_exchange := ecdhe_rsa, +suite_map_to_bin(#{key_exchange := ecdhe_rsa, cipher := aes_256_cbc, mac := sha}) -> ?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA; -suite(#{key_exchange := ecdh_anon, +suite_map_to_bin(#{key_exchange := ecdh_anon, cipher := null, mac := sha}) -> ?TLS_ECDH_anon_WITH_NULL_SHA; -suite(#{key_exchange := ecdh_anon, +suite_map_to_bin(#{key_exchange := ecdh_anon, cipher := rc4_128, mac := sha}) -> ?TLS_ECDH_anon_WITH_RC4_128_SHA; -suite(#{key_exchange := ecdh_anon, +suite_map_to_bin(#{key_exchange := ecdh_anon, cipher := '3des_ede_cbc', mac := sha}) -> ?TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA; -suite(#{key_exchange := ecdh_anon, +suite_map_to_bin(#{key_exchange := ecdh_anon, cipher := aes_128_cbc, mac := sha}) -> ?TLS_ECDH_anon_WITH_AES_128_CBC_SHA; -suite(#{key_exchange := ecdh_anon, +suite_map_to_bin(#{key_exchange := ecdh_anon, cipher := aes_256_cbc, mac := sha}) -> ?TLS_ECDH_anon_WITH_AES_256_CBC_SHA; %%% RFC 5289 EC TLS suites -suite(#{key_exchange := ecdhe_ecdsa, +suite_map_to_bin(#{key_exchange := ecdhe_ecdsa, cipher := aes_128_cbc, mac:= sha256, prf := sha256}) -> ?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256; -suite(#{key_exchange := ecdhe_ecdsa, +suite_map_to_bin(#{key_exchange := ecdhe_ecdsa, cipher := aes_256_cbc, mac := sha384, prf := sha384}) -> ?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384; -suite(#{key_exchange := ecdh_ecdsa, +suite_map_to_bin(#{key_exchange := ecdh_ecdsa, cipher := aes_128_cbc, mac := sha256, prf := sha256}) -> ?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256; -suite(#{key_exchange := ecdh_ecdsa, +suite_map_to_bin(#{key_exchange := ecdh_ecdsa, cipher := aes_256_cbc, mac := sha384, prf := sha384}) -> ?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384; -suite(#{key_exchange := ecdhe_rsa, +suite_map_to_bin(#{key_exchange := ecdhe_rsa, cipher := aes_128_cbc, mac := sha256, prf := sha256}) -> ?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256; -suite(#{key_exchange := ecdhe_rsa, +suite_map_to_bin(#{key_exchange := ecdhe_rsa, cipher := aes_256_cbc, mac := sha384, prf := sha384}) -> ?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384; -suite(#{key_exchange := ecdh_rsa, +suite_map_to_bin(#{key_exchange := ecdh_rsa, cipher := aes_128_cbc, mac := sha256, prf := sha256}) -> ?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256; -suite(#{key_exchange := ecdh_rsa, +suite_map_to_bin(#{key_exchange := ecdh_rsa, cipher := aes_256_cbc, mac := sha384, prf := sha384}) -> ?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384; %% RFC 5288 AES-GCM Cipher Suites -suite(#{key_exchange := rsa, +suite_map_to_bin(#{key_exchange := rsa, cipher := aes_128_gcm, mac := aead, prf := sha256}) -> ?TLS_RSA_WITH_AES_128_GCM_SHA256; -suite(#{key_exchange := rsa, +suite_map_to_bin(#{key_exchange := rsa, cipher := aes_256_gcm, mac := aead, prf := sha384}) -> ?TLS_RSA_WITH_AES_256_GCM_SHA384; -suite(#{key_exchange := dhe_rsa, +suite_map_to_bin(#{key_exchange := dhe_rsa, cipher := aes_128_gcm, mac := aead, prf := sha256}) -> ?TLS_DHE_RSA_WITH_AES_128_GCM_SHA256; -suite(#{key_exchange := dhe_rsa, +suite_map_to_bin(#{key_exchange := dhe_rsa, cipher := aes_256_gcm, mac := aead, prf := sha384}) -> ?TLS_DHE_RSA_WITH_AES_256_GCM_SHA384; -suite(#{key_exchange := dh_rsa, +suite_map_to_bin(#{key_exchange := dh_rsa, cipher := aes_128_gcm, mac := aead, prf := sha256}) -> ?TLS_DH_RSA_WITH_AES_128_GCM_SHA256; -suite(#{key_exchange := dh_rsa, +suite_map_to_bin(#{key_exchange := dh_rsa, cipher := aes_256_gcm, mac := aead, prf := sha384}) -> ?TLS_DH_RSA_WITH_AES_256_GCM_SHA384; -suite(#{key_exchange := dhe_dss, +suite_map_to_bin(#{key_exchange := dhe_dss, cipher := aes_128_gcm, mac := aead, prf := sha256}) -> ?TLS_DHE_DSS_WITH_AES_128_GCM_SHA256; -suite(#{key_exchange := dhe_dss, +suite_map_to_bin(#{key_exchange := dhe_dss, cipher := aes_256_gcm, mac := aead, prf := sha384}) -> ?TLS_DHE_DSS_WITH_AES_256_GCM_SHA384; -suite(#{key_exchange := dh_dss, +suite_map_to_bin(#{key_exchange := dh_dss, cipher := aes_128_gcm, mac := aead, prf := sha256}) -> ?TLS_DH_DSS_WITH_AES_128_GCM_SHA256; -suite(#{key_exchange := dh_dss, +suite_map_to_bin(#{key_exchange := dh_dss, cipher := aes_256_gcm, mac := aead, prf := sha384}) -> ?TLS_DH_DSS_WITH_AES_256_GCM_SHA384; -suite(#{key_exchange := dh_anon, +suite_map_to_bin(#{key_exchange := dh_anon, cipher := aes_128_gcm, mac := aead, prf := sha256}) -> ?TLS_DH_anon_WITH_AES_128_GCM_SHA256; -suite(#{key_exchange := dh_anon, +suite_map_to_bin(#{key_exchange := dh_anon, cipher := aes_256_gcm, mac := aead, prf := sha384}) -> ?TLS_DH_anon_WITH_AES_256_GCM_SHA384; %% RFC 5289 ECC AES-GCM Cipher Suites -suite(#{key_exchange := ecdhe_ecdsa, +suite_map_to_bin(#{key_exchange := ecdhe_ecdsa, cipher := aes_128_gcm, mac := aead, prf := sha256}) -> ?TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256; -suite(#{key_exchange := ecdhe_ecdsa, +suite_map_to_bin(#{key_exchange := ecdhe_ecdsa, cipher := aes_256_gcm, mac := aead, prf := sha384}) -> ?TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384; -suite(#{key_exchange := ecdh_ecdsa, +suite_map_to_bin(#{key_exchange := ecdh_ecdsa, cipher := aes_128_gcm, mac := aead, prf := sha256}) -> ?TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256; -suite(#{key_exchange := ecdh_ecdsa, +suite_map_to_bin(#{key_exchange := ecdh_ecdsa, cipher := aes_256_gcm, mac := aead, prf := sha384}) -> ?TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384; -suite(#{key_exchange := ecdhe_rsa, +suite_map_to_bin(#{key_exchange := ecdhe_rsa, cipher := aes_128_gcm, mac := aead, prf := sha256}) -> ?TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256; -suite(#{key_exchange := ecdhe_rsa, +suite_map_to_bin(#{key_exchange := ecdhe_rsa, cipher := aes_256_gcm, mac := aead, prf := sha384}) -> ?TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384; -suite(#{key_exchange := ecdh_rsa, +suite_map_to_bin(#{key_exchange := ecdh_rsa, cipher := aes_128_gcm, mac := aead, prf := sha256}) -> ?TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256; -suite(#{key_exchange := ecdh_rsa, +suite_map_to_bin(#{key_exchange := ecdh_rsa, cipher := aes_256_gcm, mac := aead, prf := sha384}) -> ?TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384; %% draft-agl-tls-chacha20poly1305-04 Chacha20/Poly1305 Suites -suite(#{key_exchange := ecdhe_rsa, +suite_map_to_bin(#{key_exchange := ecdhe_rsa, cipher := chacha20_poly1305, mac := aead, prf := sha256}) -> ?TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256; -suite(#{key_exchange := ecdhe_ecdsa, +suite_map_to_bin(#{key_exchange := ecdhe_ecdsa, cipher := chacha20_poly1305, mac := aead, prf := sha256}) -> ?TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256; -suite(#{key_exchange := dhe_rsa, +suite_map_to_bin(#{key_exchange := dhe_rsa, cipher := chacha20_poly1305, mac := aead, prf := sha256}) -> - ?TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256. + ?TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256; -%%-------------------------------------------------------------------- --spec openssl_suite(openssl_cipher_suite()) -> cipher_suite(). -%% -%% Description: Return TLS cipher suite definition. -%%-------------------------------------------------------------------- -%% translate constants <-> openssl-strings -openssl_suite("DHE-RSA-AES256-SHA256") -> - ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256; -openssl_suite("DHE-DSS-AES256-SHA256") -> - ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256; -openssl_suite("AES256-SHA256") -> - ?TLS_RSA_WITH_AES_256_CBC_SHA256; -openssl_suite("DHE-RSA-AES128-SHA256") -> - ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256; -openssl_suite("DHE-DSS-AES128-SHA256") -> - ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256; -openssl_suite("AES128-SHA256") -> - ?TLS_RSA_WITH_AES_128_CBC_SHA256; -openssl_suite("DHE-RSA-AES256-SHA") -> - ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA; -openssl_suite("DHE-DSS-AES256-SHA") -> - ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA; -openssl_suite("AES256-SHA") -> - ?TLS_RSA_WITH_AES_256_CBC_SHA; -openssl_suite("EDH-RSA-DES-CBC3-SHA") -> - ?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA; -openssl_suite("EDH-DSS-DES-CBC3-SHA") -> - ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA; -openssl_suite("DES-CBC3-SHA") -> - ?TLS_RSA_WITH_3DES_EDE_CBC_SHA; -openssl_suite("DHE-RSA-AES128-SHA") -> - ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA; -openssl_suite("DHE-DSS-AES128-SHA") -> - ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA; -openssl_suite("AES128-SHA") -> - ?TLS_RSA_WITH_AES_128_CBC_SHA; -openssl_suite("RC4-SHA") -> - ?TLS_RSA_WITH_RC4_128_SHA; -openssl_suite("RC4-MD5") -> - ?TLS_RSA_WITH_RC4_128_MD5; -openssl_suite("EDH-RSA-DES-CBC-SHA") -> - ?TLS_DHE_RSA_WITH_DES_CBC_SHA; -openssl_suite("DES-CBC-SHA") -> - ?TLS_RSA_WITH_DES_CBC_SHA; +%% RFC 6655 - TLS-1.2 cipher suites +suite_map_to_bin(#{key_exchange := psk, + cipher := aes_128_ccm, + mac := aead, + prf := sha256}) -> + ?TLS_PSK_WITH_AES_128_CCM; +suite_map_to_bin(#{key_exchange := psk, + cipher := aes_256_ccm, + mac := aead, + prf := sha256}) -> + ?TLS_PSK_WITH_AES_256_CCM; +suite_map_to_bin(#{key_exchange := dhe_psk, + cipher := aes_128_ccm, + mac := aead, + prf := sha256}) -> + ?TLS_DHE_PSK_WITH_AES_128_CCM; +suite_map_to_bin(#{key_exchange := dhe_psk, + cipher := aes_256_ccm, + mac := aead, + prf := sha256}) -> + ?TLS_DHE_PSK_WITH_AES_256_CCM; +suite_map_to_bin(#{key_exchange := rsa, + cipher := aes_128_ccm, + mac := aead, + prf := sha256}) -> + ?TLS_RSA_WITH_AES_128_CCM; +suite_map_to_bin(#{key_exchange := rsa, + cipher := aes_256_ccm, + mac := aead, + prf := sha256}) -> + ?TLS_RSA_WITH_AES_256_CCM; +suite_map_to_bin(#{key_exchange := dhe_rsa, + cipher := aes_128_ccm, + mac := aead, + prf := sha256}) -> + ?TLS_DHE_RSA_WITH_AES_128_CCM; +suite_map_to_bin(#{key_exchange := dhe_rsa, + cipher := aes_256_ccm, + mac := aead, + prf := sha256}) -> + ?TLS_DHE_RSA_WITH_AES_256_CCM; -%%% SRP Cipher Suites RFC 5054 +suite_map_to_bin(#{key_exchange := psk, + cipher := aes_128_ccm_8, + mac := aead, + prf := sha256}) -> + ?TLS_PSK_WITH_AES_128_CCM_8; +suite_map_to_bin(#{key_exchange := psk, + cipher := aes_256_ccm_8, + mac := aead, + prf := sha256}) -> + ?TLS_PSK_WITH_AES_256_CCM_8; +suite_map_to_bin(#{key_exchange := dhe_psk, + cipher := aes_128_ccm_8, + mac := aead, + prf := sha256}) -> + ?TLS_PSK_DHE_WITH_AES_128_CCM_8; +suite_map_to_bin(#{key_exchange := dhe_psk, + cipher := aes_256_ccm_8, + mac := aead, + prf := sha256}) -> + ?TLS_PSK_DHE_WITH_AES_256_CCM_8; +suite_map_to_bin(#{key_exchange := rsa, + cipher := aes_128_ccm_8, + mac := aead, + prf := sha256}) -> + ?TLS_RSA_WITH_AES_128_CCM_8; +suite_map_to_bin(#{key_exchange := rsa, + cipher := aes_256_ccm_8, + mac := aead, + prf := sha256}) -> + ?TLS_RSA_WITH_AES_256_CCM_8; +suite_map_to_bin(#{key_exchange := dhe_rsa, + cipher := aes_128_ccm_8, + mac := aead, + prf := sha256}) -> + ?TLS_DHE_RSA_WITH_AES_128_CCM_8; +suite_map_to_bin(#{key_exchange := dhe_rsa, + cipher := aes_256_ccm_8, + mac := aead, + prf := sha256}) -> + ?TLS_DHE_RSA_WITH_AES_256_CCM_8; -openssl_suite("SRP-DSS-AES-256-CBC-SHA") -> - ?TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA; -openssl_suite("SRP-RSA-AES-256-CBC-SHA") -> - ?TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA; -openssl_suite("SRP-DSS-3DES-EDE-CBC-SHA") -> - ?TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA; -openssl_suite("SRP-RSA-3DES-EDE-CBC-SHA") -> - ?TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA; -openssl_suite("SRP-DSS-AES-128-CBC-SHA") -> - ?TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA; -openssl_suite("SRP-RSA-AES-128-CBC-SHA") -> - ?TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA; +%% TLS 1.3 Cipher Suites RFC8446 +suite_map_to_bin(#{key_exchange := any, + cipher := aes_128_gcm, + mac := aead, + prf := sha256}) -> + ?TLS_AES_128_GCM_SHA256; +suite_map_to_bin(#{key_exchange := any, + cipher := aes_256_gcm, + mac := aead, + prf := sha384}) -> + ?TLS_AES_256_GCM_SHA384; +suite_map_to_bin(#{key_exchange := any, + cipher := chacha20_poly1305, + mac := aead, + prf := sha256}) -> + ?TLS_CHACHA20_POLY1305_SHA256. +%% suite_map_to_bin(#{key_exchange := any, +%% cipher := aes_128_ccm, +%% mac := aead, +%% prf := sha256}) -> +%% ?TLS_AES_128_CCM_SHA256; +%% suite_map_to_bin(#{key_exchange := any, +%% cipher := aes_128_ccm_8, +%% mac := aead, +%% prf := sha256}) -> +%% ?TLS_AES_128_CCM_8_SHA256. -%% RFC 4492 EC TLS suites -openssl_suite("ECDH-ECDSA-RC4-SHA") -> - ?TLS_ECDH_ECDSA_WITH_RC4_128_SHA; -openssl_suite("ECDH-ECDSA-DES-CBC3-SHA") -> - ?TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA; -openssl_suite("ECDH-ECDSA-AES128-SHA") -> - ?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA; -openssl_suite("ECDH-ECDSA-AES256-SHA") -> - ?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA; -openssl_suite("ECDHE-ECDSA-RC4-SHA") -> - ?TLS_ECDHE_ECDSA_WITH_RC4_128_SHA; -openssl_suite("ECDHE-ECDSA-DES-CBC3-SHA") -> - ?TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA; -openssl_suite("ECDHE-ECDSA-AES128-SHA") -> - ?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA; -openssl_suite("ECDHE-ECDSA-AES256-SHA") -> - ?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA; +tls_1_3_suite_str_to_map(CipherStr) -> + {Cipher, Mac, Prf} = cipher_str_to_algs(CipherStr, ""), + #{key_exchange => any, + mac => Mac, + cipher => Cipher, + prf => Prf + }. -openssl_suite("ECDHE-RSA-RC4-SHA") -> - ?TLS_ECDHE_RSA_WITH_RC4_128_SHA; -openssl_suite("ECDHE-RSA-DES-CBC3-SHA") -> - ?TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA; -openssl_suite("ECDHE-RSA-AES128-SHA") -> - ?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA; -openssl_suite("ECDHE-RSA-AES256-SHA") -> - ?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA; +pre_tls_1_3_suite_str_to_map(KexStr, Rest) -> + Kex = algo_str_to_atom(KexStr), + [CipherStr, AlgStr] = string:split(Rest, "_", trailing), + {Cipher, Mac, Prf} = cipher_str_to_algs(CipherStr, AlgStr), + #{key_exchange => Kex, + mac => Mac, + cipher => Cipher, + prf => Prf + }. + +cipher_str_to_algs(CipherStr, "CCM"= End) -> %% PRE TLS 1.3 + Cipher = algo_str_to_atom(CipherStr ++ "_" ++ End), + {Cipher, aead, sha256}; +cipher_str_to_algs(CipherStr, "8" = End) -> %% PRE TLS 1.3 + Cipher = algo_str_to_atom(CipherStr ++ "_" ++ End), + {Cipher, aead, sha256}; +cipher_str_to_algs(CipherStr, "CHACHA20_POLY1305" = End) -> %% PRE TLS 1.3 + Cipher = algo_str_to_atom(CipherStr ++ "_" ++ End), + {Cipher, aead, sha256}; +cipher_str_to_algs(CipherStr0, "") -> %% TLS 1.3 + [CipherStr, AlgStr] = string:split(CipherStr0, "_", trailing), + Hash = algo_str_to_atom(AlgStr), + Cipher = algo_str_to_atom(CipherStr), + {Cipher, aead, Hash}; +cipher_str_to_algs(CipherStr, HashStr) -> %% PRE TLS 1.3 + Hash = algo_str_to_atom(HashStr), + Cipher = algo_str_to_atom(CipherStr), + case is_aead_cipher(CipherStr) of + true -> + {Cipher, aead, Hash}; + false -> + {Cipher, Hash, default_prf} + end. -openssl_suite("ECDH-RSA-RC4-SHA") -> - ?TLS_ECDH_RSA_WITH_RC4_128_SHA; -openssl_suite("ECDH-RSA-DES-CBC3-SHA") -> - ?TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA; -openssl_suite("ECDH-RSA-AES128-SHA") -> - ?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA; -openssl_suite("ECDH-RSA-AES256-SHA") -> - ?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA; +%% PRE TLS 1.3 +is_aead_cipher("CHACHA20_POLY1305") -> + true; +is_aead_cipher(CipherStr) -> + [_, Rest] = string:split(CipherStr, "_", trailing), + (Rest == "GCM") orelse (Rest == "CCM") orelse (Rest == "8"). -%% RFC 5289 EC TLS suites -openssl_suite("ECDHE-ECDSA-AES128-SHA256") -> - ?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256; -openssl_suite("ECDHE-ECDSA-AES256-SHA384") -> - ?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384; -openssl_suite("ECDH-ECDSA-AES128-SHA256") -> - ?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256; -openssl_suite("ECDH-ECDSA-AES256-SHA384") -> - ?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384; -openssl_suite("ECDHE-RSA-AES128-SHA256") -> - ?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256; -openssl_suite("ECDHE-RSA-AES256-SHA384") -> - ?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384; -openssl_suite("ECDH-RSA-AES128-SHA256") -> - ?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256; -openssl_suite("ECDH-RSA-AES256-SHA384") -> - ?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384; +openssl_is_aead_cipher("CHACHA20-POLY1305") -> + true; +openssl_is_aead_cipher(CipherStr) -> + case string:split(CipherStr, "-", trailing) of + [_, Rest] -> + (Rest == "GCM") orelse (Rest == "CCM") orelse (Rest == "8"); + [_] -> + false + end. -%% RFC 5288 AES-GCM Cipher Suites -openssl_suite("AES128-GCM-SHA256") -> - ?TLS_RSA_WITH_AES_128_GCM_SHA256; -openssl_suite("AES256-GCM-SHA384") -> - ?TLS_RSA_WITH_AES_256_GCM_SHA384; -openssl_suite("DHE-RSA-AES128-GCM-SHA256") -> - ?TLS_DHE_RSA_WITH_AES_128_GCM_SHA256; -openssl_suite("DHE-RSA-AES256-GCM-SHA384") -> - ?TLS_DHE_RSA_WITH_AES_256_GCM_SHA384; -openssl_suite("DH-RSA-AES128-GCM-SHA256") -> - ?TLS_DH_RSA_WITH_AES_128_GCM_SHA256; -openssl_suite("DH-RSA-AES256-GCM-SHA384") -> - ?TLS_DH_RSA_WITH_AES_256_GCM_SHA384; -openssl_suite("DHE-DSS-AES128-GCM-SHA256") -> - ?TLS_DHE_DSS_WITH_AES_128_GCM_SHA256; -openssl_suite("DHE-DSS-AES256-GCM-SHA384") -> - ?TLS_DHE_DSS_WITH_AES_256_GCM_SHA384; -openssl_suite("DH-DSS-AES128-GCM-SHA256") -> - ?TLS_DH_DSS_WITH_AES_128_GCM_SHA256; -openssl_suite("DH-DSS-AES256-GCM-SHA384") -> - ?TLS_DH_DSS_WITH_AES_256_GCM_SHA384; +algo_str_to_atom(AlgoStr) -> + erlang:list_to_existing_atom(string:to_lower(AlgoStr)). -%% RFC 5289 ECC AES-GCM Cipher Suites -openssl_suite("ECDHE-ECDSA-AES128-GCM-SHA256") -> - ?TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256; -openssl_suite("ECDHE-ECDSA-AES256-GCM-SHA384") -> - ?TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384; -openssl_suite("ECDH-ECDSA-AES128-GCM-SHA256") -> - ?TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256; -openssl_suite("ECDH-ECDSA-AES256-GCM-SHA384") -> - ?TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384; -openssl_suite("ECDHE-RSA-AES128-GCM-SHA256") -> - ?TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256; -openssl_suite("ECDHE-RSA-AES256-GCM-SHA384") -> - ?TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384; -openssl_suite("ECDH-RSA-AES128-GCM-SHA256") -> - ?TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256; -openssl_suite("ECDH-RSA-AES256-GCM-SHA384") -> - ?TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384. -%%-------------------------------------------------------------------- --spec openssl_suite_name(cipher_suite()) -> openssl_cipher_suite() | internal_erl_cipher_suite(). -%% -%% Description: Return openssl cipher suite name if possible -%%------------------------------------------------------------------- -openssl_suite_name(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA) -> - "DHE-RSA-AES256-SHA"; -openssl_suite_name(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA) -> - "DHE-DSS-AES256-SHA"; -openssl_suite_name(?TLS_RSA_WITH_AES_256_CBC_SHA) -> - "AES256-SHA"; -openssl_suite_name(?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA) -> - "EDH-RSA-DES-CBC3-SHA"; -openssl_suite_name(?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA) -> - "EDH-DSS-DES-CBC3-SHA"; -openssl_suite_name(?TLS_RSA_WITH_3DES_EDE_CBC_SHA) -> - "DES-CBC3-SHA"; -openssl_suite_name( ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA) -> - "DHE-RSA-AES128-SHA"; -openssl_suite_name(?TLS_DHE_DSS_WITH_AES_128_CBC_SHA) -> - "DHE-DSS-AES128-SHA"; -openssl_suite_name(?TLS_RSA_WITH_AES_128_CBC_SHA) -> - "AES128-SHA"; -openssl_suite_name(?TLS_RSA_WITH_RC4_128_SHA) -> - "RC4-SHA"; -openssl_suite_name(?TLS_RSA_WITH_RC4_128_MD5) -> - "RC4-MD5"; -openssl_suite_name(?TLS_DHE_RSA_WITH_DES_CBC_SHA) -> - "EDH-RSA-DES-CBC-SHA"; -openssl_suite_name(?TLS_RSA_WITH_DES_CBC_SHA) -> - "DES-CBC-SHA"; -openssl_suite_name(?TLS_RSA_WITH_NULL_SHA256) -> - "NULL-SHA256"; -openssl_suite_name(?TLS_RSA_WITH_AES_128_CBC_SHA256) -> - "AES128-SHA256"; -openssl_suite_name(?TLS_RSA_WITH_AES_256_CBC_SHA256) -> - "AES256-SHA256"; -openssl_suite_name(?TLS_DH_DSS_WITH_AES_128_CBC_SHA256) -> - "DH-DSS-AES128-SHA256"; -openssl_suite_name(?TLS_DH_RSA_WITH_AES_128_CBC_SHA256) -> - "DH-RSA-AES128-SHA256"; -openssl_suite_name(?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256) -> - "DHE-DSS-AES128-SHA256"; -openssl_suite_name(?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256) -> - "DHE-RSA-AES128-SHA256"; -openssl_suite_name(?TLS_DH_DSS_WITH_AES_256_CBC_SHA256) -> - "DH-DSS-AES256-SHA256"; -openssl_suite_name(?TLS_DH_RSA_WITH_AES_256_CBC_SHA256) -> - "DH-RSA-AES256-SHA256"; -openssl_suite_name(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256) -> - "DHE-DSS-AES256-SHA256"; -openssl_suite_name(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256) -> - "DHE-RSA-AES256-SHA256"; +openssl_cipher_name(Kex, "AES_128_CBC" ++ _ = CipherStr) when Kex == rsa; + Kex == dhe_rsa; + Kex == ecdhe_rsa; + Kex == ecdhe_ecdsa -> + openssl_name_concat(CipherStr); +openssl_cipher_name(Kex, "AES_256_CBC" ++ _ = CipherStr) when Kex == rsa; + Kex == dhe_rsa; + Kex == ecdhe_rsa; + Kex == ecdhe_ecdsa -> + openssl_name_concat(CipherStr); +openssl_cipher_name(Kex, "AES_128_CBC" ++ _ = CipherStr) when Kex == srp; + Kex == srp_rsa -> + lists:append(string:replace(CipherStr, "_", "-", all)); +openssl_cipher_name(Kex, "AES_256_CBC" ++ _ = CipherStr) when Kex == srp; + Kex == srp_rsa -> + lists:append(string:replace(CipherStr, "_", "-", all)); +openssl_cipher_name(_, "AES_128_CBC" ++ _ = CipherStr) -> + openssl_name_concat(CipherStr) ++ "-CBC"; +openssl_cipher_name(_, "AES_256_CBC" ++ _ = CipherStr) -> + openssl_name_concat(CipherStr) ++ "-CBC"; +openssl_cipher_name(_, "AES_128_GCM" ++ _ = CipherStr) -> + openssl_name_concat(CipherStr) ++ "-GCM"; +openssl_cipher_name(_, "AES_256_GCM" ++ _ = CipherStr) -> + openssl_name_concat(CipherStr) ++ "-GCM"; +openssl_cipher_name(_, "RC4" ++ _) -> + "RC4"; +openssl_cipher_name(_, CipherStr) -> + lists:append(string:replace(CipherStr, "_", "-", all)). -%%% PSK Cipher Suites RFC 4279 -openssl_suite_name(?TLS_PSK_WITH_AES_256_CBC_SHA) -> - "PSK-AES256-CBC-SHA"; -openssl_suite_name(?TLS_PSK_WITH_3DES_EDE_CBC_SHA) -> - "PSK-3DES-EDE-CBC-SHA"; -openssl_suite_name(?TLS_PSK_WITH_AES_128_CBC_SHA) -> - "PSK-AES128-CBC-SHA"; -openssl_suite_name(?TLS_PSK_WITH_RC4_128_SHA) -> - "PSK-RC4-SHA"; +openssl_suite_start(Kex) -> + case openssl_kex_name(Kex) of + "" -> + ""; + Name -> + Name ++ "-" + end. -%%% SRP Cipher Suites RFC 5054 +openssl_kex_name("RSA") -> + ""; +openssl_kex_name(Kex) -> + lists:append(string:replace(Kex, "_", "-", all)). -openssl_suite_name(?TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA) -> - "SRP-RSA-3DES-EDE-CBC-SHA"; -openssl_suite_name(?TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA) -> - "SRP-DSS-3DES-EDE-CBC-SHA"; -openssl_suite_name(?TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA) -> - "SRP-RSA-AES-128-CBC-SHA"; -openssl_suite_name(?TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA) -> - "SRP-DSS-AES-128-CBC-SHA"; -openssl_suite_name(?TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA) -> - "SRP-RSA-AES-256-CBC-SHA"; -openssl_suite_name(?TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA) -> - "SRP-DSS-AES-256-CBC-SHA"; +kex_name_from_openssl(Kex) -> + lists:append(string:replace(Kex, "-", "_", all)). -%% RFC 4492 EC TLS suites -openssl_suite_name(?TLS_ECDH_ECDSA_WITH_RC4_128_SHA) -> - "ECDH-ECDSA-RC4-SHA"; -openssl_suite_name(?TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA) -> - "ECDH-ECDSA-DES-CBC3-SHA"; -openssl_suite_name(?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA) -> - "ECDH-ECDSA-AES128-SHA"; -openssl_suite_name(?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA) -> - "ECDH-ECDSA-AES256-SHA"; +cipher_name_from_openssl("AES128") -> + "AES_128_CBC"; +cipher_name_from_openssl("AES256") -> + "AES_256_CBC"; +cipher_name_from_openssl("AES128-CBC") -> + "AES_128_CBC"; +cipher_name_from_openssl("AES256-CBC") -> + "AES_256_CBC"; +cipher_name_from_openssl("AES-128-CBC") -> + "AES_128_CBC"; +cipher_name_from_openssl("AES-256-CBC") -> + "AES_256_CBC"; +cipher_name_from_openssl("AES128-GCM") -> + "AES_128_GCM"; +cipher_name_from_openssl("AES256-GCM") -> + "AES_256_GCM"; +cipher_name_from_openssl("RC4") -> + "RC4_128"; +cipher_name_from_openssl(Str) -> + Str. -openssl_suite_name(?TLS_ECDHE_ECDSA_WITH_RC4_128_SHA) -> - "ECDHE-ECDSA-RC4-SHA"; -openssl_suite_name(?TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA) -> - "ECDHE-ECDSA-DES-CBC3-SHA"; -openssl_suite_name(?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA) -> - "ECDHE-ECDSA-AES128-SHA"; -openssl_suite_name(?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA) -> - "ECDHE-ECDSA-AES256-SHA"; +openssl_name_concat(Str0) -> + [Str, _] = string:split(Str0, "_", trailing), + [Part1, Part2] = string:split(Str, "_", trailing), + Part1 ++ Part2. -openssl_suite_name(?TLS_ECDH_RSA_WITH_RC4_128_SHA) -> - "ECDH-RSA-RC4-SHA"; -openssl_suite_name(?TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA) -> - "ECDH-RSA-DES-CBC3-SHA"; -openssl_suite_name(?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA) -> - "ECDH-RSA-AES128-SHA"; -openssl_suite_name(?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA) -> - "ECDH-RSA-AES256-SHA"; -openssl_suite_name(?TLS_ECDHE_RSA_WITH_RC4_128_SHA) -> - "ECDHE-RSA-RC4-SHA"; -openssl_suite_name(?TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA) -> - "ECDHE-RSA-DES-CBC3-SHA"; -openssl_suite_name(?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA) -> - "ECDHE-RSA-AES128-SHA"; -openssl_suite_name(?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA) -> - "ECDHE-RSA-AES256-SHA"; +suite_openssl_str_to_map(Kex0, Rest) -> + Kex = algo_str_to_atom(kex_name_from_openssl(Kex0)), + [CipherStr, AlgStr] = string:split(Rest, "-", trailing), + {Cipher, Mac, Prf} = openssl_cipher_str_to_algs(CipherStr, AlgStr), + #{key_exchange => Kex, + mac => Mac, + cipher => Cipher, + prf => Prf + }. -%% RFC 5289 EC TLS suites -openssl_suite_name(?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256) -> - "ECDHE-ECDSA-AES128-SHA256"; -openssl_suite_name(?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384) -> - "ECDHE-ECDSA-AES256-SHA384"; -openssl_suite_name(?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256) -> - "ECDH-ECDSA-AES128-SHA256"; -openssl_suite_name(?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384) -> - "ECDH-ECDSA-AES256-SHA384"; -openssl_suite_name(?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256) -> - "ECDHE-RSA-AES128-SHA256"; -openssl_suite_name(?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384) -> - "ECDHE-RSA-AES256-SHA384"; -openssl_suite_name(?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256) -> - "ECDH-RSA-AES128-SHA256"; -openssl_suite_name(?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384) -> - "ECDH-RSA-AES256-SHA384"; +%% Does only need own implementation PRE TLS 1.3 +openssl_cipher_str_to_algs(CipherStr, "CCM"= End) -> + Cipher = algo_str_to_atom(CipherStr ++ "_" ++ End), + {Cipher, aead, sha256}; +openssl_cipher_str_to_algs(CipherStr, "8" = End) -> + Cipher = algo_str_to_atom(CipherStr ++ "_" ++ End), + {Cipher, aead, sha256}; +openssl_cipher_str_to_algs(CipherStr, "POLY1305" = End) -> + Cipher = algo_str_to_atom(CipherStr ++ "_" ++ End), + {Cipher, aead, sha256}; +openssl_cipher_str_to_algs(CipherStr, HashStr) -> + Hash = algo_str_to_atom(HashStr), + Cipher = algo_str_to_atom(cipher_name_from_openssl(CipherStr)), + case openssl_is_aead_cipher(CipherStr) of + true -> + {Cipher, aead, Hash}; + false -> + {Cipher, Hash, openssl_prf(Hash)} + end. + +openssl_prf(sha256)-> + sha256; +openssl_prf(sha384) -> + sha384; +openssl_prf(_) -> + default_prf. -%% RFC 5288 AES-GCM Cipher Suites -openssl_suite_name(?TLS_RSA_WITH_AES_128_GCM_SHA256) -> - "AES128-GCM-SHA256"; -openssl_suite_name(?TLS_RSA_WITH_AES_256_GCM_SHA384) -> - "AES256-GCM-SHA384"; -openssl_suite_name(?TLS_DHE_RSA_WITH_AES_128_GCM_SHA256) -> - "DHE-RSA-AES128-GCM-SHA256"; -openssl_suite_name(?TLS_DHE_RSA_WITH_AES_256_GCM_SHA384) -> - "DHE-RSA-AES256-GCM-SHA384"; -openssl_suite_name(?TLS_DH_RSA_WITH_AES_128_GCM_SHA256) -> - "DH-RSA-AES128-GCM-SHA256"; -openssl_suite_name(?TLS_DH_RSA_WITH_AES_256_GCM_SHA384) -> - "DH-RSA-AES256-GCM-SHA384"; -openssl_suite_name(?TLS_DHE_DSS_WITH_AES_128_GCM_SHA256) -> - "DHE-DSS-AES128-GCM-SHA256"; -openssl_suite_name(?TLS_DHE_DSS_WITH_AES_256_GCM_SHA384) -> - "DHE-DSS-AES256-GCM-SHA384"; -openssl_suite_name(?TLS_DH_DSS_WITH_AES_128_GCM_SHA256) -> - "DH-DSS-AES128-GCM-SHA256"; -openssl_suite_name(?TLS_DH_DSS_WITH_AES_256_GCM_SHA384) -> - "DH-DSS-AES256-GCM-SHA384"; -%% RFC 5289 ECC AES-GCM Cipher Suites -openssl_suite_name(?TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256) -> - "ECDHE-ECDSA-AES128-GCM-SHA256"; -openssl_suite_name(?TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384) -> - "ECDHE-ECDSA-AES256-GCM-SHA384"; -openssl_suite_name(?TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256) -> - "ECDH-ECDSA-AES128-GCM-SHA256"; -openssl_suite_name(?TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384) -> - "ECDH-ECDSA-AES256-GCM-SHA384"; -openssl_suite_name(?TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) -> - "ECDHE-RSA-AES128-GCM-SHA256"; -openssl_suite_name(?TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) -> - "ECDHE-RSA-AES256-GCM-SHA384"; -openssl_suite_name(?TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256) -> - "ECDH-RSA-AES128-GCM-SHA256"; -openssl_suite_name(?TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384) -> - "ECDH-RSA-AES256-GCM-SHA384"; -%% No oppenssl name -openssl_suite_name(Cipher) -> - suite_definition(Cipher). diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index ad81288f64..a5f754d2e3 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -35,10 +35,11 @@ -include("ssl_internal.hrl"). -include("ssl_srp.hrl"). -include_lib("public_key/include/public_key.hrl"). +-include_lib("kernel/include/logger.hrl"). %% Setup --export([connect/8, handshake/7, handshake/2, handshake/3, +-export([connect/8, handshake/7, handshake/2, handshake/3, handle_common_event/5, handshake_continue/3, handshake_cancel/1, socket_control/4, socket_control/5]). @@ -59,7 +60,7 @@ %% Help functions for tls|dtls_connection.erl -export([handle_session/7, ssl_config/3, - prepare_connection/2, hibernate_after/3, map_extensions/1]). + prepare_connection/2, hibernate_after/3]). %% General gen_statem state functions with extra callback argument %% to determine if it is an SSL/TLS or DTLS gen_statem machine @@ -114,7 +115,7 @@ handshake(Connection, Port, Socket, Opts, User, CbInfo, Timeout) -> %%-------------------------------------------------------------------- -spec handshake(#sslsocket{}, timeout()) -> {ok, #sslsocket{}} | - {ok, #sslsocket{}, map()}| {error, reason()}. + {ok, #sslsocket{}, map()}| {error, reason()}. %% %% Description: Starts ssl handshake. %%-------------------------------------------------------------------- @@ -129,8 +130,8 @@ handshake(#sslsocket{pid = [Pid|_]} = Socket, Timeout) -> end. %%-------------------------------------------------------------------- --spec handshake(#sslsocket{}, {#ssl_options{},#socket_options{}}, - timeout()) -> {ok, #sslsocket{}} | {error, reason()}. +-spec handshake(#sslsocket{}, {#ssl_options{},#socket_options{}}, timeout()) -> + {ok, #sslsocket{}} | {ok, #sslsocket{}, map()} | {error, reason()}. %% %% Description: Starts ssl handshake with some new options %%-------------------------------------------------------------------- @@ -337,7 +338,9 @@ handle_own_alert(Alert, _, StateName, ignore end, try %% Try to tell the local user - log_alert(SslOpts#ssl_options.log_alert, Role, Connection:protocol_name(), StateName, Alert#alert{role = Role}), + log_alert(SslOpts#ssl_options.log_level, Role, + Connection:protocol_name(), StateName, + Alert#alert{role = Role}), handle_normal_shutdown(Alert,StateName, State) catch _:_ -> ok @@ -379,10 +382,11 @@ handle_alert(#alert{level = ?FATAL} = Alert, StateName, session = Session, socket_options = Opts} = State) -> invalidate_session(Role, Host, Port, Session), - log_alert(SslOpts#ssl_options.log_alert, Role, Connection:protocol_name(), + log_alert(SslOpts#ssl_options.log_level, Role, Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}), Pids = Connection:pids(State), - alert_user(Pids, Transport, Tracker, Socket, StateName, Opts, Pid, From, Alert, Role, Connection), + alert_user(Pids, Transport, Tracker, Socket, StateName, Opts, Pid, From, Alert, + opposite_role(Role), Connection), {stop, {shutdown, normal}, State}; handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert, @@ -397,7 +401,7 @@ handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, protocol_cb = Connection}, handshake_env = #handshake_env{renegotiation = {true, internal}}, ssl_options = SslOpts} = State) -> - log_alert(SslOpts#ssl_options.log_alert, Role, + log_alert(SslOpts#ssl_options.log_level, Role, Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}), handle_normal_shutdown(Alert, StateName, State), {stop,{shutdown, peer_close}, State}; @@ -408,7 +412,7 @@ handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, handshake_env = #handshake_env{renegotiation = {true, From}} = HsEnv, ssl_options = SslOpts } = State0) -> - log_alert(SslOpts#ssl_options.log_alert, Role, + log_alert(SslOpts#ssl_options.log_level, Role, Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}), gen_statem:reply(From, {error, renegotiation_rejected}), State = Connection:reinit_handshake_data(State0), @@ -419,8 +423,8 @@ handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, protocol_cb = Connection}, handshake_env = #handshake_env{renegotiation = {true, From}} = HsEnv, ssl_options = SslOpts - } = State0) -> - log_alert(SslOpts#ssl_options.log_alert, Role, + } = State0) -> + log_alert(SslOpts#ssl_options.log_level, Role, Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}), gen_statem:reply(From, {error, renegotiation_rejected}), %% Go back to connection! @@ -432,8 +436,9 @@ handle_alert(#alert{level = ?WARNING} = Alert, StateName, #state{static_env = #static_env{role = Role, protocol_cb = Connection}, ssl_options = SslOpts} = State) -> - log_alert(SslOpts#ssl_options.log_alert, Role, - Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}), + log_alert(SslOpts#ssl_options.log_level, Role, + Connection:protocol_name(), StateName, + Alert#alert{role = opposite_role(Role)}), Connection:next_event(StateName, no_record, State). %%==================================================================== @@ -442,8 +447,7 @@ handle_alert(#alert{level = ?WARNING} = Alert, StateName, passive_receive(State0 = #state{user_data_buffer = {_,BufferSize,_}}, StateName, Connection, StartTimerAction) -> case BufferSize of 0 -> - {Record, State} = Connection:next_record(State0), - Connection:next_event(StateName, Record, State, StartTimerAction); + Connection:next_event(StateName, no_record, State0, StartTimerAction); _ -> case read_application_data(<<>>, State0) of {stop, _, _} = ShutdownError -> @@ -699,7 +703,7 @@ handle_session(#server_hello{cipher_suite = CipherSuite, handshake_env = #handshake_env{negotiated_protocol = CurrentProtocol} = HsEnv, connection_env = #connection_env{negotiated_version = ReqVersion} = CEnv} = State0) -> #{key_exchange := KeyAlgorithm} = - ssl_cipher_format:suite_definition(CipherSuite), + ssl_cipher_format:suite_bin_to_map(CipherSuite), PremasterSecret = make_premaster_secret(ReqVersion, KeyAlgorithm), @@ -1022,7 +1026,8 @@ certify(internal, #certificate_request{} = CertRequest, connection_env = #connection_env{negotiated_version = Version}, session = #session{own_certificate = Cert}, ssl_options = #ssl_options{signature_algs = SupportedHashSigns}} = State, 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, State); NegotiatedHashSign -> @@ -1188,10 +1193,8 @@ cipher(internal, #finished{verify_data = Data} = Finished, cipher(internal, #next_protocol{selected_protocol = SelectedProtocol}, #state{static_env = #static_env{role = server}, handshake_env = #handshake_env{expecting_finished = true, - expecting_next_protocol_negotiation = true} = HsEnv} = State0, Connection) -> - {Record, State} = - Connection:next_record(State0), - Connection:next_event(?FUNCTION_NAME, Record, + expecting_next_protocol_negotiation = true} = HsEnv} = State, Connection) -> + Connection:next_event(?FUNCTION_NAME, no_record, State#state{handshake_env = HsEnv#handshake_env{negotiated_protocol = SelectedProtocol, expecting_next_protocol_negotiation = false}}); cipher(internal, #change_cipher_spec{type = <<1>>}, #state{handshake_env = HsEnv, connection_states = ConnectionStates0} = @@ -1313,10 +1316,10 @@ handle_common_event({timeout, handshake}, close, _StateName, #state{start_or_rec handle_common_event({timeout, recv}, timeout, StateName, #state{start_or_recv_from = RecvFrom} = State, _) -> {next_state, StateName, State#state{start_or_recv_from = undefined, bytes_to_read = undefined}, [{reply, RecvFrom, {error, timeout}}]}; -handle_common_event(_Type, Msg, StateName, #state{connection_env = +handle_common_event(Type, Msg, StateName, #state{connection_env = #connection_env{negotiated_version = Version}} = State, _) -> - Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE, {unexpected_msg, Msg}), + Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE, {unexpected_msg, {Type,Msg}}), handle_own_alert(Alert, Version, StateName, State). handle_call({application_data, _Data}, _, _, _, _) -> @@ -1448,7 +1451,7 @@ handle_info({ErrorTag, Socket, econnaborted}, StateName, handle_info({ErrorTag, Socket, Reason}, StateName, #state{static_env = #static_env{socket = Socket, error_tag = ErrorTag}} = State) -> Report = io_lib:format("SSL: Socket error: ~p ~n", [Reason]), - error_logger:error_report(Report), + ?LOG_ERROR(Report), handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State), {stop, {shutdown,normal}, State}; @@ -1478,7 +1481,7 @@ handle_info(allow_renegotiate, StateName, #state{handshake_env = HsEnv} = State) handle_info(Msg, StateName, #state{static_env = #static_env{socket = Socket, error_tag = Tag}} = State) -> Report = io_lib:format("SSL: Got unexpected info: ~p ~n", [{Msg, Tag, Socket}]), - error_logger:info_report(Report), + ?LOG_NOTICE(Report), {next_state, StateName, State}. %%==================================================================== @@ -1570,7 +1573,7 @@ connection_info(#state{static_env = #static_env{protocol_cb = Connection}, connection_env = #connection_env{negotiated_version = {_,_} = Version}, ssl_options = Opts}) -> RecordCB = record_cb(Connection), - CipherSuiteDef = #{key_exchange := KexAlg} = ssl_cipher_format:suite_definition(CipherSuite), + CipherSuiteDef = #{key_exchange := KexAlg} = ssl_cipher_format:suite_bin_to_map(CipherSuite), IsNamedCurveSuite = lists:member(KexAlg, [ecdh_ecdsa, ecdhe_ecdsa, ecdh_rsa, ecdhe_rsa, ecdh_anon]), CurveInfo = case ECCCurve of @@ -1581,7 +1584,7 @@ connection_info(#state{static_env = #static_env{protocol_cb = Connection}, end, [{protocol, RecordCB:protocol_version(Version)}, {session_id, SessionId}, - {cipher_suite, ssl_cipher_format:erl_suite_definition(CipherSuiteDef)}, + {cipher_suite, ssl_cipher_format:suite_legacy(CipherSuiteDef)}, {selected_cipher_suite, CipherSuiteDef}, {sni_hostname, SNIHostname} | CurveInfo] ++ ssl_options_list(Opts). @@ -1593,18 +1596,23 @@ security_info(#state{connection_states = ConnectionStates}) -> ssl_record:current_connection_state(ConnectionStates, read), [{client_random, ClientRand}, {server_random, ServerRand}, {master_secret, MasterSecret}]. -do_server_hello(Type, #hello_extensions{next_protocol_negotiation = NextProtocols} = +do_server_hello(Type, #{next_protocol_negotiation := NextProtocols} = ServerHelloExt, #state{connection_env = #connection_env{negotiated_version = Version}, handshake_env = HsEnv, session = #session{session_id = SessId}, - connection_states = ConnectionStates0} + connection_states = ConnectionStates0, + ssl_options = #ssl_options{versions = [HighestVersion|_]}} = State0, Connection) when is_atom(Type) -> - + %% TLS 1.3 - Section 4.1.3 + %% Override server random values for TLS 1.3 downgrade protection mechanism. + ConnectionStates1 = update_server_random(ConnectionStates0, Version, HighestVersion), + State1 = State0#state{connection_states = ConnectionStates1}, ServerHello = - ssl_handshake:server_hello(SessId, ssl:tls_version(Version), ConnectionStates0, ServerHelloExt), + ssl_handshake:server_hello(SessId, ssl:tls_version(Version), + ConnectionStates1, ServerHelloExt), State = server_hello(ServerHello, - State0#state{handshake_env = HsEnv#handshake_env{expecting_next_protocol_negotiation = + State1#state{handshake_env = HsEnv#handshake_env{expecting_next_protocol_negotiation = NextProtocols =/= undefined}}, Connection), case Type of new -> @@ -1613,6 +1621,60 @@ do_server_hello(Type, #hello_extensions{next_protocol_negotiation = NextProtocol resumed_server_hello(State, Connection) end. +update_server_random(#{pending_read := #{security_parameters := ReadSecParams0} = + ReadState0, + pending_write := #{security_parameters := WriteSecParams0} = + WriteState0} = ConnectionStates, + Version, HighestVersion) -> + ReadRandom = override_server_random( + ReadSecParams0#security_parameters.server_random, + Version, + HighestVersion), + WriteRandom = override_server_random( + WriteSecParams0#security_parameters.server_random, + Version, + HighestVersion), + ReadSecParams = ReadSecParams0#security_parameters{server_random = ReadRandom}, + WriteSecParams = WriteSecParams0#security_parameters{server_random = WriteRandom}, + ReadState = ReadState0#{security_parameters => ReadSecParams}, + WriteState = WriteState0#{security_parameters => WriteSecParams}, + + ConnectionStates#{pending_read => ReadState, pending_write => WriteState}. + +%% TLS 1.3 - Section 4.1.3 +%% +%% If negotiating TLS 1.2, TLS 1.3 servers MUST set the last eight bytes +%% of their Random value to the bytes: +%% +%% 44 4F 57 4E 47 52 44 01 +%% +%% If negotiating TLS 1.1 or below, TLS 1.3 servers MUST and TLS 1.2 +%% servers SHOULD set the last eight bytes of their Random value to the +%% bytes: +%% +%% 44 4F 57 4E 47 52 44 00 +override_server_random(<<Random0:24/binary,_:8/binary>> = Random, {M,N}, {Major,Minor}) + when Major > 3 orelse Major =:= 3 andalso Minor >= 4 -> %% TLS 1.3 or above + if M =:= 3 andalso N =:= 3 -> %% Negotating TLS 1.2 + Down = ?RANDOM_OVERRIDE_TLS12, + <<Random0/binary,Down/binary>>; + M =:= 3 andalso N < 3 -> %% Negotating TLS 1.1 or prior + Down = ?RANDOM_OVERRIDE_TLS11, + <<Random0/binary,Down/binary>>; + true -> + Random + end; +override_server_random(<<Random0:24/binary,_:8/binary>> = Random, {M,N}, {Major,Minor}) + when Major =:= 3 andalso Minor =:= 3 -> %% TLS 1.2 + if M =:= 3 andalso N < 3 -> %% Negotating TLS 1.1 or prior + Down = ?RANDOM_OVERRIDE_TLS11, + <<Random0/binary,Down/binary>>; + true -> + Random + end; +override_server_random(Random, _, _) -> + Random. + new_server_hello(#server_hello{cipher_suite = CipherSuite, compression_method = Compression, session_id = SessionId}, @@ -1649,7 +1711,7 @@ resumed_server_hello(#state{session = Session, server_hello(ServerHello, State0, Connection) -> CipherSuite = ServerHello#server_hello.cipher_suite, - #{key_exchange := KeyAlgorithm} = ssl_cipher_format:suite_definition(CipherSuite), + #{key_exchange := KeyAlgorithm} = ssl_cipher_format:suite_bin_to_map(CipherSuite), #state{handshake_env = HsEnv} = State = Connection:queue_handshake(ServerHello, State0), State#state{handshake_env = HsEnv#handshake_env{kex_algorithm = KeyAlgorithm}}. @@ -1664,7 +1726,7 @@ handle_peer_cert(Role, PeerCert, PublicKeyInfo, State1 = State0#state{handshake_env = HsEnv#handshake_env{public_key_info = PublicKeyInfo}, session = Session#session{peer_certificate = PeerCert}}, - #{key_exchange := KeyAlgorithm} = ssl_cipher_format:suite_definition(CipherSuite), + #{key_exchange := KeyAlgorithm} = ssl_cipher_format:suite_bin_to_map(CipherSuite), State = handle_peer_cert_key(Role, PeerCert, PublicKeyInfo, KeyAlgorithm, State1), Connection:next_event(certify, no_record, State). @@ -2505,22 +2567,6 @@ hibernate_after(connection = StateName, hibernate_after(StateName, State, Actions) -> {next_state, StateName, State, Actions}. -map_extensions(#hello_extensions{renegotiation_info = RenegotiationInfo, - signature_algs = SigAlg, - alpn = Alpn, - next_protocol_negotiation = Next, - srp = SRP, - ec_point_formats = ECPointFmt, - elliptic_curves = ECCCurves, - sni = SNI}) -> - #{renegotiation_info => ssl_handshake:extension_value(RenegotiationInfo), - signature_algs => ssl_handshake:extension_value(SigAlg), - alpn => ssl_handshake:extension_value(Alpn), - srp => ssl_handshake:extension_value(SRP), - next_protocol => ssl_handshake:extension_value(Next), - ec_point_formats => ssl_handshake:extension_value(ECPointFmt), - elliptic_curves => ssl_handshake:extension_value(ECCCurves), - sni => ssl_handshake:extension_value(SNI)}. terminate_alert(normal) -> ?ALERT_REC(?WARNING, ?CLOSE_NOTIFY); @@ -2682,7 +2728,7 @@ ssl_options_list([ciphers = Key | Keys], [Value | Values], Acc) -> ssl_options_list(Keys, Values, [{Key, lists:map( fun(Suite) -> - ssl_cipher_format:suite_definition(Suite) + ssl_cipher_format:suite_bin_to_map(Suite) end, Value)} | Acc]); ssl_options_list([Key | Keys], [Value | Values], Acc) -> @@ -2885,14 +2931,14 @@ alert_user(Pids, Transport, Tracker, Socket, Active, Pid, From, Alert, Role, Con {ssl_error, Connection:socket(Pids, Transport, Socket, Tracker), ReasonCode}) end. -log_alert(true, Role, ProtocolName, StateName, #alert{role = Role} = Alert) -> +log_alert(Level, Role, ProtocolName, StateName, #alert{role = Role} = Alert) -> Txt = ssl_alert:own_alert_txt(Alert), - error_logger:info_report(io_lib:format("~s ~p: In state ~p ~s\n", [ProtocolName, Role, StateName, Txt])); -log_alert(true, Role, ProtocolName, StateName, Alert) -> + Report = io_lib:format("~s ~p: In state ~p ~s\n", [ProtocolName, Role, StateName, Txt]), + ssl_logger:notice(Level, Report); +log_alert(Level, Role, ProtocolName, StateName, Alert) -> Txt = ssl_alert:alert_txt(Alert), - error_logger:info_report(io_lib:format("~s ~p: In state ~p ~s\n", [ProtocolName, Role, StateName, Txt])); -log_alert(false, _, _, _, _) -> - ok. + Report = io_lib:format("~s ~p: In state ~p ~s\n", [ProtocolName, Role, StateName, Txt]), + ssl_logger:notice(Level, Report). invalidate_session(client, Host, Port, Session) -> ssl_manager:invalidate_session(Host, Port, Session); diff --git a/lib/ssl/src/ssl_connection.hrl b/lib/ssl/src/ssl_connection.hrl index c90fe926b7..ff7207a8ce 100644 --- a/lib/ssl/src/ssl_connection.hrl +++ b/lib/ssl/src/ssl_connection.hrl @@ -107,20 +107,77 @@ client_certificate_requested = false :: boolean(), protocol_specific = #{} :: map(), session :: #session{} | secret_printout(), + key_share, %% Data shuffling %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% connection_states :: ssl_record:connection_states() | secret_printout(), protocol_buffers :: term() | secret_printout() , %% #protocol_buffers{} from tls_record.hrl or dtls_recor.hr user_data_buffer :: undefined | {[binary()],non_neg_integer(),[binary()]} | secret_printout(), bytes_to_read :: undefined | integer(), %% bytes to read in passive mode - %% recv and start handling - start_or_recv_from :: term() + start_or_recv_from :: term(), + log_level }). - -define(DEFAULT_DIFFIE_HELLMAN_PARAMS, #'DHParameter'{prime = ?DEFAULT_DIFFIE_HELLMAN_PRIME, base = ?DEFAULT_DIFFIE_HELLMAN_GENERATOR}). -define(WAIT_TO_ALLOW_RENEGOTIATION, 12000). + +%%---------------------------------------------------------------------- +%% TLS 1.3 +%%---------------------------------------------------------------------- + +%% TLS 1.3 uses the same state record with the following differences: +%% +%% state :: record() +%% +%% session_cache - not implemented +%% session_cache_cb - not implemented +%% crl_db - not implemented +%% client_hello_version - Bleichenbacher mitigation in TLS 1.2 +%% client_certificate_requested - Built into TLS 1.3 state machine +%% key_algorithm - not used +%% diffie_hellman_params - used in TLS 1.2 ECDH key exchange +%% diffie_hellman_keys - used in TLS 1.2 ECDH key exchange +%% psk_identity - not used +%% srp_params - not used, no srp extension in TLS 1.3 +%% srp_keys - not used, no srp extension in TLS 1.3 +%% premaster_secret - not used +%% renegotiation - TLS 1.3 forbids renegotiation +%% hello - used in user_hello, handshake continue +%% allow_renegotiate - TLS 1.3 forbids renegotiation +%% expecting_next_protocol_negotiation - ALPN replaced NPN, depricated in TLS 1.3 +%% expecting_finished - not implemented, used by abbreviated +%% next_protocol - ALPN replaced NPN, depricated in TLS 1.3 +%% +%% connection_state :: map() +%% +%% compression_state - not used +%% mac_secret - not used +%% sequence_number - not used +%% secure_renegotiation - not used, no renegotiation_info in TLS 1.3 +%% client_verify_data - not used, no renegotiation_info in TLS 1.3 +%% server_verify_data - not used, no renegotiation_info in TLS 1.3 +%% beast_mitigation - not used +%% +%% security_parameters :: map() +%% +%% cipher_type - TLS 1.3 uses only AEAD ciphers +%% iv_size - not used +%% key_size - not used +%% key_material_length - not used +%% expanded_key_material_length - used in SSL 3.0 +%% mac_algorithm - not used +%% prf_algorithm - not used +%% hash_size - not used +%% compression_algorithm - not used +%% master_secret - used for multiple secret types in TLS 1.3 +%% client_random - not used +%% server_random - not used +%% exportable - not used +%% +%% cipher_state :: record() +%% nonce - used for sequence_number + -endif. % -ifdef(ssl_connection). diff --git a/lib/ssl/src/ssl_crl_hash_dir.erl b/lib/ssl/src/ssl_crl_hash_dir.erl index bb62737232..9478ff9b78 100644 --- a/lib/ssl/src/ssl_crl_hash_dir.erl +++ b/lib/ssl/src/ssl_crl_hash_dir.erl @@ -20,6 +20,7 @@ -module(ssl_crl_hash_dir). -include_lib("public_key/include/public_key.hrl"). +-include_lib("kernel/include/logger.hrl"). -behaviour(ssl_crl_cache_api). @@ -55,7 +56,7 @@ select(Issuer, {_DbHandle, [{dir, Dir}]}) -> %% is happy with that, but if it's true, this is an error. []; {error, Error} -> - error_logger:error_report( + ?LOG_ERROR( [{cannot_find_crl, Error}, {dir, Dir}, {module, ?MODULE}, @@ -86,7 +87,7 @@ find_crls(Issuer, Hash, Dir, N, Acc) -> error:Error -> %% Something is wrong with the file. Report %% it, and try the next one. - error_logger:error_report( + ?LOG_ERROR( [{crl_parse_error, Error}, {filename, Filename}, {module, ?MODULE}, diff --git a/lib/ssl/src/ssl_dh_groups.erl b/lib/ssl/src/ssl_dh_groups.erl new file mode 100644 index 0000000000..20d53de430 --- /dev/null +++ b/lib/ssl/src/ssl_dh_groups.erl @@ -0,0 +1,467 @@ +%% +%% %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). + +-include_lib("public_key/include/public_key.hrl"). + +-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, + dh_params/1]). + +%% 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). + +dh_params(ffdhe2048) -> + #'DHParameter'{ + prime = ffdhe2048_prime(), + base = ffdhe2048_generator()}; +dh_params(ffdhe3072) -> + #'DHParameter'{ + prime = ffdhe3072_prime(), + base = ffdhe3072_generator()}; +dh_params(ffdhe4096) -> + #'DHParameter'{ + prime = ffdhe4096_prime(), + base = ffdhe4096_generator()}; +dh_params(ffdhe6144) -> + #'DHParameter'{ + prime = ffdhe6144_prime(), + base = ffdhe6144_generator()}; +dh_params(ffdhe8192) -> + #'DHParameter'{ + prime = ffdhe8192_prime(), + base = ffdhe8192_generator()}. diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index 9ba62b3a12..7b34991f4f 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -30,6 +30,7 @@ -include("ssl_alert.hrl"). -include("ssl_internal.hrl"). -include("ssl_srp.hrl"). +-include("tls_handshake_1_3.hrl"). -include_lib("public_key/include/public_key.hrl"). -export_type([ssl_handshake/0, ssl_handshake_history/0, @@ -38,7 +39,7 @@ -type oid() :: tuple(). -type public_key_params() :: #'Dss-Parms'{} | {namedCurve, oid()} | #'ECParameters'{} | term(). -type public_key_info() :: {oid(), #'RSAPublicKey'{} | integer() | #'ECPoint'{}, public_key_params()}. --type ssl_handshake_history() :: {[binary()], [binary()]}. +-type ssl_handshake_history() :: {iodata(), iodata()}. -type ssl_handshake() :: #server_hello{} | #server_hello_done{} | #certificate{} | #certificate_request{} | #client_key_exchange{} | #finished{} | #certificate_verify{} | @@ -53,14 +54,14 @@ -export([certify/7, certificate_verify/6, verify_signature/5, master_secret/4, server_key_exchange_hash/2, verify_connection/6, init_handshake_history/0, update_handshake_history/2, verify_server_key/5, - select_version/3, extension_value/1 + select_version/3, select_supported_version/2, extension_value/1 ]). %% Encode --export([encode_handshake/2, encode_hello_extensions/1, +-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_hello_extensions/1, +-export([decode_handshake/3, decode_vector/1, decode_hello_extensions/4, decode_extensions/3, decode_server_key/3, decode_client_key/3, decode_suites/2 ]). @@ -71,13 +72,18 @@ premaster_secret/2, premaster_secret/3, premaster_secret/4]). %% Extensions handling --export([client_hello_extensions/5, +-export([client_hello_extensions/6, 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, add_server_share/3 ]). +-export([get_cert_params/1, + server_name/3, + validation_fun_and_state/9, + handle_path_validation_error/7]). + %%==================================================================== %% Create handshake messages %%==================================================================== @@ -92,8 +98,8 @@ hello_request() -> #hello_request{}. %%-------------------------------------------------------------------- --spec server_hello(#session{}, ssl_record:ssl_version(), ssl_record:connection_states(), - #hello_extensions{}) -> #server_hello{}. +-spec server_hello(binary(), ssl_record:ssl_version(), ssl_record:connection_states(), + Extension::map()) -> #server_hello{}. %% %% Description: Creates a server hello message. %%-------------------------------------------------------------------- @@ -176,7 +182,7 @@ client_certificate_verify(OwnCert, MasterSecret, Version, %% Description: Creates a certificate_request message, called by the server. %%-------------------------------------------------------------------- certificate_request(CipherSuite, CertDbHandle, CertDbRef, HashSigns, Version) -> - Types = certificate_types(ssl_cipher_format:suite_definition(CipherSuite), Version), + Types = certificate_types(ssl_cipher_format:suite_bin_to_map(CipherSuite), Version), Authorities = certificate_authorities(CertDbHandle, CertDbRef), #certificate_request{ certificate_types = Types, @@ -504,6 +510,21 @@ verify_server_key(#server_key_params{params_bin = EncParams, select_version(RecordCB, ClientVersion, Versions) -> do_select_version(RecordCB, ClientVersion, Versions). + +%% Called by TLS 1.2/1.3 Server when "supported_versions" is present +%% in ClientHello. +%% Input lists are ordered (highest first) +select_supported_version([], _ServerVersions) -> + undefined; +select_supported_version([ClientVersion|T], ServerVersions) -> + case lists:member(ClientVersion, ServerVersions) of + true -> + ClientVersion; + false -> + select_supported_version(T, ServerVersions) + end. + + %%==================================================================== %% Encode handshake %%==================================================================== @@ -517,7 +538,7 @@ encode_handshake(#server_hello{server_version = {Major, Minor}, session_id = Session_ID, cipher_suite = CipherSuite, compression_method = Comp_method, - extensions = #hello_extensions{} = Extensions}, _Version) -> + extensions = Extensions}, _Version) -> SID_length = byte_size(Session_ID), ExtensionsBin = encode_hello_extensions(Extensions), {?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, @@ -567,71 +588,126 @@ encode_handshake(#certificate_verify{signature = BinSig, hashsign_algorithm = Ha encode_handshake(#finished{verify_data = VerifyData}, _Version) -> {?FINISHED, VerifyData}. -encode_hello_extensions(#hello_extensions{} = Extensions) -> - encode_hello_extensions(hello_extensions_list(Extensions), <<>>). -encode_hello_extensions([], <<>>) -> - <<>>; -encode_hello_extensions([], Acc) -> +encode_hello_extensions(Extensions) -> + encode_extensions(hello_extensions_list(Extensions), <<>>). + +encode_extensions(Exts) -> + encode_extensions(Exts, <<>>). + +encode_extensions([], <<>>) -> + <<?UINT16(0)>>; +encode_extensions([], Acc) -> Size = byte_size(Acc), <<?UINT16(Size), Acc/binary>>; - -encode_hello_extensions([#alpn{extension_data = ExtensionData} | Rest], Acc) -> - Len = byte_size(ExtensionData), +encode_extensions([#alpn{extension_data = ExtensionData} | Rest], Acc) -> + Len = byte_size(ExtensionData), ExtLen = Len + 2, - encode_hello_extensions(Rest, <<?UINT16(?ALPN_EXT), ?UINT16(ExtLen), ?UINT16(Len), - ExtensionData/binary, Acc/binary>>); -encode_hello_extensions([#next_protocol_negotiation{extension_data = ExtensionData} | Rest], Acc) -> + encode_extensions(Rest, <<?UINT16(?ALPN_EXT), ?UINT16(ExtLen), ?UINT16(Len), + ExtensionData/binary, Acc/binary>>); +encode_extensions([#next_protocol_negotiation{extension_data = ExtensionData} | Rest], Acc) -> Len = byte_size(ExtensionData), - encode_hello_extensions(Rest, <<?UINT16(?NEXTPROTONEG_EXT), ?UINT16(Len), + encode_extensions(Rest, <<?UINT16(?NEXTPROTONEG_EXT), ?UINT16(Len), ExtensionData/binary, Acc/binary>>); -encode_hello_extensions([#renegotiation_info{renegotiated_connection = undefined} | Rest], Acc) -> - encode_hello_extensions(Rest, Acc); -encode_hello_extensions([#renegotiation_info{renegotiated_connection = ?byte(0) = Info} | Rest], Acc) -> +encode_extensions([#renegotiation_info{renegotiated_connection = undefined} | Rest], Acc) -> + encode_extensions(Rest, Acc); +encode_extensions([#renegotiation_info{renegotiated_connection = ?byte(0) = Info} | Rest], Acc) -> Len = byte_size(Info), - encode_hello_extensions(Rest, <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), Info/binary, Acc/binary>>); + encode_extensions(Rest, <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), Info/binary, Acc/binary>>); -encode_hello_extensions([#renegotiation_info{renegotiated_connection = Info} | Rest], Acc) -> +encode_extensions([#renegotiation_info{renegotiated_connection = Info} | Rest], Acc) -> InfoLen = byte_size(Info), Len = InfoLen +1, - encode_hello_extensions(Rest, <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), ?BYTE(InfoLen), + encode_extensions(Rest, <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), ?BYTE(InfoLen), Info/binary, Acc/binary>>); -encode_hello_extensions([#elliptic_curves{elliptic_curve_list = EllipticCurves} | Rest], Acc) -> +encode_extensions([#elliptic_curves{elliptic_curve_list = EllipticCurves} | Rest], Acc) -> EllipticCurveList = << <<(tls_v1:oid_to_enum(X)):16>> || X <- EllipticCurves>>, ListLen = byte_size(EllipticCurveList), Len = ListLen + 2, - encode_hello_extensions(Rest, <<?UINT16(?ELLIPTIC_CURVES_EXT), + encode_extensions(Rest, <<?UINT16(?ELLIPTIC_CURVES_EXT), ?UINT16(Len), ?UINT16(ListLen), EllipticCurveList/binary, Acc/binary>>); -encode_hello_extensions([#ec_point_formats{ec_point_format_list = ECPointFormats} | Rest], Acc) -> +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), Len = ListLen + 1, - encode_hello_extensions(Rest, <<?UINT16(?EC_POINT_FORMATS_EXT), + encode_extensions(Rest, <<?UINT16(?EC_POINT_FORMATS_EXT), ?UINT16(Len), ?BYTE(ListLen), ECPointFormatList/binary, Acc/binary>>); -encode_hello_extensions([#srp{username = UserName} | Rest], Acc) -> +encode_extensions([#srp{username = UserName} | Rest], Acc) -> SRPLen = byte_size(UserName), Len = SRPLen + 1, - encode_hello_extensions(Rest, <<?UINT16(?SRP_EXT), ?UINT16(Len), ?BYTE(SRPLen), + encode_extensions(Rest, <<?UINT16(?SRP_EXT), ?UINT16(Len), ?BYTE(SRPLen), UserName/binary, Acc/binary>>); -encode_hello_extensions([#hash_sign_algos{hash_sign_algos = HashSignAlgos} | Rest], Acc) -> +encode_extensions([#hash_sign_algos{hash_sign_algos = HashSignAlgos} | Rest], Acc) -> SignAlgoList = << <<(ssl_cipher:hash_algorithm(Hash)):8, (ssl_cipher:sign_algorithm(Sign)):8>> || {Hash, Sign} <- HashSignAlgos >>, ListLen = byte_size(SignAlgoList), Len = ListLen + 2, - encode_hello_extensions(Rest, <<?UINT16(?SIGNATURE_ALGORITHMS_EXT), + encode_extensions(Rest, <<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len), ?UINT16(ListLen), SignAlgoList/binary, Acc/binary>>); -encode_hello_extensions([#sni{hostname = Hostname} | Rest], Acc) -> +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 >>, + ListLen = byte_size(SignSchemeList), + Len = ListLen + 2, + encode_extensions(Rest, <<?UINT16(?SIGNATURE_ALGORITHMS_CERT_EXT), + ?UINT16(Len), ?UINT16(ListLen), SignSchemeList/binary, Acc/binary>>); +encode_extensions([#sni{hostname = Hostname} | Rest], Acc) -> HostLen = length(Hostname), HostnameBin = list_to_binary(Hostname), % Hostname type (1 byte) + Hostname length (2 bytes) + Hostname (HostLen bytes) ServerNameLength = 1 + 2 + HostLen, % ServerNameListSize (2 bytes) + ServerNameLength ExtLength = 2 + ServerNameLength, - encode_hello_extensions(Rest, <<?UINT16(?SNI_EXT), ?UINT16(ExtLength), - ?UINT16(ServerNameLength), - ?BYTE(?SNI_NAMETYPE_HOST_NAME), - ?UINT16(HostLen), HostnameBin/binary, - Acc/binary>>). + encode_extensions(Rest, <<?UINT16(?SNI_EXT), ?UINT16(ExtLength), + ?UINT16(ServerNameLength), + ?BYTE(?SNI_NAMETYPE_HOST_NAME), + ?UINT16(HostLen), HostnameBin/binary, + Acc/binary>>); +encode_extensions([#client_hello_versions{versions = Versions0} | Rest], Acc) -> + Versions = encode_versions(Versions0), + VerLen = byte_size(Versions), + Len = VerLen + 1, + encode_extensions(Rest, <<?UINT16(?SUPPORTED_VERSIONS_EXT), + ?UINT16(Len), ?BYTE(VerLen), Versions/binary, Acc/binary>>); +encode_extensions([#server_hello_selected_version{selected_version = Version0} | Rest], Acc) -> + Version = encode_versions([Version0]), + Len = byte_size(Version), %% 2 + encode_extensions(Rest, <<?UINT16(?SUPPORTED_VERSIONS_EXT), + ?UINT16(Len), Version/binary, Acc/binary>>); +encode_extensions([#key_share_client_hello{client_shares = ClientShares0} | Rest], Acc) -> + ClientShares = encode_client_shares(ClientShares0), + ClientSharesLen = byte_size(ClientShares), + Len = ClientSharesLen + 2, + encode_extensions(Rest, <<?UINT16(?KEY_SHARE_EXT), + ?UINT16(Len), ?UINT16(ClientSharesLen), + ClientShares/binary, Acc/binary>>); +encode_extensions([#key_share_server_hello{server_share = ServerShare0} | Rest], Acc) -> + ServerShare = encode_key_share_entry(ServerShare0), + Len = byte_size(ServerShare), + encode_extensions(Rest, <<?UINT16(?KEY_SHARE_EXT), + ?UINT16(Len), ServerShare/binary, Acc/binary>>); +encode_extensions([#key_share_hello_retry_request{selected_group = Group0} | Rest], Acc) -> + Group = tls_v1:group_to_enum(Group0), + encode_extensions(Rest, <<?UINT16(?KEY_SHARE_EXT), + ?UINT16(2), ?UINT16(Group), Acc/binary>>). + encode_client_protocol_negotiation(undefined, _) -> undefined; @@ -657,7 +733,7 @@ decode_handshake(_, ?NEXT_PROTOCOL, <<?BYTE(SelectedProtocolLength), ?BYTE(PaddingLength), _Padding:PaddingLength/binary>>) -> #next_protocol{selected_protocol = SelectedProtocol}; -decode_handshake(_Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, +decode_handshake(Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, ?BYTE(SID_length), Session_ID:SID_length/binary, Cipher_suite:2/binary, ?BYTE(Comp_method)>>) -> #server_hello{ @@ -666,14 +742,13 @@ decode_handshake(_Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:3 session_id = Session_ID, cipher_suite = Cipher_suite, compression_method = Comp_method, - extensions = #hello_extensions{}}; + extensions = empty_extensions(Version, server_hello)}; -decode_handshake(_Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, +decode_handshake(Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, ?BYTE(SID_length), Session_ID:SID_length/binary, Cipher_suite:2/binary, ?BYTE(Comp_method), ?UINT16(ExtLen), Extensions:ExtLen/binary>>) -> - - HelloExtensions = decode_hello_extensions(Extensions), + HelloExtensions = decode_hello_extensions(Extensions, Version, {Major, Minor}, server_hello), #server_hello{ server_version = {Major,Minor}, @@ -716,17 +791,49 @@ decode_handshake(_Version, ?FINISHED, VerifyData) -> decode_handshake(_, Message, _) -> throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, {unknown_or_malformed_handshake, Message})). + +%%-------------------------------------------------------------------- +-spec decode_vector(binary()) -> binary(). +%% +%% Description: Remove length tag from TLS Vector type. Needed +%% for client hello when extensions in older versions may be empty. +%% +%%-------------------------------------------------------------------- +decode_vector(<<>>) -> + <<>>; +decode_vector(<<?UINT16(Len), Vector:Len/binary>>) -> + Vector. + +%%-------------------------------------------------------------------- +-spec decode_hello_extensions(binary(), ssl_record:ssl_version(), + ssl_record:ssl_version(), atom()) -> map(). +%% +%% Description: Decodes TLS hello extensions +%%-------------------------------------------------------------------- +decode_hello_extensions(Extensions, LocalVersion, LegacyVersion, MessageType0) -> + %% Convert legacy atoms + MessageType = + case MessageType0 of + client -> client_hello; + server -> server_hello; + T -> T + end, + %% RFC 8446 - 4.2.1 + %% Servers MUST be prepared to receive ClientHellos that include this extension but + %% do not include 0x0304 in the list of versions. + %% Clients MUST check for this extension prior to processing the rest of the + %% ServerHello (although they will have to parse the ServerHello in order to read + %% the extension). + Version = process_supported_versions_extension(Extensions, LocalVersion, LegacyVersion), + decode_extensions(Extensions, Version, MessageType, empty_extensions(Version, MessageType)). + %%-------------------------------------------------------------------- --spec decode_hello_extensions({client, binary()} | binary()) -> #hello_extensions{}. +-spec decode_extensions(binary(),tuple(), atom()) -> map(). %% %% Description: Decodes TLS hello extensions %%-------------------------------------------------------------------- -decode_hello_extensions({client, <<>>}) -> - #hello_extensions{}; -decode_hello_extensions({client, <<?UINT16(ExtLen), Extensions:ExtLen/binary>>}) -> - decode_hello_extensions(Extensions); -decode_hello_extensions(Extensions) -> - dec_hello_extensions(Extensions, #hello_extensions{}). +decode_extensions(Extensions, Version, MessageType) -> + decode_extensions(Extensions, Version, MessageType, empty_extensions()). %%-------------------------------------------------------------------- -spec decode_server_key(binary(), ssl:kex_algo(), ssl_record:ssl_version()) -> @@ -776,7 +883,7 @@ available_suites(ServerCert, UserSuites, Version, undefined, Curve) -> filter_unavailable_ecc_suites(Curve, Suites); available_suites(ServerCert, UserSuites, Version, HashSigns, Curve) -> Suites = available_suites(ServerCert, UserSuites, Version, undefined, Curve), - filter_hashsigns(Suites, [ssl_cipher_format:suite_definition(Suite) || Suite <- Suites], HashSigns, + filter_hashsigns(Suites, [ssl_cipher_format:suite_bin_to_map(Suite) || Suite <- Suites], HashSigns, Version, []). available_signature_algs(undefined, _) -> @@ -936,88 +1043,216 @@ premaster_secret(EncSecret, #{algorithm := rsa} = Engine) -> %%==================================================================== %% Extensions handling %%==================================================================== -client_hello_extensions(Version, CipherSuites, - #ssl_options{signature_algs = SupportedHashSigns, - eccs = SupportedECCs} = 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, KeyShare) -> + HelloExtensions0 = add_tls12_extensions(Version, SslOpts, ConnectionStates, Renegotiation), + HelloExtensions1 = add_common_extensions(Version, HelloExtensions0, CipherSuites, SslOpts), + maybe_add_tls13_extensions(Version, HelloExtensions1, SslOpts, KeyShare). + + +add_tls12_extensions(_Version, + SslOpts, + ConnectionStates, + Renegotiation) -> SRP = srp_user(SslOpts), + #{renegotiation_info => renegotiation_info(tls_record, client, + ConnectionStates, Renegotiation), + srp => SRP, + 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) + }. + - #hello_extensions{ - 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_common_extensions({3,4}, + HelloExtensions, + _CipherSuites, + #ssl_options{eccs = SupportedECCs, + supported_groups = Groups, + signature_algs = SignatureSchemes}) -> + {EcPointFormats, _} = + client_ecc_extensions(SupportedECCs), + HelloExtensions#{ec_point_formats => EcPointFormats, + elliptic_curves => Groups, + signature_algs => signature_algs_ext(SignatureSchemes)}; + +add_common_extensions(Version, + HelloExtensions, + CipherSuites, + #ssl_options{eccs = SupportedECCs, + signature_algs = SupportedHashSigns}) -> + + {EcPointFormats, EllipticCurves} = + case advertises_ec_ciphers( + lists:map(fun ssl_cipher_format:suite_bin_to_map/1, + CipherSuites)) of + true -> + client_ecc_extensions(SupportedECCs); + false -> + {undefined, undefined} + end, + HelloExtensions#{ec_point_formats => EcPointFormats, + elliptic_curves => EllipticCurves, + signature_algs => available_signature_algs(SupportedHashSigns, Version)}. + + +maybe_add_tls13_extensions({3,4}, + HelloExtensions0, + #ssl_options{signature_algs_cert = SignatureSchemes, + versions = SupportedVersions}, + KeyShare) -> + HelloExtensions = + HelloExtensions0#{client_hello_versions => + #client_hello_versions{versions = SupportedVersions}, + signature_algs_cert => + signature_algs_cert(SignatureSchemes)}, + maybe_add_key_share(HelloExtensions, KeyShare); +maybe_add_tls13_extensions(_, HelloExtensions, _, _) -> + HelloExtensions. + + +%% TODO: Add support for PSK key establishment + +%% RFC 8446 (TLS 1.3) - 4.2.8. Key Share +%% +%% 4.2.8.1. Diffie-Hellman Parameters +%% Diffie-Hellman [DH76] parameters for both clients and servers are +%% encoded in the opaque key_exchange field of a KeyShareEntry in a +%% KeyShare structure. The opaque value contains the Diffie-Hellman +%% public value (Y = g^X mod p) for the specified group (see [RFC7919] +%% for group definitions) encoded as a big-endian integer and padded to +%% the left with zeros to the size of p in bytes. +%% +%% 4.2.8.2. ECDHE Parameters +%% +%% ECDHE parameters for both clients and servers are encoded in the +%% opaque key_exchange field of a KeyShareEntry in a KeyShare structure. +%% +%% For secp256r1, secp384r1, and secp521r1, the contents are the +%% serialized value of the following struct: +%% +%% struct { +%% uint8 legacy_form = 4; +%% opaque X[coordinate_length]; +%% opaque Y[coordinate_length]; +%% } UncompressedPointRepresentation; +%% +%% X and Y, respectively, are the binary representations of the x and y +%% values in network byte order. There are no internal length markers, +%% so each number representation occupies as many octets as implied by +%% the curve parameters. For P-256, this means that each of X and Y use +%% 32 octets, padded on the left by zeros if necessary. For P-384, they +%% take 48 octets each. For P-521, they take 66 octets each. +maybe_add_key_share(HelloExtensions, undefined) -> + HelloExtensions; +maybe_add_key_share(HelloExtensions, KeyShare) -> + #key_share_client_hello{client_shares = ClientShares0} = KeyShare, + %% Keep only public keys + ClientShares = lists:map(fun kse_remove_private_key/1, ClientShares0), + HelloExtensions#{key_share => #key_share_client_hello{ + client_shares = ClientShares}}. + +add_server_share(server_hello, Extensions, KeyShare) -> + #key_share_server_hello{server_share = ServerShare0} = KeyShare, + %% Keep only public keys + ServerShare = kse_remove_private_key(ServerShare0), + Extensions#{key_share => #key_share_server_hello{ + server_share = ServerShare}}; +add_server_share(hello_retry_request, Extensions, + #key_share_server_hello{ + server_share = #key_share_entry{group = Group}}) -> + Extensions#{key_share => #key_share_hello_retry_request{ + selected_group = Group}}. + + +kse_remove_private_key(#key_share_entry{ + group = Group, + key_exchange = + #'ECPrivateKey'{publicKey = PublicKey}}) -> + #key_share_entry{ + group = Group, + key_exchange = PublicKey}; +kse_remove_private_key(#key_share_entry{ + group = Group, + key_exchange = + {PublicKey, _}}) -> + #key_share_entry{ + group = Group, + key_exchange = PublicKey}. + +signature_algs_ext(undefined) -> + undefined; +signature_algs_ext(SignatureSchemes0) -> + %% The SSL option signature_algs contains both hash-sign algorithms (tuples) and + %% signature schemes (atoms) if TLS 1.3 is configured. + %% Filter out all hash-sign tuples when creating the signature_algs extension. + %% (TLS 1.3 specific record type) + SignatureSchemes = lists:filter(fun is_atom/1, SignatureSchemes0), + #signature_algorithms{signature_scheme_list = SignatureSchemes}. + +signature_algs_cert(undefined) -> + undefined; +signature_algs_cert(SignatureSchemes) -> + #signature_algorithms_cert{signature_scheme_list = SignatureSchemes}. handle_client_hello_extensions(RecordCB, Random, ClientCipherSuites, - #hello_extensions{renegotiation_info = Info, - srp = SRP, - ec_point_formats = ECCFormat, - alpn = ALPN, - next_protocol_negotiation = NextProtocolNegotiation}, Version, + Exts, Version, #ssl_options{secure_renegotiate = SecureRenegotation, alpn_preferred_protocols = ALPNPreferredProtocols} = Opts, #session{cipher_suite = NegotiatedCipherSuite, compression_method = Compression} = Session0, ConnectionStates0, Renegotiation) -> - Session = handle_srp_extension(SRP, Session0), - ConnectionStates = handle_renegotiation_extension(server, RecordCB, Version, Info, - Random, NegotiatedCipherSuite, + Session = handle_srp_extension(maps:get(srp, Exts, undefined), Session0), + ConnectionStates = handle_renegotiation_extension(server, RecordCB, Version, maps:get(renegotiation_info, Exts, undefined), + Random, NegotiatedCipherSuite, ClientCipherSuites, Compression, - ConnectionStates0, Renegotiation, SecureRenegotation), - - ServerHelloExtensions = #hello_extensions{ - renegotiation_info = renegotiation_info(RecordCB, server, + ConnectionStates0, Renegotiation, SecureRenegotation), + + Empty = empty_extensions(Version, server_hello), + ServerHelloExtensions = Empty#{renegotiation_info => renegotiation_info(RecordCB, server, ConnectionStates, Renegotiation), - ec_point_formats = server_ecc_extension(Version, ECCFormat) - }, - + ec_point_formats => server_ecc_extension(Version, + maps:get(ec_point_formats, Exts, undefined)) + }, + %% If we receive an ALPN extension and have ALPN configured for this connection, %% we handle it. Otherwise we check for the NPN extension. + ALPN = maps:get(alpn, Exts, undefined), if ALPN =/= undefined, ALPNPreferredProtocols =/= undefined -> Protocol = handle_alpn_extension(ALPNPreferredProtocols, decode_alpn(ALPN)), {Session, ConnectionStates, Protocol, - ServerHelloExtensions#hello_extensions{alpn=encode_alpn([Protocol], Renegotiation)}}; + ServerHelloExtensions#{alpn => encode_alpn([Protocol], Renegotiation)}}; true -> - ProtocolsToAdvertise = handle_next_protocol_extension(NextProtocolNegotiation, Renegotiation, Opts), + NextProtocolNegotiation = maps:get(next_protocol_negotiation, Exts, undefined), + ProtocolsToAdvertise = handle_next_protocol_extension(NextProtocolNegotiation, Renegotiation, Opts), {Session, ConnectionStates, undefined, - ServerHelloExtensions#hello_extensions{next_protocol_negotiation= - encode_protocols_advertised_on_server(ProtocolsToAdvertise)}} + ServerHelloExtensions#{next_protocol_negotiation => + encode_protocols_advertised_on_server(ProtocolsToAdvertise)}} end. handle_server_hello_extensions(RecordCB, Random, CipherSuite, Compression, - #hello_extensions{renegotiation_info = Info, - alpn = ALPN, - next_protocol_negotiation = NextProtocolNegotiation}, Version, + Exts, Version, #ssl_options{secure_renegotiate = SecureRenegotation, next_protocol_selector = NextProtoSelector}, ConnectionStates0, Renegotiation) -> - ConnectionStates = handle_renegotiation_extension(client, RecordCB, Version, Info, Random, + ConnectionStates = handle_renegotiation_extension(client, RecordCB, Version, + maps:get(renegotiation_info, Exts, undefined), Random, CipherSuite, undefined, Compression, ConnectionStates0, Renegotiation, SecureRenegotation), %% If we receive an ALPN extension then this is the protocol selected, %% otherwise handle the NPN extension. + ALPN = maps:get(alpn, Exts, undefined), case decode_alpn(ALPN) of %% ServerHello contains exactly one protocol: the one selected. %% We also ignore the ALPN extension during renegotiation (see encode_alpn/2). [Protocol] when not Renegotiation -> {ConnectionStates, alpn, Protocol}; undefined -> + NextProtocolNegotiation = maps:get(next_protocol_negotiation, Exts, undefined), Protocol = handle_next_protocol(NextProtocolNegotiation, NextProtoSelector, Renegotiation), {ConnectionStates, npn, Protocol}; {error, Reason} -> @@ -1062,26 +1297,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), @@ -1095,21 +1354,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 @@ -1122,8 +1383,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()) -> @@ -1172,6 +1463,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}) -> @@ -1192,33 +1485,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), @@ -1761,16 +2051,32 @@ encode_alpn(undefined, _) -> encode_alpn(Protocols, _) -> #alpn{extension_data = lists:foldl(fun encode_protocol/2, <<>>, Protocols)}. -hello_extensions_list(#hello_extensions{renegotiation_info = RenegotiationInfo, - srp = SRP, - signature_algs = HashSigns, - ec_point_formats = EcPointFormats, - elliptic_curves = EllipticCurves, - alpn = ALPN, - next_protocol_negotiation = NextProtocolNegotiation, - sni = Sni}) -> - [Ext || Ext <- [RenegotiationInfo, SRP, HashSigns, - EcPointFormats, EllipticCurves, ALPN, NextProtocolNegotiation, Sni], Ext =/= undefined]. + +encode_versions(Versions) -> + encode_versions(lists:reverse(Versions), <<>>). +%% +encode_versions([], Acc) -> + Acc; +encode_versions([{M,N}|T], Acc) -> + encode_versions(T, <<?BYTE(M),?BYTE(N),Acc/binary>>). + +encode_client_shares(ClientShares) -> + encode_client_shares(ClientShares, <<>>). +%% +encode_client_shares([], Acc) -> + Acc; +encode_client_shares([KeyShareEntry0|T], Acc) -> + KeyShareEntry = encode_key_share_entry(KeyShareEntry0), + encode_client_shares(T, <<Acc/binary,KeyShareEntry/binary>>). + +encode_key_share_entry(#key_share_entry{ + group = Group, + key_exchange = KeyExchange}) -> + Len = byte_size(KeyExchange), + <<?UINT16((tls_v1:group_to_enum(Group))),?UINT16(Len),KeyExchange/binary>>. + +hello_extensions_list(HelloExtensions) -> + [Ext || {_, Ext} <- maps:to_list(HelloExtensions), Ext =/= undefined]. %%-------------Decode handshakes--------------------------------- dec_server_key(<<?UINT16(PLen), P:PLen/binary, @@ -1910,16 +2216,60 @@ 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)). -dec_hello_extensions(<<>>, Acc) -> +%% Processes a ClientHello/ServerHello message and returns the version to be used +%% in the decoding functions. The following rules apply: +%% - IF supported_versions extension is absent: +%% RETURN the lowest of (LocalVersion and LegacyVersion) +%% - IF supported_versions estension is present: +%% RETURN the lowest of (LocalVersion and first element of supported versions) +process_supported_versions_extension(<<>>, LocalVersion, LegacyVersion) + when LegacyVersion =< LocalVersion -> + LegacyVersion; +process_supported_versions_extension(<<>>, LocalVersion, _LegacyVersion) -> + LocalVersion; +process_supported_versions_extension(<<?UINT16(?SUPPORTED_VERSIONS_EXT), ?UINT16(Len), + ExtData:Len/binary, _Rest/binary>>, + LocalVersion, _LegacyVersion) when Len > 2 -> + <<?BYTE(_),Versions0/binary>> = ExtData, + [Highest|_] = decode_versions(Versions0), + if Highest =< LocalVersion -> + Highest; + true -> + LocalVersion + end; +process_supported_versions_extension(<<?UINT16(?SUPPORTED_VERSIONS_EXT), ?UINT16(Len), + ?BYTE(Major),?BYTE(Minor), _Rest/binary>>, + LocalVersion, _LegacyVersion) when Len =:= 2 -> + SelectedVersion = {Major, Minor}, + if SelectedVersion =< LocalVersion -> + SelectedVersion; + true -> + LocalVersion + end; +process_supported_versions_extension(<<?UINT16(_), ?UINT16(Len), + _ExtData:Len/binary, Rest/binary>>, + LocalVersion, LegacyVersion) -> + process_supported_versions_extension(Rest, LocalVersion, LegacyVersion); +%% Tolerate protocol encoding errors and skip parsing the rest of the extension. +process_supported_versions_extension(_, LocalVersion, LegacyVersion) + when LegacyVersion =< LocalVersion -> + LegacyVersion; +process_supported_versions_extension(_, LocalVersion, _) -> + LocalVersion. + +decode_extensions(<<>>, _Version, _MessageType, Acc) -> Acc; -dec_hello_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, MessageType, Acc) + when Len + 2 =:= ExtLen -> ALPN = #alpn{extension_data = ExtensionData}, - dec_hello_extensions(Rest, Acc#hello_extensions{alpn = ALPN}); -dec_hello_extensions(<<?UINT16(?NEXTPROTONEG_EXT), ?UINT16(Len), ExtensionData:Len/binary, Rest/binary>>, Acc) -> + decode_extensions(Rest, Version, MessageType, Acc#{alpn => ALPN}); +decode_extensions(<<?UINT16(?NEXTPROTONEG_EXT), ?UINT16(Len), + ExtensionData:Len/binary, Rest/binary>>, Version, MessageType, Acc) -> NextP = #next_protocol_negotiation{extension_data = ExtensionData}, - dec_hello_extensions(Rest, Acc#hello_extensions{next_protocol_negotiation = NextP}); -dec_hello_extensions(<<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), Info:Len/binary, Rest/binary>>, Acc) -> + decode_extensions(Rest, Version, MessageType, Acc#{next_protocol_negotiation => NextP}); +decode_extensions(<<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), + Info:Len/binary, Rest/binary>>, Version, MessageType, Acc) -> RenegotiateInfo = case Len of 1 -> % Initial handshake Info; % should be <<0>> will be matched in handle_renegotiation_info @@ -1928,25 +2278,54 @@ dec_hello_extensions(<<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), Info:Len/binar <<?BYTE(VerifyLen), VerifyInfo/binary>> = Info, VerifyInfo end, - dec_hello_extensions(Rest, Acc#hello_extensions{renegotiation_info = - #renegotiation_info{renegotiated_connection = - RenegotiateInfo}}); + decode_extensions(Rest, Version, MessageType, + Acc#{renegotiation_info => + #renegotiation_info{renegotiated_connection = + RenegotiateInfo}}); -dec_hello_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, MessageType, Acc) when Len == SRPLen + 1 -> - dec_hello_extensions(Rest, Acc#hello_extensions{srp = #srp{username = SRP}}); + decode_extensions(Rest, Version, MessageType, Acc#{srp => #srp{username = SRP}}); -dec_hello_extensions(<<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len), - ExtData:Len/binary, Rest/binary>>, Acc) -> +decode_extensions(<<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len), + ExtData:Len/binary, Rest/binary>>, Version, MessageType, 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], - dec_hello_extensions(Rest, Acc#hello_extensions{signature_algs = - #hash_sign_algos{hash_sign_algos = HashSignAlgos}}); - -dec_hello_extensions(<<?UINT16(?ELLIPTIC_CURVES_EXT), ?UINT16(Len), - ExtData:Len/binary, Rest/binary>>, Acc) -> + decode_extensions(Rest, Version, MessageType, + Acc#{signature_algs => + #hash_sign_algos{hash_sign_algos = + HashSignAlgos}}); + +decode_extensions(<<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len), + ExtData:Len/binary, Rest/binary>>, Version, MessageType, 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, MessageType, + Acc#{signature_algs => + #signature_algorithms{ + signature_scheme_list = SignSchemes}}); + +decode_extensions(<<?UINT16(?SIGNATURE_ALGORITHMS_CERT_EXT), ?UINT16(Len), + ExtData:Len/binary, Rest/binary>>, Version, MessageType, Acc) -> + SignSchemeListLen = Len - 2, + <<?UINT16(SignSchemeListLen), SignSchemeList/binary>> = ExtData, + SignSchemes = [ssl_cipher:signature_scheme(SignScheme) || + <<?UINT16(SignScheme)>> <= SignSchemeList], + decode_extensions(Rest, Version, MessageType, + Acc#{signature_algs_cert => + #signature_algorithms_cert{ + signature_scheme_list = SignSchemes}}); + +decode_extensions(<<?UINT16(?ELLIPTIC_CURVES_EXT), ?UINT16(Len), + ExtData:Len/binary, Rest/binary>>, Version, MessageType, Acc) + when Version < {3,4} -> <<?UINT16(_), EllipticCurveList/binary>> = ExtData, %% Ignore unknown curves Pick = fun(Enum) -> @@ -1958,31 +2337,103 @@ dec_hello_extensions(<<?UINT16(?ELLIPTIC_CURVES_EXT), ?UINT16(Len), end end, EllipticCurves = lists:filtermap(Pick, [ECC || <<ECC:16>> <= EllipticCurveList]), - dec_hello_extensions(Rest, Acc#hello_extensions{elliptic_curves = - #elliptic_curves{elliptic_curve_list = - EllipticCurves}}); -dec_hello_extensions(<<?UINT16(?EC_POINT_FORMATS_EXT), ?UINT16(Len), - ExtData:Len/binary, Rest/binary>>, Acc) -> + decode_extensions(Rest, Version, MessageType, + Acc#{elliptic_curves => + #elliptic_curves{elliptic_curve_list = + EllipticCurves}}); + +decode_extensions(<<?UINT16(?ELLIPTIC_CURVES_EXT), ?UINT16(Len), + ExtData:Len/binary, Rest/binary>>, Version, MessageType, 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, MessageType, + Acc#{elliptic_curves => + #supported_groups{supported_groups = + SupportedGroups}}); + +decode_extensions(<<?UINT16(?EC_POINT_FORMATS_EXT), ?UINT16(Len), + ExtData:Len/binary, Rest/binary>>, Version, MessageType, Acc) -> <<?BYTE(_), ECPointFormatList/binary>> = ExtData, ECPointFormats = binary_to_list(ECPointFormatList), - dec_hello_extensions(Rest, Acc#hello_extensions{ec_point_formats = - #ec_point_formats{ec_point_format_list = - ECPointFormats}}); + decode_extensions(Rest, Version, MessageType, + Acc#{ec_point_formats => + #ec_point_formats{ec_point_format_list = + ECPointFormats}}); + +decode_extensions(<<?UINT16(?SNI_EXT), ?UINT16(Len), + Rest/binary>>, Version, MessageType, Acc) when Len == 0 -> + decode_extensions(Rest, Version, MessageType, + Acc#{sni => #sni{hostname = ""}}); %% Server may send an empy SNI + +decode_extensions(<<?UINT16(?SNI_EXT), ?UINT16(Len), + ExtData:Len/binary, Rest/binary>>, Version, MessageType, Acc) -> + <<?UINT16(_), NameList/binary>> = ExtData, + decode_extensions(Rest, Version, MessageType, + Acc#{sni => dec_sni(NameList)}); + +decode_extensions(<<?UINT16(?SUPPORTED_VERSIONS_EXT), ?UINT16(Len), + ExtData:Len/binary, Rest/binary>>, Version, MessageType, Acc) when Len > 2 -> + <<?BYTE(_),Versions/binary>> = ExtData, + decode_extensions(Rest, Version, MessageType, + Acc#{client_hello_versions => + #client_hello_versions{ + versions = decode_versions(Versions)}}); + +decode_extensions(<<?UINT16(?SUPPORTED_VERSIONS_EXT), ?UINT16(Len), + ?UINT16(SelectedVersion), Rest/binary>>, Version, MessageType, Acc) + when Len =:= 2, SelectedVersion =:= 16#0304 -> + decode_extensions(Rest, Version, MessageType, + Acc#{server_hello_selected_version => + #server_hello_selected_version{selected_version = + {3,4}}}); + +decode_extensions(<<?UINT16(?KEY_SHARE_EXT), ?UINT16(Len), + ExtData:Len/binary, Rest/binary>>, + Version, MessageType = client_hello, Acc) -> + <<?UINT16(_),ClientShares/binary>> = ExtData, + decode_extensions(Rest, Version, MessageType, + Acc#{key_share => + #key_share_client_hello{ + client_shares = decode_client_shares(ClientShares)}}); + +decode_extensions(<<?UINT16(?KEY_SHARE_EXT), ?UINT16(Len), + ExtData:Len/binary, Rest/binary>>, + Version, MessageType = server_hello, Acc) -> + <<?UINT16(Group),?UINT16(KeyLen),KeyExchange:KeyLen/binary>> = ExtData, + decode_extensions(Rest, Version, MessageType, + Acc#{key_share => + #key_share_server_hello{ + server_share = + #key_share_entry{ + group = tls_v1:enum_to_group(Group), + key_exchange = KeyExchange}}}); + +decode_extensions(<<?UINT16(?KEY_SHARE_EXT), ?UINT16(Len), + ExtData:Len/binary, Rest/binary>>, + Version, MessageType = hello_retry_request, Acc) -> + <<?UINT16(Group)>> = ExtData, + decode_extensions(Rest, Version, MessageType, + Acc#{key_share => + #key_share_hello_retry_request{ + selected_group = tls_v1:enum_to_group(Group)}}); -dec_hello_extensions(<<?UINT16(?SNI_EXT), ?UINT16(Len), Rest/binary>>, Acc) when Len == 0 -> - dec_hello_extensions(Rest, Acc#hello_extensions{sni = #sni{hostname = ""}}); %% Server may send an empy SNI -dec_hello_extensions(<<?UINT16(?SNI_EXT), ?UINT16(Len), - ExtData:Len/binary, Rest/binary>>, Acc) -> - <<?UINT16(_), NameList/binary>> = ExtData, - dec_hello_extensions(Rest, Acc#hello_extensions{sni = dec_sni(NameList)}); %% Ignore data following the ClientHello (i.e., %% extensions) if not understood. - -dec_hello_extensions(<<?UINT16(_), ?UINT16(Len), _Unknown:Len/binary, Rest/binary>>, Acc) -> - dec_hello_extensions(Rest, Acc); +decode_extensions(<<?UINT16(_), ?UINT16(Len), _Unknown:Len/binary, Rest/binary>>, Version, MessageType, Acc) -> + decode_extensions(Rest, Version, MessageType, Acc); %% This theoretically should not happen if the protocol is followed, but if it does it is ignored. -dec_hello_extensions(_, Acc) -> +decode_extensions(_, _, _, Acc) -> Acc. dec_hashsign(<<?BYTE(HashAlgo), ?BYTE(SignAlgo)>>) -> @@ -2000,6 +2451,26 @@ decode_alpn(undefined) -> decode_alpn(#alpn{extension_data=Data}) -> decode_protocols(Data, []). +decode_versions(Versions) -> + decode_versions(Versions, []). +%% +decode_versions(<<>>, Acc) -> + lists:reverse(Acc); +decode_versions(<<?BYTE(M),?BYTE(N),Rest/binary>>, Acc) -> + decode_versions(Rest, [{M,N}|Acc]). + + +decode_client_shares(ClientShares) -> + decode_client_shares(ClientShares, []). +%% +decode_client_shares(<<>>, Acc) -> + lists:reverse(Acc); +decode_client_shares(<<?UINT16(Group),?UINT16(Len),KeyExchange:Len/binary,Rest/binary>>, Acc) -> + decode_client_shares(Rest, [#key_share_entry{ + group = tls_v1:enum_to_group(Group), + key_exchange= KeyExchange + }|Acc]). + decode_next_protocols({next_protocol_negotiation, Protocols}) -> decode_protocols(Protocols, []). @@ -2253,17 +2724,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; @@ -2279,15 +2739,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) -> @@ -2306,6 +2831,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}; @@ -2460,7 +2990,7 @@ handle_renegotiation_info(_RecordCB, ConnectionStates, SecureRenegotation) -> cert_curve(_, _, no_suite) -> {no_curve, no_suite}; cert_curve(Cert, ECCCurve0, CipherSuite) -> - case ssl_cipher_format:suite_definition(CipherSuite) of + case ssl_cipher_format:suite_bin_to_map(CipherSuite) of #{key_exchange := Kex} when Kex == ecdh_ecdsa; Kex == ecdh_rsa -> OtpCert = public_key:pkix_decode_cert(Cert, otp), @@ -2473,4 +3003,51 @@ cert_curve(Cert, ECCCurve0, CipherSuite) -> {ECCCurve0, CipherSuite} end. - +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, + %% 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_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, + srp => undefined, + ec_point_formats => undefined, + elliptic_curves => undefined, + sni => undefined}; +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}. diff --git a/lib/ssl/src/ssl_handshake.hrl b/lib/ssl/src/ssl_handshake.hrl index a191fcf766..b248edcaa9 100644 --- a/lib/ssl/src/ssl_handshake.hrl +++ b/lib/ssl/src/ssl_handshake.hrl @@ -47,14 +47,15 @@ srp_username, is_resumable, time_stamp, - ecc + ecc, %% TLS 1.3 Group + sign_alg, %% TLS 1.3 Signature Algorithm + dh_public_value %% TLS 1.3 DH Public Value from peer }). -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 @@ -105,7 +106,11 @@ srp, ec_point_formats, elliptic_curves, - sni + sni, + client_hello_versions, + server_hello_selected_version, + signature_algs_cert, + key_share }). -record(server_hello, { @@ -313,12 +318,12 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -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}). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% Application-Layer Protocol Negotiation RFC 7301 +%% RFC 7301 Application-Layer Protocol Negotiation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -define(ALPN_EXT, 16). @@ -338,9 +343,8 @@ -record(next_protocol, {selected_protocol}). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% ECC Extensions RFC 4492 section 4 and 5 +%% ECC Extensions RFC 8422 section 4 and 5 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -define(ELLIPTIC_CURVES_EXT, 10). -define(EC_POINT_FORMATS_EXT, 11). @@ -348,11 +352,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). @@ -365,10 +376,11 @@ -define(NAMED_CURVE, 3). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% Server name indication RFC 6066 section 3 +%% RFC 6066 Server name indication %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --define(SNI_EXT, 16#0000). +%% section 3 +-define(SNI_EXT, 0). %% enum { host_name(0), (255) } NameType; -define(SNI_NAMETYPE_HOST_NAME, 0). @@ -377,4 +389,56 @@ hostname = undefined }). +%% Other possible values from RFC 6066, not supported +-define(MAX_FRAGMENT_LENGTH, 1). +-define(CLIENT_CERTIFICATE_URL, 2). +-define(TRUSTED_CA_KEYS, 3). +-define(TRUNCATED_HMAC, 4). +-define(STATUS_REQUEST, 5). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% RFC 7250 Using Raw Public Keys in Transport Layer Security (TLS) +%% and Datagram Transport Layer Security (DTLS) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Not supported +-define(CLIENT_CERTIFICATE_TYPE, 19). +-define(SERVER_CERTIFICATE_TYPE, 20). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% RFC 6520 Transport Layer Security (TLS) and +%% Datagram Transport Layer Security (DTLS) Heartbeat Extension +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Not supported +-define(HS_HEARTBEAT, 15). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% RFC 6962 Certificate Transparency +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Not supported +-define(SIGNED_CERTIFICATE_TIMESTAMP, 18). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% RFC 7685 A Transport Layer Security (TLS) ClientHello Padding Extension +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Not supported +-define(PADDING, 21). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Supported Versions RFC 8446 (TLS 1.3) section 4.2.1 also affects TLS-1.2 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-define(SUPPORTED_VERSIONS_EXT, 43). + +-record(client_hello_versions, {versions}). +-record(server_hello_selected_version, {selected_version}). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Signature Algorithms RFC 8446 (TLS 1.3) section 4.2.3 also affects TLS-1.2 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-define(SIGNATURE_ALGORITHMS_CERT_EXT, 50). + +-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 57c72aa122..06c3ccae45 100644 --- a/lib/ssl/src/ssl_internal.hrl +++ b/lib/ssl/src/ssl_internal.hrl @@ -25,14 +25,15 @@ -include_lib("public_key/include/public_key.hrl"). +-define(VSN, "8.2.6"). -define(SECRET_PRINTOUT, "***"). --type reason() :: term(). --type reply() :: term(). --type msg() :: term(). --type from() :: term(). +-type reason() :: any(). +-type reply() :: any(). +-type msg() :: any(). +-type from() :: any(). -type certdb_ref() :: reference(). --type db_handle() :: term(). +-type db_handle() :: any(). -type der_cert() :: binary(). -type issuer() :: tuple(). -type serialnumber() :: integer(). @@ -70,37 +71,64 @@ -define(FALSE, 1). %% sslv3 is considered insecure due to lack of padding check (Poodle attack) -%% Keep as interop with legacy software but do not support as default --define(ALL_AVAILABLE_VERSIONS, ['tlsv1.2', 'tlsv1.1', tlsv1, sslv3]). +%% Keep as interop with legacy software but do not support as default +%% tlsv1.0 and tlsv1.1 is now also considered legacy +%% tlsv1.3 is under development (experimental). +-define(ALL_AVAILABLE_VERSIONS, ['tlsv1.3', 'tlsv1.2', 'tlsv1.1', tlsv1, sslv3]). -define(ALL_AVAILABLE_DATAGRAM_VERSIONS, ['dtlsv1.2', dtlsv1]). --define(ALL_SUPPORTED_VERSIONS, ['tlsv1.2', 'tlsv1.1', tlsv1]). --define(MIN_SUPPORTED_VERSIONS, ['tlsv1.1', tlsv1]). --define(ALL_DATAGRAM_SUPPORTED_VERSIONS, ['dtlsv1.2', dtlsv1]). +%% Defines the default versions when not specified by an ssl option. +-define(ALL_SUPPORTED_VERSIONS, ['tlsv1.2']). +-define(MIN_SUPPORTED_VERSIONS, ['tlsv1.1']). + +%% Versions allowed in TLSCiphertext.version (TLS 1.2 and prior) and +%% TLSCiphertext.legacy_record_version (TLS 1.3). +%% TLS 1.3 sets TLSCiphertext.legacy_record_version to 0x0303 for all records +%% generated other than an than an initial ClientHello, where it MAY also be 0x0301. +%% Thus, the allowed range is limited to 0x0300 - 0x0303. +-define(ALL_TLS_RECORD_VERSIONS, ['tlsv1.2', 'tlsv1.1', tlsv1, sslv3]). + +-define(ALL_DATAGRAM_SUPPORTED_VERSIONS, ['dtlsv1.2']). -define(MIN_DATAGRAM_SUPPORTED_VERSIONS, [dtlsv1]). +%% TLS 1.3 - Section 4.1.3 +%% +%% If negotiating TLS 1.2, TLS 1.3 servers MUST set the last eight bytes +%% of their Random value to the bytes: +%% +%% 44 4F 57 4E 47 52 44 01 +%% +%% If negotiating TLS 1.1 or below, TLS 1.3 servers MUST and TLS 1.2 +%% servers SHOULD set the last eight bytes of their Random value to the +%% bytes: +%% +%% 44 4F 57 4E 47 52 44 00 +-define(RANDOM_OVERRIDE_TLS12, <<16#44,16#4F,16#57,16#4E,16#47,16#52,16#44,16#01>>). +-define(RANDOM_OVERRIDE_TLS11, <<16#44,16#4F,16#57,16#4E,16#47,16#52,16#44,16#00>>). + -define('24H_in_msec', 86400000). -define('24H_in_sec', 86400). -record(ssl_options, { - protocol :: tls | dtls, - versions :: [ssl_record:ssl_version()], %% ssl_record:atom_version() in API - verify :: verify_none | verify_peer, + protocol :: tls | dtls | 'undefined', + versions :: [ssl_record:ssl_version()] | 'undefined', %% ssl_record:atom_version() in API + verify :: verify_none | verify_peer | 'undefined', verify_fun, %%:: fun(CertVerifyErrors::term()) -> boolean(), - partial_chain :: fun(), - fail_if_no_peer_cert :: boolean(), - verify_client_once :: boolean(), + partial_chain :: fun() | 'undefined', + fail_if_no_peer_cert :: boolean() | 'undefined', + verify_client_once :: boolean() | 'undefined', %% fun(Extensions, State, Verify, AccError) -> {Extensions, State, AccError} validate_extensions_fun, - depth :: integer(), - certfile :: binary(), + depth :: integer() | 'undefined', + certfile :: binary() | 'undefined', cert :: public_key:der_encoded() | secret_printout() | 'undefined', - keyfile :: binary(), - key :: {'RSAPrivateKey' | 'DSAPrivateKey' | 'ECPrivateKey' | 'PrivateKeyInfo', - public_key:der_encoded()} | key_map() | secret_printout() | 'undefined', + keyfile :: binary() | 'undefined', + key :: {'RSAPrivateKey' | 'DSAPrivateKey' | 'ECPrivateKey' | 'PrivateKeyInfo' | 'undefined', + public_key:der_encoded()} | map() %%map() -> ssl:key() how to handle dialyzer? + | secret_printout() | 'undefined', password :: string() | secret_printout() | 'undefined', cacerts :: [public_key:der_encoded()] | secret_printout() | 'undefined', - cacertfile :: binary(), - dh :: public_key:der_encoded() | secret_printout(), + cacertfile :: binary() | 'undefined', + dh :: public_key:der_encoded() | secret_printout() | 'undefined', dhfile :: binary() | secret_printout() | 'undefined', user_lookup_fun, % server option, fun to lookup the user psk_identity :: binary() | secret_printout() | 'undefined', @@ -112,23 +140,23 @@ reuse_session :: fun() | binary() | undefined, %% Server side is a fun() %% If false sessions will never be reused, if true they %% will be reused if possible. - reuse_sessions :: boolean() | save, %% Only client side can use value save + reuse_sessions :: boolean() | save | 'undefined', %% Only client side can use value save renegotiate_at, secure_renegotiate, client_renegotiation, %% undefined if not hibernating, or number of ms of %% inactivity after which ssl_connection will go into %% hibernation - hibernate_after :: timeout(), + hibernate_after :: timeout() | 'undefined', %% This option should only be set to true by inet_tls_dist erl_dist = false :: boolean(), - alpn_advertised_protocols = undefined :: [binary()] | undefined , + alpn_advertised_protocols = undefined :: [binary()] | undefined, alpn_preferred_protocols = undefined :: [binary()] | undefined, next_protocols_advertised = undefined :: [binary()] | undefined, next_protocol_selector = undefined, %% fun([binary()]) -> binary()) - log_alert :: boolean(), + log_level = notice :: atom(), server_name_indication = undefined, - sni_hosts :: [{inet:hostname(), [tuple()]}], + sni_hosts :: [{inet:hostname(), [tuple()]}] | 'undefined', sni_fun :: function() | undefined, %% Should the server prefer its own cipher order over the one provided by %% the client? @@ -138,12 +166,14 @@ %%mitigation entirely? beast_mitigation = one_n_minus_one :: one_n_minus_one | zero_n | disabled, fallback = false :: boolean(), - crl_check :: boolean() | peer | best_effort, + crl_check :: boolean() | peer | best_effort | 'undefined', crl_cache, signature_algs, + signature_algs_cert, eccs, - honor_ecc_order :: boolean(), - max_handshake_size :: integer(), + supported_groups, %% RFC 8422, RFC 8446 + honor_ecc_order :: boolean() | 'undefined', + max_handshake_size :: integer() | 'undefined', handshake, customize_hostname_check %% , @@ -168,19 +198,12 @@ connection_cb }). --type key_map() :: #{algorithm := rsa | dss | ecdsa, - %% engine and key_id ought to - %% be :=, but putting it in - %% the spec gives dialyzer warning - %% of correct code! - engine => crypto:engine_ref(), - key_id => crypto:key_id(), - password => crypto:password() - }. -type state_name() :: hello | abbreviated | certify | cipher | connection. --type gen_fsm_state_return() :: {next_state, state_name(), term()} | - {next_state, state_name(), term(), timeout()} | - {stop, term(), term()}. +-type gen_fsm_state_return() :: {next_state, state_name(), any()} | + {next_state, state_name(), any(), timeout()} | + {stop, any(), any()}. +-type ssl_options() :: #ssl_options{}. + -endif. % -ifdef(ssl_internal). diff --git a/lib/ssl/src/ssl_logger.erl b/lib/ssl/src/ssl_logger.erl new file mode 100644 index 0000000000..987693b96b --- /dev/null +++ b/lib/ssl/src/ssl_logger.erl @@ -0,0 +1,446 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-2019. 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_logger). + +-export([debug/4, + format/2, + notice/2]). + +-define(DEC2HEX(X), + if ((X) >= 0) andalso ((X) =< 9) -> (X) + $0; + ((X) >= 10) andalso ((X) =< 15) -> (X) + $a - 10 + end). + +-define(rec_info(T,R),lists:zip(record_info(fields,T),tl(tuple_to_list(R)))). + +-include("tls_record.hrl"). +-include("ssl_cipher.hrl"). +-include("ssl_internal.hrl"). +-include("tls_handshake.hrl"). +-include("dtls_handshake.hrl"). +-include("tls_handshake_1_3.hrl"). +-include_lib("kernel/include/logger.hrl"). + +%%------------------------------------------------------------------------- +%% External API +%%------------------------------------------------------------------------- + +%% SSL log formatter +format(#{level:= _Level, msg:= {report, Msg}, meta:= _Meta}, _Config0) -> + #{direction := Direction, + protocol := Protocol, + message := Content} = Msg, + case Protocol of + 'record' -> + BinMsg = + case Content of + #ssl_tls{} -> + [tls_record:build_tls_record(Content)]; + _ when is_list(Content) -> + lists:flatten(Content) + end, + format_tls_record(Direction, BinMsg); + 'handshake' -> + format_handshake(Direction, Content); + _Other -> + [] + end. + +%% Stateful logging +debug(Level, Direction, Protocol, Message) + when (Direction =:= inbound orelse Direction =:= outbound) andalso + (Protocol =:= 'record' orelse Protocol =:= 'handshake') -> + case logger:compare_levels(Level, debug) of + lt -> + ?LOG_DEBUG(#{direction => Direction, + protocol => Protocol, + message => Message}, + #{domain => [otp,ssl,Protocol]}); + eq -> + ?LOG_DEBUG(#{direction => Direction, + protocol => Protocol, + message => Message}, + #{domain => [otp,ssl,Protocol]}); + _ -> + ok + end. + +%% Stateful logging +notice(Level, Report) -> + case logger:compare_levels(Level, notice) of + lt -> + ?LOG_NOTICE(Report); + eq -> + ?LOG_NOTICE(Report); + _ -> + ok + end. + + +%%------------------------------------------------------------------------- +%% Handshake Protocol +%%------------------------------------------------------------------------- +format_handshake(Direction, BinMsg) -> + {Header, Message} = parse_handshake(Direction, BinMsg), + io_lib:format("~s~n~s~n", [Header, Message]). + + +parse_handshake(Direction, #client_hello{ + client_version = Version0, + cipher_suites = CipherSuites0, + extensions = Extensions + } = ClientHello) -> + Version = get_client_version(Version0, Extensions), + Header = io_lib:format("~s ~s Handshake, ClientHello", + [header_prefix(Direction), + version(Version)]), + CipherSuites = parse_cipher_suites(CipherSuites0), + Message = io_lib:format("~p", + [?rec_info(client_hello, + ClientHello#client_hello{cipher_suites = CipherSuites})]), + {Header, Message}; +parse_handshake(Direction, #server_hello{ + server_version = Version0, + cipher_suite = CipherSuite0, + extensions = Extensions + } = ServerHello) -> + Version = get_server_version(Version0, Extensions), + Header = io_lib:format("~s ~s Handshake, ServerHello", + [header_prefix(Direction), + version(Version)]), + CipherSuite = format_cipher(CipherSuite0), + Message = io_lib:format("~p", + [?rec_info(server_hello, + ServerHello#server_hello{cipher_suite = CipherSuite})]), + {Header, Message}; +parse_handshake(Direction, #hello_verify_request{} = HelloVerifyRequest) -> + Header = io_lib:format("~s Handshake, HelloVerifyRequest", + [header_prefix(Direction)]), + Message = io_lib:format("~p", [?rec_info(hello_verify_request, HelloVerifyRequest)]), + {Header, Message}; +parse_handshake(Direction, #certificate{} = Certificate) -> + Header = io_lib:format("~s Handshake, Certificate", + [header_prefix(Direction)]), + Message = io_lib:format("~p", [?rec_info(certificate, Certificate)]), + {Header, Message}; +parse_handshake(Direction, #server_key_exchange{} = ServerKeyExchange) -> + Header = io_lib:format("~s Handshake, ServerKeyExchange", + [header_prefix(Direction)]), + Message = io_lib:format("~p", [?rec_info(server_key_exchange, ServerKeyExchange)]), + {Header, Message}; +parse_handshake(Direction, #server_key_params{} = ServerKeyExchange) -> + Header = io_lib:format("~s Handshake, ServerKeyExchange", + [header_prefix(Direction)]), + Message = io_lib:format("~p", [?rec_info(server_key_params, ServerKeyExchange)]), + {Header, Message}; +parse_handshake(Direction, #certificate_request{} = CertificateRequest) -> + Header = io_lib:format("~s Handshake, CertificateRequest", + [header_prefix(Direction)]), + Message = io_lib:format("~p", [?rec_info(certificate_request, CertificateRequest)]), + {Header, Message}; +parse_handshake(Direction, #server_hello_done{} = ServerHelloDone) -> + Header = io_lib:format("~s Handshake, ServerHelloDone", + [header_prefix(Direction)]), + Message = io_lib:format("~p", [?rec_info(server_hello_done, ServerHelloDone)]), + {Header, Message}; +parse_handshake(Direction, #client_key_exchange{} = ClientKeyExchange) -> + Header = io_lib:format("~s Handshake, ClientKeyExchange", + [header_prefix(Direction)]), + Message = io_lib:format("~p", [?rec_info(client_key_exchange, ClientKeyExchange)]), + {Header, Message}; +parse_handshake(Direction, #certificate_verify{} = CertificateVerify) -> + Header = io_lib:format("~s Handshake, CertificateVerify", + [header_prefix(Direction)]), + Message = io_lib:format("~p", [?rec_info(certificate_verify, CertificateVerify)]), + {Header, Message}; +parse_handshake(Direction, #finished{} = Finished) -> + Header = io_lib:format("~s Handshake, Finished", + [header_prefix(Direction)]), + Message = io_lib:format("~p", [?rec_info(finished, Finished)]), + {Header, Message}; +parse_handshake(Direction, #hello_request{} = HelloRequest) -> + Header = io_lib:format("~s Handshake, HelloRequest", + [header_prefix(Direction)]), + Message = io_lib:format("~p", [?rec_info(hello_request, HelloRequest)]), + {Header, Message}; +parse_handshake(Direction, #certificate_request_1_3{} = CertificateRequest) -> + Header = io_lib:format("~s Handshake, CertificateRequest", + [header_prefix(Direction)]), + Message = io_lib:format("~p", [?rec_info(certificate_request_1_3, CertificateRequest)]), + {Header, Message}; +parse_handshake(Direction, #certificate_1_3{} = Certificate) -> + Header = io_lib:format("~s Handshake, Certificate", + [header_prefix(Direction)]), + Message = io_lib:format("~p", [?rec_info(certificate_1_3, Certificate)]), + {Header, Message}; +parse_handshake(Direction, #certificate_verify_1_3{} = CertificateVerify) -> + Header = io_lib:format("~s Handshake, CertificateVerify", + [header_prefix(Direction)]), + Message = io_lib:format("~p", [?rec_info(certificate_verify_1_3, CertificateVerify)]), + {Header, Message}; +parse_handshake(Direction, #encrypted_extensions{} = EncryptedExtensions) -> + Header = io_lib:format("~s Handshake, EncryptedExtensions", + [header_prefix(Direction)]), + Message = io_lib:format("~p", [?rec_info(encrypted_extensions, EncryptedExtensions)]), + {Header, Message}. + + +parse_cipher_suites([_|_] = Ciphers) -> + [format_cipher(C) || C <- Ciphers]. + +format_cipher(C0) -> + try ssl_cipher_format:suite_bin_to_map(C0) of + Map -> + ssl_cipher_format:suite_map_to_str(Map) + catch + error:function_clause -> + format_uknown_cipher_suite(C0) + end. + +get_client_version(Version, Extensions) -> + CHVersions = maps:get(client_hello_versions, Extensions, undefined), + case CHVersions of + #client_hello_versions{versions = [Highest|_]} -> + Highest; + undefined -> + Version + end. + +get_server_version(Version, Extensions) -> + SHVersion = maps:get(server_hello_selected_version, Extensions, undefined), + case SHVersion of + #server_hello_selected_version{selected_version = SelectedVersion} -> + SelectedVersion; + undefined -> + Version + end. + +version({3,4}) -> + "TLS 1.3"; +version({3,3}) -> + "TLS 1.2"; +version({3,2}) -> + "TLS 1.1"; +version({3,1}) -> + "TLS 1.0"; +version({3,0}) -> + "SSL 3.0"; +version({254,253}) -> + "DTLS 1.2"; +version({254,255}) -> + "DTLS 1.0"; +version({M,N}) -> + io_lib:format("TLS/DTLS [0x0~B0~B]", [M,N]). + +header_prefix(inbound) -> + "<<<"; +header_prefix(outbound) -> + ">>>". + + +%%------------------------------------------------------------------------- +%% TLS Record Protocol +%%------------------------------------------------------------------------- +format_tls_record(Direction, BinMsg) -> + {Message, Size} = convert_to_hex('tls_record', BinMsg), + Header = io_lib:format("~s (~B bytes) ~s~n", + [header_prefix_tls_record(Direction), + Size, + tls_record_version(BinMsg)]), + Header ++ Message. + + +header_prefix_tls_record(inbound) -> + "reading"; +header_prefix_tls_record(outbound) -> + "writing". + + +tls_record_version([<<?BYTE(B),?BYTE(3),?BYTE(3),_/binary>>|_]) -> + io_lib:format("TLS 1.2 Record Protocol, ~s", [msg_type(B)]); +tls_record_version([<<?BYTE(B),?BYTE(3),?BYTE(2),_/binary>>|_]) -> + io_lib:format("TLS 1.1 Record Protocol, ~s", [msg_type(B)]); +tls_record_version([<<?BYTE(B),?BYTE(3),?BYTE(1),_/binary>>|_]) -> + io_lib:format("TLS 1.0 Record Protocol, ~s", [msg_type(B)]); +tls_record_version([<<?BYTE(B),?BYTE(3),?BYTE(0),_/binary>>|_]) -> + io_lib:format("SSL 3.0 Record Protocol, ~s", [msg_type(B)]); +tls_record_version([<<?BYTE(B),?BYTE(254),?BYTE(253),_/binary>>|_]) -> + io_lib:format("DTLS 1.2 Record Protocol, ~s", [msg_type(B)]); +tls_record_version([<<?BYTE(B),?BYTE(254),?BYTE(255),_/binary>>|_]) -> + io_lib:format("DTLS 1.0 Record Protocol, ~s", [msg_type(B)]); +tls_record_version([<<?BYTE(B),?BYTE(M),?BYTE(N),_/binary>>|_]) -> + io_lib:format("TLS/DTLS [0x0~B0~B] Record Protocol, ~s", [M, N, msg_type(B)]). + + +msg_type(20) -> "change_cipher_spec"; +msg_type(21) -> "alert"; +msg_type(22) -> "handshake"; +msg_type(23) -> "application_data"; +msg_type(_) -> unknown. + + +%%------------------------------------------------------------------------- +%% Hex encoding functions +%%------------------------------------------------------------------------- +convert_to_hex(Protocol, BinMsg) -> + convert_to_hex(Protocol, BinMsg, [], [], 0). +%% +convert_to_hex(P, [], Row0, Acc, C) when C rem 16 =:= 0 -> + Row = lists:reverse(end_row(P, Row0)), + {lists:reverse(Acc) ++ Row ++ io_lib:nl(), C}; +convert_to_hex(P, [], Row0, Acc, C) -> + Row = lists:reverse(end_row(P, Row0)), + Padding = calculate_padding(Row0, Acc), + PaddedRow = string:pad(Row, Padding, leading, $ ), + {lists:reverse(Acc) ++ PaddedRow ++ io_lib:nl(), C}; +convert_to_hex(P, [H|T], Row, Acc, C) when is_list(H) -> + convert_to_hex(P, H ++ T, Row, Acc, C); +convert_to_hex(P, [<<>>|T], Row, Acc, C) -> + convert_to_hex(P, T, Row, Acc, C); + +%% First line +convert_to_hex(P, [<<A:4,B:4,R/binary>>|T], Row, Acc, C) when C =:= 0 -> + convert_to_hex(P, [<<R/binary>>|T], + update_row(<<A:4,B:4>>, Row), + prepend_first_row(P, A, B, Acc, C), + C + 1); +%% New line +convert_to_hex(P, [<<A:4,B:4,R/binary>>|T], Row, Acc, C) when C rem 16 =:= 0 -> + convert_to_hex(P, [<<R/binary>>|T], + update_row(<<A:4,B:4>>, []), + prepend_row(P, A, B, Row, Acc, C), + C + 1); +%% Add 8th hex with extra whitespace +%% 0000 - 16 03 02 00 bd 01 00 00 b9 ... +%% ^^^^ +convert_to_hex(P, [<<A:4,B:4,R/binary>>|T], Row, Acc, C) when C rem 8 =:= 7 -> + convert_to_hex(P, [<<R/binary>>|T], + update_row(<<A:4,B:4>>, Row), + prepend_eighths_hex(A, B, Acc), + C + 1); +convert_to_hex(P, [<<A:4,B:4,R/binary>>|T], Row, Acc, C) -> + convert_to_hex(P, [<<R/binary>>|T], + update_row(<<A:4,B:4>>, Row), + prepend_hex(A, B, Acc), + C + 1); +%% First line +convert_to_hex(P, [H|T], Row, Acc, C) when is_integer(H), C =:= 0 -> + convert_to_hex(P, T, + update_row(H, Row), + prepend_first_row(P, H, Acc, C), + C + 1); +%% New line +convert_to_hex(P, [H|T], Row, Acc, C) when is_integer(H), C rem 16 =:= 0 -> + convert_to_hex(P, T, + update_row(H, []), + prepend_row(P, H, Row, Acc, C), + C + 1); +%% Add 8th hex with extra whitespace +%% 0000 - 16 03 02 00 bd 01 00 00 b9 ... +%% ^^^^ +convert_to_hex(P, [H|T], Row, Acc, C) when is_integer(H), C rem 8 =:= 7 -> + convert_to_hex(P, T, + update_row(H, Row), + prepend_eighths_hex(H, Acc), + C + 1); +convert_to_hex(P, [H|T], Row, Acc, C) when is_integer(H) -> + convert_to_hex(P, T, + update_row(H, Row), + prepend_hex(H, Acc), + C + 1). + + +row_prefix(_ , N) -> + S = string:pad(string:to_lower(erlang:integer_to_list(N, 16)),4,leading,$0), + lists:reverse(lists:flatten(S ++ " - ")). + + +end_row(_, Row) -> + Row ++ " ". + + +%% Calculate padding of the "printable character" lines in order to be +%% visually aligned. +calculate_padding(Row, Acc) -> + %% Number of new line characters + NNL = (length(Acc) div 75) * length(io_lib:nl()), + %% Length of the last printed line + Length = (length(Acc) - NNL) rem 75, + %% Adjusted length of the last printed line + PaddedLength = 75 - (16 - length(Row)), %% Length + %% Padding + PaddedLength - Length. + + +%%------------------------------------------------------------------------- +%% Functions operating on reversed lists +%%------------------------------------------------------------------------- +update_row(B, Row) when is_binary(B) -> + case binary_to_list(B) of + [C] when 32 =< C, C =< 126 -> + [C|Row]; + _Else -> + [$.|Row] + end; +update_row(C, Row) when 32 =< C, C =< 126 -> + [C|Row]; +update_row(_, Row) -> + [$.|Row]. + + +prepend_first_row(P, A, B, Acc, C) -> + prepend_hex(A, B,row_prefix(P, C) ++ Acc). +%% +prepend_first_row(P, N, Acc, C) -> + prepend_hex(N,row_prefix(P, C) ++ Acc). + +prepend_row(P, A, B, Row, Acc, C) -> + prepend_hex(A, B,row_prefix(P, C) ++ io_lib:nl() ++ end_row(P, Row) ++ Acc). +%% +prepend_row(P, N, Row, Acc, C) -> + prepend_hex(N,row_prefix(P, C) ++ io_lib:nl() ++ end_row(P, Row) ++ Acc). + + + +prepend_hex(A, B, Acc) -> + [$ ,?DEC2HEX(B),?DEC2HEX(A)|Acc]. +%% +prepend_hex(N, Acc) -> + " " ++ number_to_hex(N) ++ Acc. + + +prepend_eighths_hex(A, B, Acc) -> + [$ ,$ ,?DEC2HEX(B),?DEC2HEX(A)|Acc]. +%% +prepend_eighths_hex(N, Acc) -> + " " ++ number_to_hex(N) ++ Acc. + +number_to_hex(N) -> + case string:to_lower(erlang:integer_to_list(N, 16)) of + H when length(H) < 2 -> + lists:append(H, "0"); + H -> + lists:reverse(H) + end. + +format_uknown_cipher_suite(<<?BYTE(X), ?BYTE(Y)>>) -> + "0x" ++ number_to_hex(X) ++ "0x" ++ number_to_hex(Y). + diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl index c56675b691..456a560bf6 100644 --- a/lib/ssl/src/ssl_manager.erl +++ b/lib/ssl/src/ssl_manager.erl @@ -516,10 +516,10 @@ last_delay_timer({{_,_},_}, TRef, {LastServer, _}) -> last_delay_timer({_,_}, TRef, {_, LastClient}) -> {TRef, LastClient}. -%% If we can not generate a not allready in use session ID in +%% If we cannot generate a not allready in use session ID in %% ?GEN_UNIQUE_ID_MAX_TRIES we make the new session uncacheable The %% value of ?GEN_UNIQUE_ID_MAX_TRIES is stolen from open SSL which -%% states : "If we can not find a session id in +%% states : "If we cannot find a session id in %% ?GEN_UNIQUE_ID_MAX_TRIES either the RAND code is broken or someone %% is trying to open roughly very close to 2^128 (or 2^256) SSL %% sessions to our server" @@ -530,7 +530,7 @@ new_id(Port, Tries, Cache, CacheCb) -> case CacheCb:lookup(Cache, {Port, Id}) of undefined -> Now = erlang:monotonic_time(), - %% New sessions can not be set to resumable + %% New sessions cannot be set to resumable %% until handshake is compleate and the %% other session values are set. CacheCb:update(Cache, {Port, Id}, #session{session_id = Id, diff --git a/lib/ssl/src/ssl_pkix_db.erl b/lib/ssl/src/ssl_pkix_db.erl index f7ddbd060e..dec48fa914 100644 --- a/lib/ssl/src/ssl_pkix_db.erl +++ b/lib/ssl/src/ssl_pkix_db.erl @@ -27,6 +27,7 @@ -include("ssl_internal.hrl"). -include_lib("public_key/include/public_key.hrl"). -include_lib("kernel/include/file.hrl"). +-include_lib("kernel/include/logger.hrl"). -export([create/1, create_pem_cache/1, add_crls/3, remove_crls/2, remove/1, add_trusted_certs/3, @@ -311,7 +312,7 @@ decode_certs(Ref, Cert) -> error:_ -> Report = io_lib:format("SSL WARNING: Ignoring a CA cert as " "it could not be correctly decoded.~n", []), - error_logger:info_report(Report), + ?LOG_NOTICE(Report), undefined end. diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl index 1a36b2dba8..867d2cfc5a 100644 --- a/lib/ssl/src/ssl_record.erl +++ b/lib/ssl/src/ssl_record.erl @@ -25,6 +25,7 @@ -module(ssl_record). -include("ssl_record.hrl"). +-include("ssl_connection.hrl"). -include("ssl_internal.hrl"). -include("ssl_cipher.hrl"). -include("ssl_alert.hrl"). @@ -39,7 +40,8 @@ set_renegotiation_flag/2, set_client_verify_data/3, set_server_verify_data/3, - empty_connection_state/2, initial_connection_state/2, record_protocol_role/1]). + empty_connection_state/2, initial_connection_state/2, record_protocol_role/1, + step_encryption_state/1]). %% Compression -export([compress/3, uncompress/3, compressions/0]). @@ -120,6 +122,22 @@ activate_pending_connection_state(#{current_write := Current, }. %%-------------------------------------------------------------------- +-spec step_encryption_state(#state{}) -> #state{}. +%% +%% Description: Activates the next encyrption state (e.g. handshake +%% encryption). +%%-------------------------------------------------------------------- +step_encryption_state(#state{connection_states = + #{pending_read := PendingRead, + pending_write := PendingWrite} = ConnStates} = State) -> + NewRead = PendingRead#{sequence_number => 0}, + NewWrite = PendingWrite#{sequence_number => 0}, + State#state{connection_states = + ConnStates#{current_read => NewRead, + current_write => NewWrite}}. + + +%%-------------------------------------------------------------------- -spec set_security_params(#security_parameters{}, #security_parameters{}, connection_states()) -> connection_states(). %% @@ -280,13 +298,12 @@ compress(?NULL, Data, CS) -> {Data, CS}. %%-------------------------------------------------------------------- --spec compressions() -> [binary()]. +-spec compressions() -> [integer()]. %% %% Description: return a list of compressions supported (currently none) %%-------------------------------------------------------------------- compressions() -> - [?byte(?NULL)]. - + [?NULL]. %%==================================================================== %% Payload encryption/decryption @@ -378,7 +395,7 @@ 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 + case ssl_cipher:aead_decrypt(Type, Key, Nonce, CipherText, CipherTag, AAD) of Content when is_binary(Content) -> Content; _ -> @@ -454,34 +471,43 @@ initial_security_params(ConnectionEnd) -> -define(end_additional_data(AAD, Len), << (begin(AAD)end)/binary, ?UINT16(begin(Len)end) >>). -do_cipher_aead(?CHACHA20_POLY1305 = Type, Fragment, #cipher_state{key=Key} = CipherState, AAD0) -> +do_cipher_aead(?CHACHA20_POLY1305 = Type, Fragment, #cipher_state{key=Key, tag_len = TagLen} = CipherState, AAD0) -> 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), + Nonce = chacha_nonce(CipherState), + {Content, CipherTag} = ssl_cipher:aead_encrypt(Type, Key, Nonce, Fragment, AAD, TagLen), {<<Content/binary, CipherTag/binary>>, CipherState}; -do_cipher_aead(Type, Fragment, #cipher_state{key=Key, nonce = ExplicitNonce} = CipherState, AAD0) -> +do_cipher_aead(Type, Fragment, #cipher_state{key=Key, tag_len = TagLen, nonce = ExplicitNonce} = CipherState, AAD0) -> 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, CipherTag} = ssl_cipher:aead_encrypt(Type, Key, Nonce, Fragment, AAD, TagLen), {<<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}) -> + +chacha_nonce(#cipher_state{nonce = Nonce, iv = IV}) -> + crypto:exor(<<?UINT32(0), Nonce/binary>>, IV). + +encrypt_nonce(Type, #cipher_state{iv = IV, nonce = ExplicitNonce}) when Type == ?AES_GCM; + Type == ?AES_CCM; + Type == ?AES_CCM_8 -> <<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>>. +decrypt_nonce(?CHACHA20_POLY1305, CipherState, _) -> + chacha_nonce(CipherState); +decrypt_nonce(Type, #cipher_state{iv = <<Salt:4/bytes, _/binary>>}, <<ExplicitNonce:8/bytes, _/binary>>) when + Type == ?AES_GCM; + Type == ?AES_CCM; + Type == ?AES_CCM_8 -> + <<Salt/binary, ExplicitNonce/binary>>. -compile({inline, [aead_ciphertext_split/4]}). aead_ciphertext_split(?CHACHA20_POLY1305, #cipher_state{tag_len = Len}, CipherTextFragment, AAD) -> CipherLen = byte_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) -> +aead_ciphertext_split(Type, #cipher_state{tag_len = Len}, CipherTextFragment, AAD) when Type == ?AES_GCM; + Type == ?AES_CCM; + Type == ?AES_CCM_8 -> CipherLen = byte_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}. diff --git a/lib/ssl/src/ssl_record.hrl b/lib/ssl/src/ssl_record.hrl index a927fba0de..6d4d47cedb 100644 --- a/lib/ssl/src/ssl_record.hrl +++ b/lib/ssl/src/ssl_record.hrl @@ -74,7 +74,7 @@ -define(INITIAL_BYTES, 5). -define(MAX_SEQENCE_NUMBER, 18446744073709551615). %% (1 bsl 64) - 1 = 18446744073709551615 -%% Sequence numbers can not wrap so when max is about to be reached we should renegotiate. +%% Sequence numbers cannot wrap so when max is about to be reached we should renegotiate. %% We will renegotiate a little before so that there will be sequence numbers left %% for the rehandshake and a little data. Currently we decided to renegotiate a little more %% often as we can have a cheaper test to check if it is time to renegotiate. It will still @@ -96,6 +96,11 @@ -define(AES_CBC, 7). -define(AES_GCM, 8). -define(CHACHA20_POLY1305, 9). +%% Following two are not defined in any RFC but we want to have the +%% same type of handling internaly, all of these "bulk_cipher_algorithm" +%% enums are only used internaly anyway. +-define(AES_CCM, 10). +-define(AES_CCM_8, 11). %% CipherType -define(STREAM, 0). @@ -140,6 +145,7 @@ -define(ALERT, 21). -define(HANDSHAKE, 22). -define(APPLICATION_DATA, 23). +-define(HEARTBEAT, 24). -define(KNOWN_RECORD_TYPE(Type), (is_integer(Type) andalso (20 =< (Type)) andalso ((Type) =< 23))). -define(MAX_PLAIN_TEXT_LENGTH, 16384). diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl index 114710a92e..a05858221a 100644 --- a/lib/ssl/src/tls_connection.erl +++ b/lib/ssl/src/tls_connection.erl @@ -37,7 +37,8 @@ -include("ssl_api.hrl"). -include("ssl_internal.hrl"). -include("ssl_srp.hrl"). --include_lib("public_key/include/public_key.hrl"). +-include_lib("public_key/include/public_key.hrl"). +-include_lib("kernel/include/logger.hrl"). %% Internal application API @@ -49,7 +50,8 @@ handle_protocol_record/3]). %% Handshake handling --export([renegotiation/2, renegotiate/2, send_handshake/2, +-export([renegotiation/2, renegotiate/2, send_handshake/2, + send_handshake_flight/1, queue_handshake/2, queue_change_cipher/2, reinit/1, reinit_handshake_data/1, select_sni_extension/1, empty_connection_state/2]). @@ -66,9 +68,26 @@ -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]). +-export([encode_handshake/4]). -define(DIST_CNTRL_SPAWN_OPTS, [{priority, max}]). @@ -173,25 +192,32 @@ next_record(State) -> next_record(State, CipherTexts, ConnectionStates, Check) -> next_record(State, CipherTexts, ConnectionStates, Check, []). %% -next_record(State, [#ssl_tls{type = ?APPLICATION_DATA} = CT|CipherTexts], ConnectionStates0, Check, Acc) -> - case tls_record:decode_cipher_text(CT, ConnectionStates0, Check) of - {#ssl_tls{fragment = Fragment}, ConnectionStates} -> - next_record(State, CipherTexts, ConnectionStates, Check, [Fragment|Acc]); - #alert{} = Alert -> - Alert - end; -next_record(State, [CT|CipherTexts], ConnectionStates0, Check, []) -> - case tls_record:decode_cipher_text(CT, ConnectionStates0, Check) of - {Record, ConnectionStates} -> +next_record(#state{connection_env = #connection_env{negotiated_version = Version}} = State, + [CT|CipherTexts], ConnectionStates0, Check, Acc) -> + case tls_record:decode_cipher_text(Version, CT, ConnectionStates0, Check) of + {#ssl_tls{type = ?APPLICATION_DATA, fragment = Fragment}, ConnectionStates} -> + case CipherTexts of + [] -> + %% End of cipher texts - build and deliver an ?APPLICATION_DATA record + %% from the accumulated fragments + next_record_done(State, [], ConnectionStates, + #ssl_tls{type = ?APPLICATION_DATA, + fragment = iolist_to_binary(lists:reverse(Acc, [Fragment]))}); + [_|_] -> + next_record(State, CipherTexts, ConnectionStates, Check, [Fragment|Acc]) + end; + {Record, ConnectionStates} when Acc =:= [] -> + %% Singelton non-?APPLICATION_DATA record - deliver next_record_done(State, CipherTexts, ConnectionStates, Record); + {_Record, _ConnectionStates_to_forget} -> + %% Not ?APPLICATION_DATA but we have accumulated fragments + %% -> build an ?APPLICATION_DATA record with concatenated fragments + %% and forget about decrypting this record - we'll decrypt it again next time + next_record_done(State, [CT|CipherTexts], ConnectionStates0, + #ssl_tls{type = ?APPLICATION_DATA, fragment = iolist_to_binary(lists:reverse(Acc))}); #alert{} = Alert -> Alert - end; -next_record(State, CipherTexts, ConnectionStates, _Check, Acc) -> - %% Not ?APPLICATION_DATA but we have a nonempty Acc - %% -> build an ?APPLICATION_DATA record with the accumulated fragments - next_record_done(State, CipherTexts, ConnectionStates, - #ssl_tls{type = ?APPLICATION_DATA, fragment = iolist_to_binary(lists:reverse(Acc))}). + end. next_record_done(#state{protocol_buffers = Buffers} = State, CipherTexts, ConnectionStates, Record) -> {Record, @@ -209,7 +235,8 @@ next_event(StateName, no_record, State0, Actions) -> {#ssl_tls{} = Record, State} -> {next_state, StateName, State, [{next_event, internal, {protocol_record, Record}} | Actions]}; #alert{} = Alert -> - {next_state, StateName, State0, [{next_event, internal, Alert} | Actions]} + Version = State0#state.connection_env#connection_env.negotiated_version, + ssl_connection:handle_own_alert(Alert, Version, StateName, State0) end; next_event(StateName, Record, State, Actions) -> case Record of @@ -218,7 +245,8 @@ next_event(StateName, Record, State, Actions) -> #ssl_tls{} = Record -> {next_state, StateName, State, [{next_event, internal, {protocol_record, Record}} | Actions]}; #alert{} = Alert -> - {next_state, StateName, State, [{next_event, internal, Alert} | Actions]} + Version = State#state.connection_env#connection_env.negotiated_version, + ssl_connection:handle_own_alert(Alert, Version, StateName, State) end. @@ -244,8 +272,12 @@ handle_protocol_record(#ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, Stat {stop, _, _} = Stop-> Stop; {Record, State1} -> - {next_state, StateName, State, Actions} = next_event(StateName, Record, State1), - ssl_connection:hibernate_after(StateName, State, Actions) + case next_event(StateName, Record, State1) of + {next_state, StateName, State, Actions} -> + ssl_connection:hibernate_after(StateName, State, Actions); + {stop, _, _} = Stop -> + Stop + end end; %%% TLS record protocol level handshake messages handle_protocol_record(#ssl_tls{type = ?HANDSHAKE, fragment = Data}, @@ -254,7 +286,8 @@ handle_protocol_record(#ssl_tls{type = ?HANDSHAKE, fragment = Data}, connection_env = #connection_env{negotiated_version = Version}, ssl_options = Options} = State0) -> try - {Packets, Buf} = tls_handshake:get_tls_handshake(Version,Data,Buf0, Options), + EffectiveVersion = effective_version(Version, Options), + {Packets, Buf} = tls_handshake:get_tls_handshake(EffectiveVersion,Data,Buf0, Options), State = State0#state{protocol_buffers = Buffers#protocol_buffers{tls_handshake_buffer = Buf}}, @@ -290,9 +323,7 @@ handle_protocol_record(#ssl_tls{type = ?ALERT, fragment = EncAlerts}, StateName, handle_alerts(Alerts, {next_state, StateName, State}); [] -> ssl_connection:handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, empty_alert), - Version, StateName, State); - #alert{} = Alert -> - ssl_connection:handle_own_alert(Alert, Version, StateName, State) + Version, StateName, State) catch _:_ -> ssl_connection:handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, alert_decode_error), @@ -335,12 +366,17 @@ renegotiate(#state{static_env = #static_env{role = server, send_handshake(Handshake, State) -> send_handshake_flight(queue_handshake(Handshake, State)). + queue_handshake(Handshake, #state{handshake_env = #handshake_env{tls_handshake_history = Hist0} = HsEnv, connection_env = #connection_env{negotiated_version = Version}, flight_buffer = Flight0, + ssl_options = SslOpts, connection_states = ConnectionStates0} = State0) -> {BinHandshake, ConnectionStates, Hist} = encode_handshake(Handshake, Version, ConnectionStates0, Hist0), + ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'handshake', Handshake), + ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'record', BinHandshake), + State0#state{connection_states = ConnectionStates, handshake_env = HsEnv#handshake_env{tls_handshake_history = Hist}, flight_buffer = Flight0 ++ [BinHandshake]}. @@ -351,11 +387,14 @@ send_handshake_flight(#state{static_env = #static_env{socket = Socket, tls_socket:send(Transport, Socket, Flight), {State0#state{flight_buffer = []}, []}. + queue_change_cipher(Msg, #state{connection_env = #connection_env{negotiated_version = Version}, flight_buffer = Flight0, + ssl_options = SslOpts, connection_states = ConnectionStates0} = State0) -> {BinChangeCipher, ConnectionStates} = encode_change_cipher(Msg, Version, ConnectionStates0), + ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'record', BinChangeCipher), State0#state{connection_states = ConnectionStates, flight_buffer = Flight0 ++ [BinChangeCipher]}. @@ -375,8 +414,8 @@ reinit_handshake_data(#state{handshake_env = HsEnv} =State) -> premaster_secret = undefined} }. -select_sni_extension(#client_hello{extensions = HelloExtensions}) -> - HelloExtensions#hello_extensions.sni; +select_sni_extension(#client_hello{extensions = #{sni := SNI}}) -> + SNI; select_sni_extension(_) -> undefined. @@ -386,6 +425,7 @@ empty_connection_state(ConnectionEnd, BeastMitigation) -> %%==================================================================== %% Alert and close handling %%==================================================================== + %%-------------------------------------------------------------------- -spec encode_alert(#alert{}, ssl_record:ssl_version(), ssl_record:connection_states()) -> {iolist(), ssl_record:connection_states()}. @@ -398,10 +438,12 @@ encode_alert(#alert{} = Alert, Version, ConnectionStates) -> send_alert(Alert, #state{static_env = #static_env{socket = Socket, transport_cb = Transport}, connection_env = #connection_env{negotiated_version = Version}, + ssl_options = SslOpts, connection_states = ConnectionStates0} = StateData0) -> {BinMsg, ConnectionStates} = encode_alert(Alert, Version, ConnectionStates0), tls_socket:send(Transport, Socket, BinMsg), + ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'record', BinMsg), StateData0#state{connection_states = ConnectionStates}. %% If an ALERT sent in the connection state, should cause the TLS @@ -488,22 +530,27 @@ init({call, From}, {start, Timeout}, session = #session{own_certificate = Cert} = Session0, connection_states = ConnectionStates0 } = State0) -> + KeyShare = maybe_generate_client_shares(SslOpts), Hello = tls_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts, - Cache, CacheCb, Renegotiation, Cert), - - Version = Hello#client_hello.client_version, - HelloVersion = tls_record:hello_version(Version, SslOpts#ssl_options.versions), + Cache, CacheCb, Renegotiation, Cert, KeyShare), + + HelloVersion = tls_record:hello_version(SslOpts#ssl_options.versions), Handshake0 = ssl_handshake:init_handshake_history(), {BinMsg, ConnectionStates, Handshake} = encode_handshake(Hello, HelloVersion, ConnectionStates0, Handshake0), tls_socket:send(Transport, Socket, BinMsg), + ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'handshake', Hello), + ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'record', BinMsg), + State = State0#state{connection_states = ConnectionStates, - connection_env = CEnv#connection_env{negotiated_version = Version}, %% Requested version + connection_env = CEnv#connection_env{negotiated_version = HelloVersion}, %% Requested version session = Session0#session{session_id = Hello#client_hello.session_id}, handshake_env = HsEnv#handshake_env{tls_handshake_history = Handshake}, - start_or_recv_from = From}, + start_or_recv_from = From, + key_share = KeyShare}, next_event(hello, no_record, State, [{{timeout, handshake}, Timeout, close}]); + init(Type, Event, State) -> gen_handshake(?FUNCTION_NAME, Type, Event, State). @@ -534,14 +581,15 @@ hello(internal, #client_hello{extensions = Extensions} = Hello, start_or_recv_from = From} = State) -> {next_state, user_hello, State#state{start_or_recv_from = undefined, handshake_env = HsEnv#handshake_env{hello = Hello}}, - [{reply, From, {ok, ssl_connection:map_extensions(Extensions)}}]}; + [{reply, From, {ok, Extensions}}]}; hello(internal, #server_hello{extensions = Extensions} = Hello, #state{ssl_options = #ssl_options{handshake = hello}, handshake_env = HsEnv, start_or_recv_from = From} = State) -> {next_state, user_hello, State#state{start_or_recv_from = undefined, handshake_env = HsEnv#handshake_env{hello = Hello}}, - [{reply, From, {ok, ssl_connection:map_extensions(Extensions)}}]}; + [{reply, From, {ok, Extensions}}]}; + hello(internal, #client_hello{client_version = ClientVersion} = Hello, #state{connection_states = ConnectionStates0, static_env = #static_env{ @@ -554,27 +602,40 @@ hello(internal, #client_hello{client_version = ClientVersion} = Hello, connection_env = CEnv, session = #session{own_certificate = Cert} = Session0, ssl_options = SslOpts} = State) -> - case tls_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb, - ConnectionStates0, Cert, KeyExAlg}, Renegotiation) of - #alert{} = Alert -> - ssl_connection:handle_own_alert(Alert, ClientVersion, hello, - State#state{connection_env = - CEnv#connection_env{negotiated_version = ClientVersion}}); - {Version, {Type, Session}, - ConnectionStates, Protocol0, ServerHelloExt, HashSign} -> - Protocol = case Protocol0 of - undefined -> CurrentProtocol; - _ -> Protocol0 - end, - gen_handshake(?FUNCTION_NAME, internal, {common_client_hello, Type, ServerHelloExt}, - State#state{connection_states = ConnectionStates, - connection_env = CEnv#connection_env{negotiated_version = Version}, - handshake_env = HsEnv#handshake_env{ - hashsign_algorithm = HashSign, - client_hello_version = ClientVersion, - negotiated_protocol = Protocol}, - session = Session - }) + + case choose_tls_version(SslOpts, Hello) of + 'tls_v1.3' -> + %% Continue in TLS 1.3 'start' state + {next_state, start, State, [{next_event, internal, Hello}]}; + 'tls_v1.2' -> + case tls_handshake:hello(Hello, + SslOpts, + {Port, Session0, Cache, CacheCb, + ConnectionStates0, Cert, KeyExAlg}, + Renegotiation) of + #alert{} = Alert -> + ssl_connection:handle_own_alert(Alert, ClientVersion, hello, + State#state{connection_env = CEnv#connection_env{negotiated_version + = ClientVersion}}); + {Version, {Type, Session}, + ConnectionStates, Protocol0, ServerHelloExt, HashSign} -> + Protocol = case Protocol0 of + undefined -> CurrentProtocol; + _ -> Protocol0 + end, + gen_handshake(?FUNCTION_NAME, + internal, + {common_client_hello, Type, ServerHelloExt}, + State#state{connection_states = ConnectionStates, + connection_env = CEnv#connection_env{negotiated_version = Version}, + handshake_env = HsEnv#handshake_env{ + hashsign_algorithm = HashSign, + client_hello_version = ClientVersion, + negotiated_protocol = Protocol}, + session = Session + }) + end + end; hello(internal, #server_hello{} = Hello, #state{connection_states = ConnectionStates0, @@ -679,7 +740,7 @@ connection(internal, #hello_request{}, try tls_sender:peer_renegotiate(Pid) of {ok, Write} -> Hello = tls_handshake:client_hello(Host, Port, ConnectionStates, SslOpts, - Cache, CacheCb, Renegotiation, Cert), + Cache, CacheCb, Renegotiation, Cert, undefined), {State, Actions} = send_handshake(Hello, State0#state{connection_states = ConnectionStates#{current_write => Write}}), next_event(hello, no_record, State#state{session = Session0#session{session_id = Hello#client_hello.session_id}}, Actions) @@ -698,7 +759,8 @@ connection(internal, #hello_request{}, ssl_options = SslOpts, connection_states = ConnectionStates} = State0) -> Hello = tls_handshake:client_hello(Host, Port, ConnectionStates, SslOpts, - Cache, CacheCb, Renegotiation, Cert), + Cache, CacheCb, Renegotiation, Cert, undefined), + {State, Actions} = send_handshake(Hello, State0), next_event(hello, no_record, State#state{session = Session0#session{session_id = Hello#client_hello.session_id}}, Actions); @@ -753,14 +815,126 @@ downgrade(info, {CloseTag, Socket}, downgrade(info, Info, State) -> handle_info(Info, ?FUNCTION_NAME, State); downgrade(Type, Event, State) -> - ssl_connection:?FUNCTION_NAME(Type, Event, State, ?MODULE). + 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_1_3(Event, ?FUNCTION_NAME, State); +start(Type, Event, State) -> + gen_handshake_1_3(?FUNCTION_NAME, Type, Event, State). + +%%-------------------------------------------------------------------- +-spec negotiated(gen_statem:event_type(), term(), #state{}) -> + gen_statem:state_function_result(). +%%-------------------------------------------------------------------- +negotiated(info, Event, State) -> + gen_info_1_3(Event, ?FUNCTION_NAME, State); +negotiated(Type, Event, State) -> + gen_handshake_1_3(?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_1_3(Event, ?FUNCTION_NAME, State); +recvd_ch(Type, Event, State) -> + gen_handshake_1_3(?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_1_3(Event, ?FUNCTION_NAME, State); +wait_cert(Type, Event, State) -> + gen_handshake_1_3(?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_1_3(Event, ?FUNCTION_NAME, State); +wait_cv(Type, Event, State) -> + gen_handshake_1_3(?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_1_3(Event, ?FUNCTION_NAME, State); +wait_eoed(Type, Event, State) -> + gen_handshake_1_3(?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_1_3(Event, ?FUNCTION_NAME, State); +wait_finished(Type, Event, State) -> + gen_handshake_1_3(?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_1_3(Event, ?FUNCTION_NAME, State); +wait_flight2(Type, Event, State) -> + gen_handshake_1_3(?FUNCTION_NAME, Type, Event, State). + +%%-------------------------------------------------------------------- +-spec connected(gen_statem:event_type(), term(), #state{}) -> + gen_statem:state_function_result(). +%%-------------------------------------------------------------------- +connected(info, Event, State) -> + gen_info_1_3(Event, ?FUNCTION_NAME, State); +connected(Type, Event, State) -> + gen_handshake_1_3(?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_1_3(Event, ?FUNCTION_NAME, State); +wait_cert_cr(Type, Event, State) -> + gen_handshake_1_3(?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_1_3(Event, ?FUNCTION_NAME, State); +wait_ee(Type, Event, State) -> + gen_handshake_1_3(?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_1_3(Event, ?FUNCTION_NAME, State); +wait_sh(Type, Event, State) -> + gen_handshake_1_3(?FUNCTION_NAME, Type, Event, State). + %-------------------------------------------------------------------- %% gen_statem callbacks %%-------------------------------------------------------------------- callback_mode() -> state_functions. -terminate({shutdown, sender_died, Reason}, _StateName, +terminate({shutdown, {sender_died, Reason}}, _StateName, #state{static_env = #static_env{socket = Socket, transport_cb = Transport}} = State) -> @@ -784,7 +958,6 @@ initial_state(Role, Sender, Host, Port, Socket, {SSLOptions, SocketOptions, Trac #ssl_options{beast_mitigation = BeastMitigation, erl_dist = IsErlDist} = SSLOptions, ConnectionStates = tls_record:init_connection_states(Role, BeastMitigation), - SessionCacheCb = case application:get_env(ssl, session_cb) of {ok, Cb} when is_atom(Cb) -> Cb; @@ -841,8 +1014,9 @@ initialize_tls_sender(#state{static_env = #static_env{ tracker = Tracker }, connection_env = #connection_env{negotiated_version = Version}, - socket_options = SockOpts, - ssl_options = #ssl_options{renegotiate_at = RenegotiateAt}, + socket_options = SockOpts, + ssl_options = #ssl_options{renegotiate_at = RenegotiateAt, + log_level = LogLevel}, connection_states = #{current_write := ConnectionWriteState}, protocol_specific = #{sender := Sender}}) -> Init = #{current_write => ConnectionWriteState, @@ -852,21 +1026,27 @@ initialize_tls_sender(#state{static_env = #static_env{ tracker => Tracker, 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 = #protocol_buffers{tls_record_buffer = Buf0, - tls_cipher_texts = CT0} = Buffers} = State0) -> + tls_cipher_texts = CT0} = Buffers, + ssl_options = SslOpts} = State0) -> Versions = + %% TLS 1.3 Client/Server + %% - Ignore TLSPlaintext.legacy_record_version + %% - Verify that TLSCiphertext.legacy_record_version is set to 0x0303 for all records + %% other than an initial ClientHello, where it MAY also be 0x0301. case StateName of hello -> [tls_record:protocol_version(Vsn) || Vsn <- ?ALL_AVAILABLE_VERSIONS]; _ -> State0#state.connection_env#connection_env.negotiated_version end, - case tls_record:get_tls_records(Data, Versions, Buf0) of + case tls_record:get_tls_records(Data, Versions, Buf0, SslOpts) of {Records, Buf1} -> CT1 = CT0 ++ Records, next_record(State0#state{protocol_buffers = @@ -939,7 +1119,7 @@ handle_info({CloseTag, Socket}, StateName, end; handle_info({'EXIT', Sender, Reason}, _, #state{protocol_specific = #{sender := Sender}} = State) -> - {stop, {shutdown, sender_died, Reason}, State}; + {stop, {shutdown, {sender_died, Reason}}, State}; handle_info(Msg, StateName, State) -> ssl_connection:StateName(info, Msg, State, ?MODULE). @@ -970,6 +1150,7 @@ encode_handshake(Handshake, Version, ConnectionStates0, Hist0) -> encode_change_cipher(#change_cipher_spec{}, Version, ConnectionStates) -> tls_record:encode_change_cipher_spec(Version, ConnectionStates). +-spec decode_alerts(binary()) -> list(). decode_alerts(Bin) -> ssl_alert:decode(Bin). @@ -985,12 +1166,26 @@ gen_handshake(StateName, Type, Event, Version, StateName, State) end. + +gen_handshake_1_3(StateName, Type, Event, + #state{connection_env = #connection_env{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. + + gen_info(Event, connection = StateName, #state{connection_env = #connection_env{negotiated_version = Version}} = State) -> try handle_info(Event, StateName, State) of Result -> Result catch - _:_ -> + _:_ -> ssl_connection:handle_own_alert(?ALERT_REC(?FATAL, ?INTERNAL_ERROR, malformed_data), Version, StateName, State) @@ -1006,6 +1201,29 @@ gen_info(Event, StateName, #state{connection_env = #connection_env{negotiated_ve malformed_handshake_data), Version, StateName, State) end. + +gen_info_1_3(Event, connected = StateName, #state{connection_env = #connection_env{negotiated_version = Version}} = State) -> + try handle_info(Event, StateName, State) of + Result -> + Result + catch + _:_ -> + ssl_connection:handle_own_alert(?ALERT_REC(?FATAL, ?INTERNAL_ERROR, + malformed_data), + Version, StateName, State) + end; + +gen_info_1_3(Event, StateName, #state{connection_env = #connection_env{negotiated_version = Version}} = State) -> + try handle_info(Event, StateName, State) of + Result -> + Result + catch + _:_ -> + ssl_connection:handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, + malformed_handshake_data), + Version, StateName, State) + end. + unprocessed_events(Events) -> %% The first handshake event will be processed immediately @@ -1050,3 +1268,34 @@ ensure_sender_terminate(_, #state{protocol_specific = #{sender := Sender}}) -> end end, spawn(Kill). + +maybe_generate_client_shares(#ssl_options{ + versions = [Version|_], + supported_groups = + #supported_groups{ + supported_groups = Groups}}) + when Version =:= {3,4} -> + ssl_cipher:generate_client_shares(Groups); +maybe_generate_client_shares(_) -> + undefined. + +choose_tls_version(#ssl_options{versions = Versions}, + #client_hello{ + extensions = #{client_hello_versions := + #client_hello_versions{versions = ClientVersions} + } + }) -> + case ssl_handshake:select_supported_version(ClientVersions, Versions) of + {3,4} -> + 'tls_v1.3'; + _Else -> + 'tls_v1.2' + end; +choose_tls_version(_, _) -> + 'tls_v1.2'. + + +effective_version(undefined, #ssl_options{versions = [Version|_]}) -> + Version; +effective_version(Version, _) -> + Version. 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..701a5860c2 --- /dev/null +++ b/lib/ssl/src/tls_connection_1_3.erl @@ -0,0 +1,192 @@ +%% +%% %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"). +-include("tls_handshake.hrl"). +-include("tls_handshake_1_3.hrl"). + +%% gen_statem helper functions +-export([start/4, + negotiated/4, + wait_cert/4, + wait_cv/4, + wait_finished/4 + ]). + + +start(internal, #change_cipher_spec{}, State0, _Module) -> + {Record, State} = tls_connection:next_record(State0), + tls_connection:next_event(?FUNCTION_NAME, Record, State); +start(internal, #client_hello{} = Hello, State0, _Module) -> + case tls_handshake_1_3:do_start(Hello, State0) of + #alert{} = Alert -> + ssl_connection:handle_own_alert(Alert, {3,4}, start, State0); + {State, start} -> + {next_state, start, State, []}; + {State, negotiated} -> + {next_state, negotiated, State, [{next_event, internal, start_handshake}]} + end; +start(Type, Msg, State, Connection) -> + ssl_connection:handle_common_event(Type, Msg, ?FUNCTION_NAME, State, Connection). + + +negotiated(internal, #change_cipher_spec{}, State0, _Module) -> + {Record, State} = tls_connection:next_record(State0), + tls_connection:next_event(?FUNCTION_NAME, Record, State); +negotiated(internal, Message, State0, _Module) -> + case tls_handshake_1_3:do_negotiated(Message, State0) of + #alert{} = Alert -> + ssl_connection:handle_own_alert(Alert, {3,4}, negotiated, State0); + {State, NextState} -> + {next_state, NextState, State, []} + end. + + +wait_cert(internal, #change_cipher_spec{}, State0, _Module) -> + {Record, State} = tls_connection:next_record(State0), + tls_connection:next_event(?FUNCTION_NAME, Record, State); +wait_cert(internal, + #certificate_1_3{} = Certificate, State0, _Module) -> + case tls_handshake_1_3:do_wait_cert(Certificate, State0) of + {#alert{} = Alert, State} -> + ssl_connection:handle_own_alert(Alert, {3,4}, wait_cert, State); + {State1, NextState} -> + {Record, State} = tls_connection:next_record(State1), + tls_connection:next_event(NextState, Record, State) + end; +wait_cert(Type, Msg, State, Connection) -> + ssl_connection:handle_common_event(Type, Msg, ?FUNCTION_NAME, State, Connection). + + +wait_cv(internal, #change_cipher_spec{}, State0, _Module) -> + {Record, State} = tls_connection:next_record(State0), + tls_connection:next_event(?FUNCTION_NAME, Record, State); +wait_cv(internal, + #certificate_verify_1_3{} = CertificateVerify, State0, _Module) -> + case tls_handshake_1_3:do_wait_cv(CertificateVerify, State0) of + {#alert{} = Alert, State} -> + ssl_connection:handle_own_alert(Alert, {3,4}, wait_cv, State); + {State1, NextState} -> + {Record, State} = tls_connection:next_record(State1), + tls_connection:next_event(NextState, Record, State) + end; +wait_cv(Type, Msg, State, Connection) -> + ssl_connection:handle_common_event(Type, Msg, ?FUNCTION_NAME, State, Connection). + + +wait_finished(internal, #change_cipher_spec{}, State0, _Module) -> + {Record, State} = tls_connection:next_record(State0), + tls_connection:next_event(?FUNCTION_NAME, Record, State); +wait_finished(internal, + #finished{} = Finished, State0, Module) -> + case tls_handshake_1_3:do_wait_finished(Finished, State0) of + #alert{} = Alert -> + ssl_connection:handle_own_alert(Alert, {3,4}, finished, State0); + State1 -> + {Record, State} = ssl_connection:prepare_connection(State1, Module), + tls_connection:next_event(connection, Record, State) + end; +wait_finished(Type, Msg, State, Connection) -> + ssl_connection:handle_common_event(Type, Msg, ?FUNCTION_NAME, State, Connection). diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl index 0f0de5936a..2480e05097 100644 --- a/lib/ssl/src/tls_handshake.erl +++ b/lib/ssl/src/tls_handshake.erl @@ -26,15 +26,17 @@ -module(tls_handshake). -include("tls_handshake.hrl"). +-include("tls_handshake_1_3.hrl"). -include("tls_record.hrl"). -include("ssl_alert.hrl"). -include("ssl_internal.hrl"). -include("ssl_cipher.hrl"). -include("ssl_api.hrl"). -include_lib("public_key/include/public_key.hrl"). +-include_lib("kernel/include/logger.hrl"). %% Handshake handling --export([client_hello/8, hello/4]). +-export([client_hello/9, hello/4]). %% Handshake encoding -export([encode_handshake/2]). @@ -49,7 +51,8 @@ %%==================================================================== %%-------------------------------------------------------------------- -spec client_hello(ssl:host(), inet:port_number(), ssl_record:connection_states(), - #ssl_options{}, integer(), atom(), boolean(), der_cert()) -> + #ssl_options{}, integer(), atom(), boolean(), der_cert(), + #key_share_client_hello{} | undefined) -> #client_hello{}. %% %% Description: Creates a client hello message. @@ -59,19 +62,32 @@ client_hello(Host, Port, ConnectionStates, ciphers = UserSuites, fallback = Fallback } = SslOpts, - Cache, CacheCb, Renegotiation, OwnCert) -> + Cache, CacheCb, Renegotiation, OwnCert, KeyShare) -> Version = tls_record:highest_protocol_version(Versions), + + %% In TLS 1.3, the client indicates its version preferences in the + %% "supported_versions" extension (Section 4.2.1) and the + %% legacy_version field MUST be set to 0x0303, which is the version + %% number for TLS 1.2. + LegacyVersion = + case tls_record:is_higher(Version, {3,2}) of + true -> + {3,3}; + false -> + Version + end, #{security_parameters := SecParams} = ssl_record:pending_connection_state(ConnectionStates, read), AvailableCipherSuites = ssl_handshake:available_suites(UserSuites, Version), Extensions = ssl_handshake:client_hello_extensions(Version, AvailableCipherSuites, SslOpts, ConnectionStates, - Renegotiation), + Renegotiation, + KeyShare), CipherSuites = ssl_handshake:cipher_suites(AvailableCipherSuites, Renegotiation, Fallback), Id = ssl_session:client_id({Host, Port, SslOpts}, Cache, CacheCb, OwnCert), #client_hello{session_id = Id, - client_version = Version, + client_version = LegacyVersion, cipher_suites = CipherSuites, compression_methods = ssl_record:compressions(), random = SecParams#security_parameters.client_random, @@ -88,11 +104,69 @@ client_hello(Host, Port, ConnectionStates, ssl_record:connection_states(), alpn | npn, binary() | undefined}| {tls_record:tls_version(), {resumed | new, #session{}}, ssl_record:connection_states(), binary() | undefined, - #hello_extensions{}, {ssl:hash(), ssl:sign_algo()} | + HelloExt::map(), {ssl:hash(), ssl:sign_algo()} | undefined} | #alert{}. %% %% Description: Handles a received hello message %%-------------------------------------------------------------------- + + +%% TLS 1.3 - Section 4.1.3 +%% TLS 1.3 clients receiving a ServerHello indicating TLS 1.2 or below +%% MUST check that the last eight bytes are not equal to either of these +%% values. +hello(#server_hello{server_version = {Major, Minor}, + random = <<_:24/binary,Down:8/binary>>}, + #ssl_options{versions = [{M,N}|_]}, _, _) + when (M > 3 orelse M =:= 3 andalso N >= 4) andalso %% TLS 1.3 client + (Major =:= 3 andalso Minor =:= 3 andalso %% Negotiating TLS 1.2 + Down =:= ?RANDOM_OVERRIDE_TLS12) orelse + + (M > 3 orelse M =:= 3 andalso N >= 4) andalso %% TLS 1.3 client + (Major =:= 3 andalso Minor < 3 andalso %% Negotiating TLS 1.1 or prior + Down =:= ?RANDOM_OVERRIDE_TLS11) -> + ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); + +%% TLS 1.2 clients SHOULD also check that the last eight bytes are not +%% equal to the second value if the ServerHello indicates TLS 1.1 or below. +hello(#server_hello{server_version = {Major, Minor}, + random = <<_:24/binary,Down:8/binary>>}, + #ssl_options{versions = [{M,N}|_]}, _, _) + when (M =:= 3 andalso N =:= 3) andalso %% TLS 1.2 client + (Major =:= 3 andalso Minor < 3 andalso %% Negotiating TLS 1.1 or prior + Down =:= ?RANDOM_OVERRIDE_TLS11) -> + ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); + + +%% TLS 1.3 - 4.2.1. Supported Versions +%% If the "supported_versions" extension in the ServerHello contains a +%% version not offered by the client or contains a version prior to TLS +%% 1.3, the client MUST abort the handshake with an "illegal_parameter" +%% alert. +%%-------------------------------------------------------------------- +%% TLS 1.2 Client +%% +%% - If "supported_version" is present (ServerHello): +%% - Abort handshake with an "illegal_parameter" alert +hello(#server_hello{server_version = Version, + extensions = #{server_hello_selected_version := + #server_hello_selected_version{selected_version = Version}} + }, + #ssl_options{versions = SupportedVersions}, + _ConnectionStates0, _Renegotiation) -> + case tls_record:is_higher({3,4}, Version) of + true -> + ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); + false -> + case tls_record:is_acceptable_version(Version, SupportedVersions) of + true -> + %% Implement TLS 1.3 statem ??? + ?ALERT_REC(?FATAL, ?PROTOCOL_VERSION); + false -> + ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER) + end + end; + hello(#server_hello{server_version = Version, random = Random, cipher_suite = CipherSuite, compression_method = Compression, @@ -107,6 +181,36 @@ hello(#server_hello{server_version = Version, random = Random, false -> ?ALERT_REC(?FATAL, ?PROTOCOL_VERSION) end; + + +%% TLS 1.2 Server +%% - If "supported_versions" is present (ClientHello): +%% - Select version from "supported_versions" (ignore ClientHello.legacy_version) +%% - If server only supports versions greater than "supported_versions": +%% - Abort handshake with a "protocol_version" alert (*) +%% - If "supported_versions" is absent (ClientHello): +%% - Negotiate the minimum of ClientHello.legacy_version and TLS 1.2 (**) +%% - If server only supports versions greater than ClientHello.legacy_version: +%% - Abort handshake with a "protocol_version" alert +%% +%% (*) Sends alert even if there is a gap in supported versions +%% e.g. Server 1.0,1.2 Client 1.1,1.3 +%% (**) Current implementation can negotiate a version not supported by the client +%% e.g. Server 1.0,1.2 Client 1.1 -> ServerHello 1.0 +hello(#client_hello{client_version = _ClientVersion, + cipher_suites = CipherSuites, + extensions = #{client_hello_versions := + #client_hello_versions{versions = ClientVersions} + }} = Hello, + #ssl_options{versions = Versions} = SslOpts, + Info, Renegotiation) -> + try + Version = ssl_handshake:select_supported_version(ClientVersions, Versions), + do_hello(Version, Versions, CipherSuites, Hello, SslOpts, Info, Renegotiation) + catch + _:_ -> + ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, malformed_handshake_data) + end; hello(#client_hello{client_version = ClientVersion, cipher_suites = CipherSuites} = Hello, @@ -114,18 +218,7 @@ hello(#client_hello{client_version = ClientVersion, Info, Renegotiation) -> try Version = ssl_handshake:select_version(tls_record, ClientVersion, Versions), - case ssl_cipher:is_fallback(CipherSuites) 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; - false -> - handle_client_hello(Version, Hello, SslOpts, Info, Renegotiation) - end + do_hello(Version, Versions, CipherSuites, Hello, SslOpts, Info, Renegotiation) catch error:{case_clause,{asn1, Asn1Reason}} -> %% ASN-1 decode of certificate somehow failed @@ -140,7 +233,8 @@ hello(#client_hello{client_version = ClientVersion, %%-------------------------------------------------------------------- %%-------------------------------------------------------------------- --spec encode_handshake(tls_handshake(), tls_record:tls_version()) -> iolist(). +-spec encode_handshake(tls_handshake() | tls_handshake_1_3:tls_handshake_1_3(), + tls_record:tls_version()) -> iolist(). %% %% Description: Encode a handshake packet %%-------------------------------------------------------------------- @@ -157,7 +251,7 @@ encode_handshake(Package, Version) -> %%-------------------------------------------------------------------- -spec get_tls_handshake(tls_record:tls_version(), binary(), binary() | iolist(), #ssl_options{}) -> - {[tls_handshake()], binary()}. + {[{tls_handshake(), binary()}], binary()}. %% %% Description: Given buffered and new data from ssl_record, collects %% and returns it as a list of handshake messages, also returns leftover @@ -176,10 +270,7 @@ handle_client_hello(Version, cipher_suites = CipherSuites, compression_methods = Compressions, random = Random, - extensions = - #hello_extensions{elliptic_curves = Curves, - signature_algs = ClientHashSigns} - = HelloExt}, + extensions = HelloExt}, #ssl_options{versions = Versions, signature_algs = SupportedHashSigns, eccs = SupportedECCs, @@ -188,6 +279,9 @@ handle_client_hello(Version, Renegotiation) -> case tls_record:is_acceptable_version(Version, Versions) of 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), @@ -200,9 +294,11 @@ handle_client_hello(Version, no_suite -> ?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 + #{key_exchange := KeyExAlg} = ssl_cipher_format:suite_bin_to_map(CipherSuite), + case ssl_handshake:select_hashsign({ClientHashSigns, ClientSignatureSchemes}, + Cert, KeyExAlg, + SupportedHashSigns, + Version) of #alert{} = Alert -> Alert; HashSign -> @@ -243,8 +339,26 @@ handle_server_hello_extensions(Version, SessionId, Random, CipherSuite, catch throw:Alert -> Alert end. + + +do_hello(undefined, _Versions, _CipherSuites, _Hello, _SslOpts, _Info, _Renegotiation) -> + ?ALERT_REC(?FATAL, ?PROTOCOL_VERSION); +do_hello(Version, Versions, CipherSuites, Hello, SslOpts, Info, Renegotiation) -> + case ssl_cipher:is_fallback(CipherSuites) 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; + false -> + handle_client_hello(Version, Hello, SslOpts, Info, Renegotiation) + end. + %%-------------------------------------------------------------------- -enc_handshake(#hello_request{}, _Version) -> +enc_handshake(#hello_request{}, {3, N}) when N < 4 -> {?HELLO_REQUEST, <<>>}; enc_handshake(#client_hello{client_version = {Major, Minor}, random = Random, @@ -263,7 +377,8 @@ enc_handshake(#client_hello{client_version = {Major, Minor}, ?BYTE(SIDLength), SessionID/binary, ?UINT16(CsLength), BinCipherSuites/binary, ?BYTE(CmLength), BinCompMethods/binary, ExtensionsBin/binary>>}; - +enc_handshake(HandshakeMsg, {3, 4}) -> + tls_handshake_1_3:encode_handshake(HandshakeMsg); enc_handshake(HandshakeMsg, Version) -> ssl_handshake:encode_handshake(HandshakeMsg, Version). @@ -274,6 +389,7 @@ get_tls_handshake_aux(Version, <<?BYTE(Type), ?UINT24(Length), Raw = <<?BYTE(Type), ?UINT24(Length), Body/binary>>, try decode_handshake(Version, Type, Body) of Handshake -> + ssl_logger:debug(Opts#ssl_options.log_level, inbound, 'handshake', Handshake), get_tls_handshake_aux(Version, Rest, Opts, [{Handshake,Raw} | Acc]) catch _:_ -> @@ -282,24 +398,26 @@ get_tls_handshake_aux(Version, <<?BYTE(Type), ?UINT24(Length), get_tls_handshake_aux(_Version, Data, _, Acc) -> {lists:reverse(Acc), Data}. -decode_handshake(_, ?HELLO_REQUEST, <<>>) -> +decode_handshake({3, N}, ?HELLO_REQUEST, <<>>) when N < 4 -> #hello_request{}; -decode_handshake(_Version, ?CLIENT_HELLO, +decode_handshake(Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, ?BYTE(SID_length), Session_ID:SID_length/binary, ?UINT16(Cs_length), CipherSuites:Cs_length/binary, ?BYTE(Cm_length), Comp_methods:Cm_length/binary, Extensions/binary>>) -> - DecodedExtensions = ssl_handshake:decode_hello_extensions({client, Extensions}), + Exts = ssl_handshake:decode_vector(Extensions), + DecodedExtensions = ssl_handshake:decode_hello_extensions(Exts, Version, {Major, Minor}, + client_hello), #client_hello{ client_version = {Major,Minor}, random = Random, session_id = Session_ID, cipher_suites = ssl_handshake:decode_suites('2_bytes', CipherSuites), - compression_methods = Comp_methods, + compression_methods = erlang:binary_to_list(Comp_methods), extensions = DecodedExtensions }; +decode_handshake({3, 4}, Tag, Msg) -> + tls_handshake_1_3:decode_handshake(Tag, Msg); decode_handshake(Version, Tag, Msg) -> ssl_handshake:decode_handshake(Version, Tag, Msg). - - diff --git a/lib/ssl/src/tls_handshake.hrl b/lib/ssl/src/tls_handshake.hrl index f6644f64af..fc67bb61fd 100644 --- a/lib/ssl/src/tls_handshake.hrl +++ b/lib/ssl/src/tls_handshake.hrl @@ -32,6 +32,7 @@ client_version, random, session_id, % opaque SessionID<0..32> + cookie, % opaque<2..2^16-1> cipher_suites, % cipher_suites<2..2^16-1> compression_methods, % compression_methods<1..2^8-1>, %% Extensions diff --git a/lib/ssl/src/tls_handshake_1_3.erl b/lib/ssl/src/tls_handshake_1_3.erl new file mode 100644 index 0000000000..8a4ad922e1 --- /dev/null +++ b/lib/ssl/src/tls_handshake_1_3.erl @@ -0,0 +1,1343 @@ +%% +%% %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: Help funtions for handling the TLS 1.3 (specific parts of) +%%% TLS handshake protocol +%%---------------------------------------------------------------------- + +-module(tls_handshake_1_3). + +-include("tls_handshake_1_3.hrl"). +-include("ssl_alert.hrl"). +-include("ssl_cipher.hrl"). +-include("ssl_connection.hrl"). +-include("ssl_internal.hrl"). +-include("ssl_record.hrl"). +-include_lib("public_key/include/public_key.hrl"). + +%% Encode +-export([encode_handshake/1, decode_handshake/2]). + +%% Create handshake messages +-export([certificate/5, + certificate_verify/4, + encrypted_extensions/0, + server_hello/4]). + +-export([do_start/2, + do_negotiated/2, + do_wait_cert/2, + do_wait_cv/2, + do_wait_finished/2]). + +%%==================================================================== +%% Create handshake messages +%%==================================================================== + +server_hello(MsgType, SessionId, KeyShare, ConnectionStates) -> + #{security_parameters := SecParams} = + ssl_record:pending_connection_state(ConnectionStates, read), + Extensions = server_hello_extensions(MsgType, KeyShare), + #server_hello{server_version = {3,3}, %% legacy_version + cipher_suite = SecParams#security_parameters.cipher_suite, + compression_method = 0, %% legacy attribute + random = server_hello_random(MsgType, SecParams), + session_id = SessionId, + extensions = Extensions + }. + +server_hello_extensions(MsgType, KeyShare) -> + SupportedVersions = #server_hello_selected_version{selected_version = {3,4}}, + Extensions = #{server_hello_selected_version => SupportedVersions}, + ssl_handshake:add_server_share(MsgType, Extensions, KeyShare). + +server_hello_random(server_hello, #security_parameters{server_random = Random}) -> + Random; +%% For reasons of backward compatibility with middleboxes (see +%% Appendix D.4), the HelloRetryRequest message uses the same structure +%% as the ServerHello, but with Random set to the special value of the +%% SHA-256 of "HelloRetryRequest": +%% +%% CF 21 AD 74 E5 9A 61 11 BE 1D 8C 02 1E 65 B8 91 +%% C2 A2 11 16 7A BB 8C 5E 07 9E 09 E2 C8 A8 33 9C +server_hello_random(hello_retry_request, _) -> + crypto:hash(sha256, "HelloRetryRequest"). + + +%% TODO: implement support for encrypted_extensions +encrypted_extensions() -> + #encrypted_extensions{ + extensions = #{} + }. + + +certificate_request(SignAlgs0, SignAlgsCert0) -> + %% Input arguments contain TLS 1.2 algorithms due to backward compatibility + %% reasons. These {Hash, Algo} tuples must be filtered before creating the + %% the extensions. + SignAlgs = filter_tls13_algs(SignAlgs0), + SignAlgsCert = filter_tls13_algs(SignAlgsCert0), + Extensions0 = add_signature_algorithms(#{}, SignAlgs), + Extensions = add_signature_algorithms_cert(Extensions0, SignAlgsCert), + #certificate_request_1_3{ + certificate_request_context = <<>>, + extensions = Extensions}. + + +add_signature_algorithms(Extensions, SignAlgs) -> + Extensions#{signature_algorithms => + #signature_algorithms{signature_scheme_list = SignAlgs}}. + + +add_signature_algorithms_cert(Extensions, undefined) -> + Extensions; +add_signature_algorithms_cert(Extensions, SignAlgsCert) -> + Extensions#{signature_algorithms_cert => + #signature_algorithms{signature_scheme_list = SignAlgsCert}}. + + +filter_tls13_algs(undefined) -> undefined; +filter_tls13_algs(Algo) -> + lists:filter(fun is_atom/1, Algo). + + +%% TODO: use maybe monad for error handling! +%% enum { +%% X509(0), +%% RawPublicKey(2), +%% (255) +%% } CertificateType; +%% +%% struct { +%% select (certificate_type) { +%% case RawPublicKey: +%% /* From RFC 7250 ASN.1_subjectPublicKeyInfo */ +%% opaque ASN1_subjectPublicKeyInfo<1..2^24-1>; +%% +%% case X509: +%% opaque cert_data<1..2^24-1>; +%% }; +%% Extension extensions<0..2^16-1>; +%% } CertificateEntry; +%% +%% struct { +%% opaque certificate_request_context<0..2^8-1>; +%% CertificateEntry certificate_list<0..2^24-1>; +%% } Certificate; +certificate(OwnCert, CertDbHandle, CertDbRef, _CRContext, server) -> + case ssl_certificate:certificate_chain(OwnCert, CertDbHandle, CertDbRef) of + {ok, _, Chain} -> + CertList = chain_to_cert_list(Chain), + %% If this message is in response to a CertificateRequest, the value of + %% certificate_request_context in that message. Otherwise (in the case + %%of server authentication), this field SHALL be zero length. + #certificate_1_3{ + certificate_request_context = <<>>, + certificate_list = CertList}; + {error, Error} -> + ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {server_has_no_suitable_certificates, Error}) + end. + + +certificate_verify(PrivateKey, SignatureScheme, + #state{connection_states = ConnectionStates, + handshake_env = + #handshake_env{ + tls_handshake_history = {Messages, _}}}, server) -> + #{security_parameters := SecParamsR} = + ssl_record:pending_connection_state(ConnectionStates, write), + #security_parameters{prf_algorithm = HKDFAlgo} = SecParamsR, + + {HashAlgo, _, _} = + ssl_cipher:scheme_to_components(SignatureScheme), + + Context = lists:reverse(Messages), + + %% Transcript-Hash uses the HKDF hash function defined by the cipher suite. + THash = tls_v1:transcript_hash(Context, HKDFAlgo), + + %% Digital signatures use the hash function defined by the selected signature + %% scheme. + case sign(THash, <<"TLS 1.3, server CertificateVerify">>, + HashAlgo, PrivateKey) of + {ok, Signature} -> + {ok, #certificate_verify_1_3{ + algorithm = SignatureScheme, + signature = Signature + }}; + {error, badarg} -> + {error, badarg} + + end. + + +finished(#state{connection_states = ConnectionStates, + handshake_env = + #handshake_env{ + tls_handshake_history = {Messages, _}}}) -> + #{security_parameters := SecParamsR, + cipher_state := #cipher_state{finished_key = FinishedKey}} = + ssl_record:current_connection_state(ConnectionStates, write), + #security_parameters{prf_algorithm = HKDFAlgo} = SecParamsR, + + VerifyData = tls_v1:finished_verify_data(FinishedKey, HKDFAlgo, Messages), + + #finished{ + verify_data = VerifyData + }. + + +%%==================================================================== +%% Encode handshake +%%==================================================================== + +encode_handshake(#certificate_request_1_3{ + certificate_request_context = Context, + extensions = Exts})-> + EncContext = encode_cert_req_context(Context), + BinExts = encode_extensions(Exts), + {?CERTIFICATE_REQUEST, <<EncContext/binary, BinExts/binary>>}; +encode_handshake(#certificate_1_3{ + certificate_request_context = Context, + certificate_list = Entries}) -> + EncContext = encode_cert_req_context(Context), + EncEntries = encode_cert_entries(Entries), + {?CERTIFICATE, <<EncContext/binary, EncEntries/binary>>}; +encode_handshake(#certificate_verify_1_3{ + algorithm = Algorithm, + signature = Signature}) -> + EncAlgo = encode_algorithm(Algorithm), + EncSign = encode_signature(Signature), + {?CERTIFICATE_VERIFY, <<EncAlgo/binary, EncSign/binary>>}; +encode_handshake(#encrypted_extensions{extensions = Exts})-> + {?ENCRYPTED_EXTENSIONS, encode_extensions(Exts)}; +encode_handshake(#new_session_ticket{ + ticket_lifetime = LifeTime, + ticket_age_add = Age, + ticket_nonce = Nonce, + ticket = Ticket, + extensions = Exts}) -> + TicketSize = byte_size(Ticket), + BinExts = encode_extensions(Exts), + {?NEW_SESSION_TICKET, <<?UINT32(LifeTime), ?UINT32(Age), + ?BYTE(Nonce), ?UINT16(TicketSize), Ticket/binary, + BinExts/binary>>}; +encode_handshake(#end_of_early_data{}) -> + {?END_OF_EARLY_DATA, <<>>}; +encode_handshake(#key_update{request_update = Update}) -> + {?KEY_UPDATE, <<?BYTE(Update)>>}; +encode_handshake(HandshakeMsg) -> + ssl_handshake:encode_handshake(HandshakeMsg, {3,4}). + + +%%==================================================================== +%% Decode handshake +%%==================================================================== + +decode_handshake(?CERTIFICATE_REQUEST, <<?BYTE(0), ?UINT16(Size), EncExts:Size/binary>>) -> + Exts = decode_extensions(EncExts, certificate_request), + #certificate_request_1_3{ + certificate_request_context = <<>>, + extensions = Exts}; +decode_handshake(?CERTIFICATE_REQUEST, <<?BYTE(CSize), Context:CSize/binary, + ?UINT16(Size), EncExts:Size/binary>>) -> + Exts = decode_extensions(EncExts, certificate_request), + #certificate_request_1_3{ + certificate_request_context = Context, + extensions = Exts}; +decode_handshake(?CERTIFICATE, <<?BYTE(0), ?UINT24(Size), Certs:Size/binary>>) -> + CertList = decode_cert_entries(Certs), + #certificate_1_3{ + certificate_request_context = <<>>, + certificate_list = CertList + }; +decode_handshake(?CERTIFICATE, <<?BYTE(CSize), Context:CSize/binary, + ?UINT24(Size), Certs:Size/binary>>) -> + CertList = decode_cert_entries(Certs), + #certificate_1_3{ + certificate_request_context = Context, + certificate_list = CertList + }; +decode_handshake(?CERTIFICATE_VERIFY, <<?UINT16(EncAlgo), ?UINT16(Size), Signature:Size/binary>>) -> + Algorithm = ssl_cipher:signature_scheme(EncAlgo), + #certificate_verify_1_3{ + algorithm = Algorithm, + signature = Signature}; +decode_handshake(?ENCRYPTED_EXTENSIONS, <<?UINT16(Size), EncExts:Size/binary>>) -> + #encrypted_extensions{ + extensions = decode_extensions(EncExts, encrypted_extensions) + }; +decode_handshake(?NEW_SESSION_TICKET, <<?UINT32(LifeTime), ?UINT32(Age), + ?BYTE(Nonce), ?UINT16(TicketSize), Ticket:TicketSize/binary, + BinExts/binary>>) -> + Exts = decode_extensions(BinExts, encrypted_extensions), + #new_session_ticket{ticket_lifetime = LifeTime, + ticket_age_add = Age, + ticket_nonce = Nonce, + ticket = Ticket, + extensions = Exts}; +decode_handshake(?END_OF_EARLY_DATA, _) -> + #end_of_early_data{}; +decode_handshake(?KEY_UPDATE, <<?BYTE(Update)>>) -> + #key_update{request_update = Update}; +decode_handshake(Tag, HandshakeMsg) -> + ssl_handshake:decode_handshake({3,4}, Tag, HandshakeMsg). + +%%-------------------------------------------------------------------- +%%% Internal functions +%%-------------------------------------------------------------------- +encode_cert_req_context(<<>>) -> + <<?BYTE(0)>>; +encode_cert_req_context(Bin) -> + Size = byte_size(Bin), + <<?BYTE(Size), Bin/binary>>. + +encode_cert_entries(Entries) -> + CertEntryList = encode_cert_entries(Entries, []), + Size = byte_size(CertEntryList), + <<?UINT24(Size), CertEntryList/binary>>. + +encode_cert_entries([], Acc) -> + iolist_to_binary(lists:reverse(Acc)); +encode_cert_entries([#certificate_entry{data = Data, + extensions = Exts} | Rest], Acc) -> + DSize = byte_size(Data), + BinExts = encode_extensions(Exts), + encode_cert_entries(Rest, + [<<?UINT24(DSize), Data/binary, BinExts/binary>> | Acc]). + +encode_algorithm(Algo) -> + Scheme = ssl_cipher:signature_scheme(Algo), + <<?UINT16(Scheme)>>. + +encode_signature(Signature) -> + Size = byte_size(Signature), + <<?UINT16(Size), Signature/binary>>. + +decode_cert_entries(Entries) -> + decode_cert_entries(Entries, []). + +decode_cert_entries(<<>>, Acc) -> + lists:reverse(Acc); +decode_cert_entries(<<?UINT24(DSize), Data:DSize/binary, ?UINT16(Esize), BinExts:Esize/binary, + Rest/binary>>, Acc) -> + Exts = decode_extensions(BinExts, certificate_request), + decode_cert_entries(Rest, [#certificate_entry{data = Data, + extensions = Exts} | Acc]). + +encode_extensions(Exts)-> + ssl_handshake:encode_extensions(extensions_list(Exts)). +decode_extensions(Exts, MessageType) -> + ssl_handshake:decode_extensions(Exts, {3,4}, MessageType). + +extensions_list(HelloExtensions) -> + [Ext || {_, Ext} <- maps:to_list(HelloExtensions)]. + + +%% TODO: add extensions! +chain_to_cert_list(L) -> + chain_to_cert_list(L, []). +%% +chain_to_cert_list([], Acc) -> + lists:reverse(Acc); +chain_to_cert_list([H|T], Acc) -> + chain_to_cert_list(T, [certificate_entry(H)|Acc]). + + +certificate_entry(DER) -> + #certificate_entry{ + data = DER, + extensions = #{} %% Extensions not supported. + }. + +%% The digital signature is then computed over the concatenation of: +%% - A string that consists of octet 32 (0x20) repeated 64 times +%% - The context string +%% - A single 0 byte which serves as the separator +%% - The content to be signed +%% +%% For example, if the transcript hash was 32 bytes of 01 (this length +%% would make sense for SHA-256), the content covered by the digital +%% signature for a server CertificateVerify would be: +%% +%% 2020202020202020202020202020202020202020202020202020202020202020 +%% 2020202020202020202020202020202020202020202020202020202020202020 +%% 544c5320312e332c207365727665722043657274696669636174655665726966 +%% 79 +%% 00 +%% 0101010101010101010101010101010101010101010101010101010101010101 +sign(THash, Context, HashAlgo, PrivateKey) -> + Content = build_content(Context, THash), + + %% The length of the Salt MUST be equal to the length of the output + %% of the digest algorithm: rsa_pss_saltlen = -1 + try public_key:sign(Content, HashAlgo, PrivateKey, + [{rsa_padding, rsa_pkcs1_pss_padding}, + {rsa_pss_saltlen, -1}, + {rsa_mgf1_md, HashAlgo}]) of + Signature -> + {ok, Signature} + catch + error:badarg -> + {error, badarg} + end. + + +verify(THash, Context, HashAlgo, Signature, PublicKey) -> + Content = build_content(Context, THash), + + %% The length of the Salt MUST be equal to the length of the output + %% of the digest algorithm: rsa_pss_saltlen = -1 + try public_key:verify(Content, HashAlgo, Signature, PublicKey, + [{rsa_padding, rsa_pkcs1_pss_padding}, + {rsa_pss_saltlen, -1}, + {rsa_mgf1_md, HashAlgo}]) of + Result -> + {ok, Result} + catch + error:badarg -> + {error, badarg} + end. + + +build_content(Context, THash) -> + Prefix = binary:copy(<<32>>, 64), + <<Prefix/binary,Context/binary,?BYTE(0),THash/binary>>. + + +%%==================================================================== +%% Handle handshake messages +%%==================================================================== + + +do_start(#client_hello{cipher_suites = ClientCiphers, + session_id = SessionId, + extensions = Extensions} = _Hello, + #state{connection_states = _ConnectionStates0, + ssl_options = #ssl_options{ciphers = ServerCiphers, + signature_algs = ServerSignAlgs, + supported_groups = ServerGroups0}, + session = #session{own_certificate = Cert}} = State0) -> + + ClientGroups0 = maps:get(elliptic_curves, Extensions, undefined), + ClientGroups = get_supported_groups(ClientGroups0), + ServerGroups = get_supported_groups(ServerGroups0), + + ClientShares0 = maps:get(key_share, Extensions, undefined), + ClientShares = get_key_shares(ClientShares0), + + ClientSignAlgs = get_signature_scheme_list( + maps:get(signature_algs, Extensions, undefined)), + ClientSignAlgsCert = get_signature_scheme_list( + maps:get(signature_algs_cert, Extensions, undefined)), + + %% TODO: use library function if it exists + %% Init the maybe "monad" + {Ref,Maybe} = maybe(), + + try + %% If the server does not select a PSK, then the server independently selects a + %% cipher suite, an (EC)DHE group and key share for key establishment, + %% and a signature algorithm/certificate pair to authenticate itself to + %% the client. + Cipher = Maybe(select_cipher_suite(ClientCiphers, ServerCiphers)), + Groups = Maybe(select_common_groups(ServerGroups, ClientGroups)), + Maybe(validate_key_share(ClientGroups, ClientShares)), + + {PublicKeyAlgo, SignAlgo, SignHash} = get_certificate_params(Cert), + + %% Check if client supports signature algorithm of server certificate + Maybe(check_cert_sign_algo(SignAlgo, SignHash, ClientSignAlgs, ClientSignAlgsCert)), + + %% Select signature algorithm (used in CertificateVerify message). + SelectedSignAlg = Maybe(select_sign_algo(PublicKeyAlgo, ClientSignAlgs, ServerSignAlgs)), + + %% Select client public key. If no public key found in ClientShares or + %% ClientShares is empty, trigger HelloRetryRequest as we were able + %% to find an acceptable set of parameters but the ClientHello does not + %% contain sufficient information. + {Group, ClientPubKey} = get_client_public_key(Groups, ClientShares), + + %% Generate server_share + KeyShare = ssl_cipher:generate_server_share(Group), + + State1 = update_start_state(State0, Cipher, KeyShare, SessionId, + Group, SelectedSignAlg, ClientPubKey), + + %% 4.1.4. Hello Retry Request + %% + %% The server will send this message in response to a ClientHello + %% message if it is able to find an acceptable set of parameters but the + %% ClientHello does not contain sufficient information to proceed with + %% the handshake. + Maybe(send_hello_retry_request(State1, ClientPubKey, KeyShare, SessionId)) + + %% TODO: + %% - session handling + %% - handle extensions: ALPN + %% (do not handle: NPN, srp, renegotiation_info, ec_point_formats) + + catch + {Ref, {insufficient_security, no_suitable_groups}} -> + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_groups); + {Ref, illegal_parameter} -> + ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); + {Ref, no_suitable_cipher} -> + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_cipher); + {Ref, {insufficient_security, no_suitable_signature_algorithm}} -> + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, "No suitable signature algorithm"); + {Ref, {insufficient_security, no_suitable_public_key}} -> + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_public_key) + end. + + +do_negotiated(start_handshake, + #state{connection_states = ConnectionStates0, + session = #session{session_id = SessionId, + own_certificate = OwnCert, + ecc = SelectedGroup, + sign_alg = SignatureScheme, + dh_public_value = ClientKey}, + ssl_options = #ssl_options{} = SslOpts, + key_share = KeyShare, + handshake_env = #handshake_env{tls_handshake_history = _HHistory0}, + connection_env = #connection_env{private_key = CertPrivateKey}, + static_env = #static_env{ + cert_db = CertDbHandle, + cert_db_ref = CertDbRef, + socket = _Socket, + transport_cb = _Transport} + } = State0) -> + {Ref,Maybe} = maybe(), + + try + %% Create server_hello + %% Extensions: supported_versions, key_share, (pre_shared_key) + ServerHello = server_hello(server_hello, SessionId, KeyShare, ConnectionStates0), + + {State1, _} = tls_connection:send_handshake(ServerHello, State0), + + State2 = + calculate_handshake_secrets(ClientKey, SelectedGroup, KeyShare, State1), + + State3 = ssl_record:step_encryption_state(State2), + + %% Create EncryptedExtensions + EncryptedExtensions = encrypted_extensions(), + + %% Encode EncryptedExtensions + State4 = tls_connection:queue_handshake(EncryptedExtensions, State3), + + %% Create and send CertificateRequest ({verify, verify_peer}) + {State5, NextState} = maybe_send_certificate_request(State4, SslOpts), + + %% Create Certificate + Certificate = certificate(OwnCert, CertDbHandle, CertDbRef, <<>>, server), + + %% Encode Certificate + State6 = tls_connection:queue_handshake(Certificate, State5), + + %% Create CertificateVerify + CertificateVerify = Maybe(certificate_verify(CertPrivateKey, SignatureScheme, + State6, server)), + %% Encode CertificateVerify + State7 = tls_connection:queue_handshake(CertificateVerify, State6), + + %% Create Finished + Finished = finished(State7), + + %% Encode Finished + State8 = tls_connection:queue_handshake(Finished, State7), + + %% Send first flight + {State9, _} = tls_connection:send_handshake_flight(State8), + + {State9, NextState} + + catch + {Ref, badarg} -> + ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {digitally_sign, badarg}) + end. + + +do_wait_cert(#certificate_1_3{} = Certificate, State0) -> + {Ref,Maybe} = maybe(), + try + Maybe(process_client_certificate(Certificate, State0)) + catch + {Ref, {certificate_required, State}} -> + {?ALERT_REC(?FATAL, ?CERTIFICATE_REQUIRED, certificate_required), State}; + {Ref, {{certificate_unknown, Reason}, State}} -> + {?ALERT_REC(?FATAL, ?CERTIFICATE_UNKNOWN, Reason), State}; + {Ref, {{internal_error, Reason}, State}} -> + {?ALERT_REC(?FATAL, ?INTERNAL_ERROR, Reason), State}; + {Ref, {{handshake_failure, Reason}, State}} -> + {?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, Reason), State}; + {#alert{} = Alert, State} -> + {Alert, State} + end. + + +do_wait_cv(#certificate_verify_1_3{} = CertificateVerify, State0) -> + {Ref,Maybe} = maybe(), + try + Maybe(verify_signature_algorithm(State0, CertificateVerify)), + Maybe(verify_certificate_verify(State0, CertificateVerify)) + catch + {Ref, {{bad_certificate, Reason}, State}} -> + {?ALERT_REC(?FATAL, ?BAD_CERTIFICATE, {bad_certificate, Reason}), State}; + {Ref, {badarg, State}} -> + {?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {verify, badarg}), State}; + {Ref, {{handshake_failure, Reason}, State}} -> + {?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, {handshake_failure, Reason}), State} + end. + + +do_wait_finished(#finished{verify_data = VerifyData}, + #state{connection_states = _ConnectionStates0, + session = #session{session_id = _SessionId, + own_certificate = _OwnCert}, + ssl_options = #ssl_options{} = _SslOpts, + key_share = _KeyShare, + handshake_env = #handshake_env{tls_handshake_history = _HHistory0}, + static_env = #static_env{ + cert_db = _CertDbHandle, + cert_db_ref = _CertDbRef, + socket = _Socket, + transport_cb = _Transport} + } = State0) -> + + {Ref,Maybe} = maybe(), + + try + Maybe(validate_client_finished(State0, VerifyData)), + + State1 = calculate_traffic_secrets(State0), + + %% Configure traffic keys + ssl_record:step_encryption_state(State1) + + + catch + {Ref, decrypt_error} -> + ?ALERT_REC(?FATAL, ?DECRYPT_ERROR, decrypt_error) + end. + + +%% TODO: Remove this function! +%% not_implemented(State, Reason) -> +%% {error, {not_implemented, State, Reason}}. +%% +%% not_implemented(update_secrets, State0, Reason) -> +%% State1 = calculate_traffic_secrets(State0), +%% State = ssl_record:step_encryption_state(State1), +%% {error, {not_implemented, State, Reason}}. + + + +%% Recipients of Finished messages MUST verify that the contents are +%% correct and if incorrect MUST terminate the connection with a +%% "decrypt_error" alert. +validate_client_finished(#state{connection_states = ConnectionStates, + handshake_env = + #handshake_env{ + tls_handshake_history = {Messages0, _}}}, VerifyData) -> + #{security_parameters := SecParamsR, + cipher_state := #cipher_state{finished_key = FinishedKey}} = + ssl_record:current_connection_state(ConnectionStates, read), + #security_parameters{prf_algorithm = HKDFAlgo} = SecParamsR, + + %% Drop the client's finished message, it is not part of the handshake context + %% when the client calculates its finished message. + [_|Messages] = Messages0, + + ControlData = tls_v1:finished_verify_data(FinishedKey, HKDFAlgo, Messages), + compare_verify_data(ControlData, VerifyData). + + +compare_verify_data(Data, Data) -> + ok; +compare_verify_data(_, _) -> + {error, decrypt_error}. + + +send_hello_retry_request(#state{connection_states = ConnectionStates0} = State0, + no_suitable_key, KeyShare, SessionId) -> + ServerHello = server_hello(hello_retry_request, SessionId, KeyShare, ConnectionStates0), + {State1, _} = tls_connection:send_handshake(ServerHello, State0), + + %% TODO: Fix handshake history! + State2 = replace_ch1_with_message_hash(State1), + + {ok, {State2, start}}; +send_hello_retry_request(State0, _, _, _) -> + %% Suitable key found. + {ok, {State0, negotiated}}. + + +maybe_send_certificate_request(State, #ssl_options{verify = verify_none}) -> + {State, wait_finished}; +maybe_send_certificate_request(State, #ssl_options{ + verify = verify_peer, + signature_algs = SignAlgs, + signature_algs_cert = SignAlgsCert}) -> + CertificateRequest = certificate_request(SignAlgs, SignAlgsCert), + {tls_connection:queue_handshake(CertificateRequest, State), wait_cert}. + + +process_client_certificate(#certificate_1_3{ + certificate_request_context = <<>>, + certificate_list = []}, + #state{ssl_options = + #ssl_options{ + fail_if_no_peer_cert = false}} = State) -> + {ok, {State, wait_finished}}; +process_client_certificate(#certificate_1_3{ + certificate_request_context = <<>>, + certificate_list = []}, + #state{ssl_options = + #ssl_options{ + fail_if_no_peer_cert = true}} = State0) -> + + %% At this point the client believes that the connection is up and starts using + %% its traffic secrets. In order to be able send an proper Alert to the client + %% the server should also change its connection state and use the traffic + %% secrets. + State1 = calculate_traffic_secrets(State0), + State = ssl_record:step_encryption_state(State1), + {error, {certificate_required, State}}; +process_client_certificate(#certificate_1_3{certificate_list = Certs0}, + #state{ssl_options = + #ssl_options{signature_algs = SignAlgs, + signature_algs_cert = SignAlgsCert} = SslOptions, + static_env = + #static_env{ + role = Role, + host = Host, + cert_db = CertDbHandle, + cert_db_ref = CertDbRef, + crl_db = CRLDbHandle}} = State0) -> + %% TODO: handle extensions! + + %% Remove extensions from list of certificates! + Certs = convert_certificate_chain(Certs0), + case is_supported_signature_algorithm(Certs, SignAlgs, SignAlgsCert) of + true -> + case validate_certificate_chain(Certs, CertDbHandle, CertDbRef, + SslOptions, CRLDbHandle, Role, Host) of + {ok, {PeerCert, PublicKeyInfo}} -> + State = store_peer_cert(State0, PeerCert, PublicKeyInfo), + {ok, {State, wait_cv}}; + {error, Reason} -> + State1 = calculate_traffic_secrets(State0), + State = ssl_record:step_encryption_state(State1), + {error, {Reason, State}}; + #alert{} = Alert -> + State1 = calculate_traffic_secrets(State0), + State = ssl_record:step_encryption_state(State1), + {Alert, State} + end; + false -> + State1 = calculate_traffic_secrets(State0), + State = ssl_record:step_encryption_state(State1), + {error, {{handshake_failure, + "Client certificate uses unsupported signature algorithm"}, State}} + end. + + +%% TODO: check whole chain! +is_supported_signature_algorithm(Certs, SignAlgs, undefined) -> + is_supported_signature_algorithm(Certs, SignAlgs); +is_supported_signature_algorithm(Certs, _, SignAlgsCert) -> + is_supported_signature_algorithm(Certs, SignAlgsCert). +%% +is_supported_signature_algorithm([BinCert|_], SignAlgs0) -> + #'OTPCertificate'{signatureAlgorithm = SignAlg} = + public_key:pkix_decode_cert(BinCert, otp), + SignAlgs = filter_tls13_algs(SignAlgs0), + Scheme = ssl_cipher:signature_algorithm_to_scheme(SignAlg), + lists:member(Scheme, SignAlgs). + + +validate_certificate_chain(Certs, CertDbHandle, CertDbRef, SslOptions, CRLDbHandle, Role, Host) -> + ServerName = ssl_handshake:server_name(SslOptions#ssl_options.server_name_indication, Host, Role), + [PeerCert | ChainCerts ] = Certs, + try + {TrustedCert, CertPath} = + ssl_certificate:trusted_cert_and_path(Certs, CertDbHandle, CertDbRef, + SslOptions#ssl_options.partial_chain), + ValidationFunAndState = + ssl_handshake:validation_fun_and_state(SslOptions#ssl_options.verify_fun, Role, + CertDbHandle, CertDbRef, ServerName, + SslOptions#ssl_options.customize_hostname_check, + SslOptions#ssl_options.crl_check, CRLDbHandle, CertPath), + Options = [{max_path_length, SslOptions#ssl_options.depth}, + {verify_fun, ValidationFunAndState}], + %% TODO: Validate if Certificate is using a supported signature algorithm + %% (signature_algs_cert)! + case public_key:pkix_path_validation(TrustedCert, CertPath, Options) of + {ok, {PublicKeyInfo,_}} -> + {ok, {PeerCert, PublicKeyInfo}}; + {error, Reason} -> + ssl_handshake:handle_path_validation_error(Reason, PeerCert, ChainCerts, + SslOptions, Options, + CertDbHandle, CertDbRef) + end + catch + error:{badmatch,{asn1, Asn1Reason}} -> + %% ASN-1 decode of certificate somehow failed + {error, {certificate_unknown, {failed_to_decode_certificate, Asn1Reason}}}; + error:OtherReason -> + {error, {internal_error, {unexpected_error, OtherReason}}} + end. + + +store_peer_cert(#state{session = Session, + handshake_env = HsEnv} = State, PeerCert, PublicKeyInfo) -> + State#state{session = Session#session{peer_certificate = PeerCert}, + handshake_env = HsEnv#handshake_env{public_key_info = PublicKeyInfo}}. + + +convert_certificate_chain(Certs) -> + Fun = fun(#certificate_entry{data = Data}) -> + {true, Data}; + (_) -> + false + end, + lists:filtermap(Fun, Certs). + + +%% 4.4.1. The Transcript Hash +%% +%% As an exception to this general rule, when the server responds to a +%% ClientHello with a HelloRetryRequest, the value of ClientHello1 is +%% replaced with a special synthetic handshake message of handshake type +%% "message_hash" containing Hash(ClientHello1). I.e., +%% +%% Transcript-Hash(ClientHello1, HelloRetryRequest, ... Mn) = +%% Hash(message_hash || /* Handshake type */ +%% 00 00 Hash.length || /* Handshake message length (bytes) */ +%% Hash(ClientHello1) || /* Hash of ClientHello1 */ +%% HelloRetryRequest || ... || Mn) +%% +%% NOTE: Hash.length is used in practice (openssl) and not message length! +%% It is most probably a fault in the RFC. +replace_ch1_with_message_hash(#state{connection_states = ConnectionStates, + handshake_env = + #handshake_env{ + tls_handshake_history = + {[HRR,CH1|HHistory], LM}} = HSEnv} = State0) -> + #{security_parameters := SecParamsR} = + ssl_record:pending_connection_state(ConnectionStates, read), + #security_parameters{prf_algorithm = HKDFAlgo} = SecParamsR, + MessageHash = message_hash(CH1, HKDFAlgo), + State0#state{handshake_env = + HSEnv#handshake_env{ + tls_handshake_history = + {[HRR,MessageHash|HHistory], LM}}}. + + +message_hash(ClientHello1, HKDFAlgo) -> + [?MESSAGE_HASH, + 0,0,ssl_cipher:hash_size(HKDFAlgo), + crypto:hash(HKDFAlgo, ClientHello1)]. + + +calculate_handshake_secrets(ClientKey, SelectedGroup, KeyShare, + #state{connection_states = ConnectionStates, + handshake_env = + #handshake_env{ + tls_handshake_history = HHistory}} = State0) -> + #{security_parameters := SecParamsR} = + ssl_record:pending_connection_state(ConnectionStates, read), + #security_parameters{prf_algorithm = HKDFAlgo, + cipher_suite = CipherSuite} = SecParamsR, + + %% Calculate handshake_secret + PSK = binary:copy(<<0>>, ssl_cipher:hash_size(HKDFAlgo)), + EarlySecret = tls_v1:key_schedule(early_secret, HKDFAlgo , {psk, PSK}), + PrivateKey = get_server_private_key(KeyShare), %% #'ECPrivateKey'{} + + IKM = calculate_shared_secret(ClientKey, PrivateKey, SelectedGroup), + HandshakeSecret = tls_v1:key_schedule(handshake_secret, HKDFAlgo, IKM, EarlySecret), + + %% Calculate [sender]_handshake_traffic_secret + {Messages, _} = HHistory, + ClientHSTrafficSecret = + tls_v1:client_handshake_traffic_secret(HKDFAlgo, HandshakeSecret, lists:reverse(Messages)), + ServerHSTrafficSecret = + tls_v1:server_handshake_traffic_secret(HKDFAlgo, HandshakeSecret, lists:reverse(Messages)), + + %% Calculate traffic keys + #{cipher := Cipher} = ssl_cipher_format:suite_bin_to_map(CipherSuite), + {ReadKey, ReadIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, Cipher, ClientHSTrafficSecret), + {WriteKey, WriteIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, Cipher, ServerHSTrafficSecret), + + %% Calculate Finished Keys + ReadFinishedKey = tls_v1:finished_key(ClientHSTrafficSecret, HKDFAlgo), + WriteFinishedKey = tls_v1:finished_key(ServerHSTrafficSecret, HKDFAlgo), + + update_pending_connection_states(State0, HandshakeSecret, + ReadKey, ReadIV, ReadFinishedKey, + WriteKey, WriteIV, WriteFinishedKey). + +calculate_traffic_secrets(#state{connection_states = ConnectionStates, + handshake_env = + #handshake_env{ + tls_handshake_history = HHistory}} = State0) -> + #{security_parameters := SecParamsR} = + ssl_record:pending_connection_state(ConnectionStates, read), + #security_parameters{prf_algorithm = HKDFAlgo, + cipher_suite = CipherSuite, + master_secret = HandshakeSecret} = SecParamsR, + + MasterSecret = + tls_v1:key_schedule(master_secret, HKDFAlgo, HandshakeSecret), + + %% Get the correct list messages for the handshake context. + Messages = get_handshake_context(HHistory), + + %% Calculate [sender]_application_traffic_secret_0 + ClientAppTrafficSecret0 = + tls_v1:client_application_traffic_secret_0(HKDFAlgo, MasterSecret, lists:reverse(Messages)), + ServerAppTrafficSecret0 = + tls_v1:server_application_traffic_secret_0(HKDFAlgo, MasterSecret, lists:reverse(Messages)), + + %% Calculate traffic keys + #{cipher := Cipher} = ssl_cipher_format:suite_bin_to_map(CipherSuite), + {ReadKey, ReadIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, Cipher, ClientAppTrafficSecret0), + {WriteKey, WriteIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, Cipher, ServerAppTrafficSecret0), + + update_pending_connection_states(State0, MasterSecret, + ReadKey, ReadIV, undefined, + WriteKey, WriteIV, undefined). + + +get_server_private_key(#key_share_server_hello{server_share = ServerShare}) -> + get_private_key(ServerShare). + +get_private_key(#key_share_entry{ + key_exchange = #'ECPrivateKey'{} = PrivateKey}) -> + PrivateKey; +get_private_key(#key_share_entry{ + key_exchange = + {_, PrivateKey}}) -> + PrivateKey. + +%% TODO: implement EC keys +get_public_key({?'rsaEncryption', PublicKey, _}) -> + PublicKey. + + +%% X25519, X448 +calculate_shared_secret(OthersKey, MyKey, Group) + when is_binary(OthersKey) andalso is_binary(MyKey) andalso + (Group =:= x25519 orelse Group =:= x448)-> + crypto:compute_key(ecdh, OthersKey, MyKey, Group); +%% FFDHE +calculate_shared_secret(OthersKey, MyKey, Group) + when is_binary(OthersKey) andalso is_binary(MyKey) -> + Params = #'DHParameter'{prime = P} = ssl_dh_groups:dh_params(Group), + S = public_key:compute_key(OthersKey, MyKey, Params), + Size = byte_size(binary:encode_unsigned(P)), + ssl_cipher:add_zero_padding(S, Size); +%% ECDHE +calculate_shared_secret(OthersKey, MyKey = #'ECPrivateKey'{}, _Group) + when is_binary(OthersKey) -> + Point = #'ECPoint'{point = OthersKey}, + public_key:compute_key(Point, MyKey). + + +update_pending_connection_states(#state{connection_states = + CS = #{pending_read := PendingRead0, + pending_write := PendingWrite0}} = State, + HandshakeSecret, + ReadKey, ReadIV, ReadFinishedKey, + WriteKey, WriteIV, WriteFinishedKey) -> + PendingRead = update_connection_state(PendingRead0, HandshakeSecret, + ReadKey, ReadIV, ReadFinishedKey), + PendingWrite = update_connection_state(PendingWrite0, HandshakeSecret, + WriteKey, WriteIV, WriteFinishedKey), + State#state{connection_states = CS#{pending_read => PendingRead, + pending_write => PendingWrite}}. + +update_connection_state(ConnectionState = #{security_parameters := SecurityParameters0}, + HandshakeSecret, Key, IV, FinishedKey) -> + %% Store secret + SecurityParameters = SecurityParameters0#security_parameters{ + master_secret = HandshakeSecret}, + ConnectionState#{security_parameters => SecurityParameters, + cipher_state => cipher_init(Key, IV, FinishedKey)}. + + +update_start_state(#state{connection_states = ConnectionStates0, + connection_env = CEnv, + session = Session} = State, + Cipher, KeyShare, SessionId, + Group, SelectedSignAlg, ClientPubKey) -> + #{security_parameters := SecParamsR0} = PendingRead = + maps:get(pending_read, ConnectionStates0), + #{security_parameters := SecParamsW0} = PendingWrite = + maps:get(pending_write, ConnectionStates0), + SecParamsR = ssl_cipher:security_parameters_1_3(SecParamsR0, Cipher), + SecParamsW = ssl_cipher:security_parameters_1_3(SecParamsW0, Cipher), + ConnectionStates = + ConnectionStates0#{pending_read => PendingRead#{security_parameters => SecParamsR}, + pending_write => PendingWrite#{security_parameters => SecParamsW}}, + State#state{connection_states = ConnectionStates, + key_share = KeyShare, + session = Session#session{session_id = SessionId, + ecc = Group, + sign_alg = SelectedSignAlg, + dh_public_value = ClientPubKey, + cipher_suite = Cipher}, + connection_env = CEnv#connection_env{negotiated_version = {3,4}}}. + + +cipher_init(Key, IV, FinishedKey) -> + #cipher_state{key = Key, + iv = IV, + finished_key = FinishedKey, + tag_len = 16}. + + +%% Get handshake context for verification of CertificateVerify. +%% +%% Verify CertificateVerify: +%% ClientHello (client) (1) +%% ServerHello (server) (2) +%% EncryptedExtensions (server) (8) +%% CertificateRequest (server) (13) +%% Certificate (server) (11) +%% CertificateVerify (server) (15) +%% Finished (server) (20) +%% Certificate (client) (11) +%% CertificateVerify (client) (15) - Drop! Not included in calculations! +get_handshake_context_cv({[<<15,_/binary>>|Messages], _}) -> + Messages. + + +%% Get handshake context for traffic key calculation. +%% +%% Client is authenticated with certificate: +%% ClientHello (client) (1) +%% ServerHello (server) (2) +%% EncryptedExtensions (server) (8) +%% CertificateRequest (server) (13) +%% Certificate (server) (11) +%% CertificateVerify (server) (15) +%% Finished (server) (20) +%% Certificate (client) (11) - Drop! Not included in calculations! +%% CertificateVerify (client) (15) - Drop! Not included in calculations! +%% Finished (client) (20) - Drop! Not included in calculations! +%% +%% Client is authenticated but sends empty certificate: +%% ClientHello (client) (1) +%% ServerHello (server) (2) +%% EncryptedExtensions (server) (8) +%% CertificateRequest (server) (13) +%% Certificate (server) (11) +%% CertificateVerify (server) (15) +%% Finished (server) (20) +%% Certificate (client) (11) - Drop! Not included in calculations! +%% Finished (client) (20) - Drop! Not included in calculations! +%% +%% Client is not authenticated: +%% ClientHello (client) (1) +%% ServerHello (server) (2) +%% EncryptedExtensions (server) (8) +%% Certificate (server) (11) +%% CertificateVerify (server) (15) +%% Finished (server) (20) +%% Finished (client) (20) - Drop! Not included in calculations! +%% +%% Drop all client messages from the front of the iolist using the property that +%% incoming messages are binaries. +get_handshake_context({Messages, _}) -> + get_handshake_context(Messages); +get_handshake_context([H|T]) when is_binary(H) -> + get_handshake_context(T); +get_handshake_context(L) -> + L. + + +%% If sent by a client, the signature algorithm used in the signature +%% MUST be one of those present in the supported_signature_algorithms +%% field of the "signature_algorithms" extension in the +%% CertificateRequest message. +verify_signature_algorithm(#state{ssl_options = + #ssl_options{ + signature_algs = ServerSignAlgs}} = State0, + #certificate_verify_1_3{algorithm = ClientSignAlg}) -> + case lists:member(ClientSignAlg, ServerSignAlgs) of + true -> + ok; + false -> + State1 = calculate_traffic_secrets(State0), + State = ssl_record:step_encryption_state(State1), + {error, {{handshake_failure, + "CertificateVerify uses unsupported signature algorithm"}, State}} + end. + + +verify_certificate_verify(#state{connection_states = ConnectionStates, + handshake_env = + #handshake_env{ + public_key_info = PublicKeyInfo, + tls_handshake_history = HHistory}} = State0, + #certificate_verify_1_3{ + algorithm = SignatureScheme, + signature = Signature}) -> + #{security_parameters := SecParamsR} = + ssl_record:pending_connection_state(ConnectionStates, write), + #security_parameters{prf_algorithm = HKDFAlgo} = SecParamsR, + + {HashAlgo, _, _} = + ssl_cipher:scheme_to_components(SignatureScheme), + + Messages = get_handshake_context_cv(HHistory), + + Context = lists:reverse(Messages), + + %% Transcript-Hash uses the HKDF hash function defined by the cipher suite. + THash = tls_v1:transcript_hash(Context, HKDFAlgo), + + PublicKey = get_public_key(PublicKeyInfo), + + %% Digital signatures use the hash function defined by the selected signature + %% scheme. + case verify(THash, <<"TLS 1.3, client CertificateVerify">>, + HashAlgo, Signature, PublicKey) of + {ok, true} -> + {ok, {State0, wait_finished}}; + {ok, false} -> + State1 = calculate_traffic_secrets(State0), + State = ssl_record:step_encryption_state(State1), + {error, {{handshake_failure, "Failed to verify CertificateVerify"}, State}}; + {error, badarg} -> + State1 = calculate_traffic_secrets(State0), + State = ssl_record:step_encryption_state(State1), + {error, {badarg, State}} + end. + + +%% If there is no overlap between the received +%% "supported_groups" and the groups supported by the server, then the +%% server MUST abort the handshake with a "handshake_failure" or an +%% "insufficient_security" alert. +select_common_groups(_, []) -> + {error, {insufficient_security, no_suitable_groups}}; +select_common_groups(ServerGroups, ClientGroups) -> + Fun = fun(E) -> lists:member(E, ClientGroups) end, + case lists:filter(Fun, ServerGroups) of + [] -> + {error, {insufficient_security, no_suitable_groups}}; + L -> + {ok, L} + end. + + +%% RFC 8446 - 4.2.8. Key Share +%% This vector MAY be empty if the client is requesting a +%% HelloRetryRequest. Each KeyShareEntry value MUST correspond to a +%% group offered in the "supported_groups" extension and MUST appear in +%% the same order. However, the values MAY be a non-contiguous subset +%% of the "supported_groups" extension and MAY omit the most preferred +%% groups. +%% +%% Clients can offer as many KeyShareEntry values as the number of +%% supported groups it is offering, each representing a single set of +%% key exchange parameters. +%% +%% Clients MUST NOT offer multiple KeyShareEntry values +%% for the same group. Clients MUST NOT offer any KeyShareEntry values +%% for groups not listed in the client's "supported_groups" extension. +%% Servers MAY check for violations of these rules and abort the +%% handshake with an "illegal_parameter" alert if one is violated. +validate_key_share(_ ,[]) -> + ok; +validate_key_share([], _) -> + {error, illegal_parameter}; +validate_key_share([G|ClientGroups], [{_, G, _}|ClientShares]) -> + validate_key_share(ClientGroups, ClientShares); +validate_key_share([_|ClientGroups], [_|_] = ClientShares) -> + validate_key_share(ClientGroups, ClientShares). + + +get_client_public_key([Group|_] = Groups, ClientShares) -> + get_client_public_key(Groups, ClientShares, Group). +%% +get_client_public_key(_, [], PreferredGroup) -> + {PreferredGroup, no_suitable_key}; +get_client_public_key([], _, PreferredGroup) -> + {PreferredGroup, no_suitable_key}; +get_client_public_key([Group|Groups], ClientShares, PreferredGroup) -> + case lists:keysearch(Group, 2, ClientShares) of + {value, {_, _, ClientPublicKey}} -> + {Group, ClientPublicKey}; + false -> + get_client_public_key(Groups, ClientShares, PreferredGroup) + end. + + +%% get_client_public_key(Group, ClientShares) -> +%% case lists:keysearch(Group, 2, ClientShares) of +%% {value, {_, _, ClientPublicKey}} -> +%% ClientPublicKey; +%% false -> +%% %% 4.1.4. Hello Retry Request +%% %% +%% %% The server will send this message in response to a ClientHello +%% %% message if it is able to find an acceptable set of parameters but the +%% %% ClientHello does not contain sufficient information to proceed with +%% %% the handshake. +%% no_suitable_key +%% end. + +select_cipher_suite([], _) -> + {error, no_suitable_cipher}; +select_cipher_suite([Cipher|ClientCiphers], ServerCiphers) -> + case lists:member(Cipher, tls_v1:suites('TLS_v1.3')) andalso + lists:member(Cipher, ServerCiphers) of + true -> + {ok, Cipher}; + false -> + select_cipher_suite(ClientCiphers, ServerCiphers) + end. + +%% 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. + +%% Check if the signature algorithm of the server certificate is supported +%% by the client. +check_cert_sign_algo(SignAlgo, SignHash, ClientSignAlgs, undefined) -> + do_check_cert_sign_algo(SignAlgo, SignHash, ClientSignAlgs); +check_cert_sign_algo(SignAlgo, SignHash, _, ClientSignAlgsCert) -> + do_check_cert_sign_algo(SignAlgo, SignHash, ClientSignAlgsCert). + + +%% DSA keys are not supported by TLS 1.3 +select_sign_algo(dsa, _ClientSignAlgs, _ServerSignAlgs) -> + {error, {insufficient_security, no_suitable_public_key}}; +%% TODO: Implement support for ECDSA keys! +select_sign_algo(_, [], _) -> + {error, {insufficient_security, no_suitable_signature_algorithm}}; +select_sign_algo(PublicKeyAlgo, [C|ClientSignAlgs], ServerSignAlgs) -> + {_, S, _} = ssl_cipher:scheme_to_components(C), + %% RSASSA-PKCS1-v1_5 and Legacy algorithms are not defined for use in signed + %% TLS handshake messages: filter sha-1 and rsa_pkcs1. + case ((PublicKeyAlgo =:= rsa andalso S =:= rsa_pss_rsae) + orelse (PublicKeyAlgo =:= rsa_pss andalso S =:= rsa_pss_rsae)) + andalso + lists:member(C, ServerSignAlgs) of + true -> + {ok, C}; + false -> + select_sign_algo(PublicKeyAlgo, ClientSignAlgs, ServerSignAlgs) + end. + + +do_check_cert_sign_algo(_, _, []) -> + {error, {insufficient_security, no_suitable_signature_algorithm}}; +do_check_cert_sign_algo(SignAlgo, SignHash, [Scheme|T]) -> + {Hash, Sign, _Curve} = ssl_cipher:scheme_to_components(Scheme), + case compare_sign_algos(SignAlgo, SignHash, Sign, Hash) of + true -> + ok; + _Else -> + do_check_cert_sign_algo(SignAlgo, SignHash, T) + end. + + +%% id-RSASSA-PSS (rsa_pss) indicates that the key may only be used for PSS signatures. +%% TODO: Uncomment when rsa_pss signatures are supported in certificates +%% compare_sign_algos(rsa_pss, Hash, Algo, Hash) +%% when Algo =:= rsa_pss_pss -> +%% true; +%% rsaEncryption (rsa) allows the key to be used for any of the standard encryption or +%% signature schemes. +compare_sign_algos(rsa, Hash, Algo, Hash) + when Algo =:= rsa_pss_rsae orelse + Algo =:= rsa_pkcs1 -> + true; +compare_sign_algos(Algo, Hash, Algo, Hash) -> + true; +compare_sign_algos(_, _, _, _) -> + false. + + +get_certificate_params(Cert) -> + {SignAlgo0, _Param, PublicKeyAlgo0} = ssl_handshake:get_cert_params(Cert), + {SignHash0, SignAlgo} = public_key:pkix_sign_types(SignAlgo0), + %% Convert hash to new format + SignHash = case SignHash0 of + sha -> + sha1; + H -> H + end, + PublicKeyAlgo = public_key_algo(PublicKeyAlgo0), + {PublicKeyAlgo, SignAlgo, SignHash}. + + +%% Note: copied from ssl_handshake +public_key_algo(?'id-RSASSA-PSS') -> + rsa_pss; +public_key_algo(?rsaEncryption) -> + rsa; +public_key_algo(?'id-ecPublicKey') -> + ecdsa; +public_key_algo(?'id-dsa') -> + dsa. + +get_signature_scheme_list(undefined) -> + undefined; +get_signature_scheme_list(#signature_algorithms_cert{ + signature_scheme_list = ClientSignatureSchemes}) -> + ClientSignatureSchemes; +get_signature_scheme_list(#signature_algorithms{ + signature_scheme_list = ClientSignatureSchemes}) -> + %% Filter unassigned and legacy elements + lists:filter(fun (E) -> is_atom(E) andalso E =/= unassigned end, + ClientSignatureSchemes). + +get_supported_groups(#supported_groups{supported_groups = Groups}) -> + Groups. + +get_key_shares(#key_share_client_hello{client_shares = ClientShares}) -> + ClientShares. + +maybe() -> + Ref = erlang:make_ref(), + Ok = fun(ok) -> ok; + ({ok,R}) -> R; + ({error,Reason}) -> + throw({Ref,Reason}) + end, + {Ref,Ok}. diff --git a/lib/ssl/src/tls_handshake_1_3.hrl b/lib/ssl/src/tls_handshake_1_3.hrl new file mode 100644 index 0000000000..7ae1b93e1c --- /dev/null +++ b/lib/ssl/src/tls_handshake_1_3.hrl @@ -0,0 +1,238 @@ +%% +%% %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-handshake protocol +%% see RFC 8446. Also includes supported hello extensions. +%%---------------------------------------------------------------------- + +-ifndef(tls_handshake_1_3). +-define(tls_handshake_1_3, true). + +%% Common to TLS-1.3 and previous TLS versions +%% Some defenitions may not exist in TLS-1.3 this is +%% handled elsewhere +-include("tls_handshake.hrl"). + +%% New handshake types in TLS-1.3 RFC 8446 B.3 +-define(NEW_SESSION_TICKET, 4). +-define(END_OF_EARLY_DATA, 5). +-define(ENCRYPTED_EXTENSIONS, 8). +-define(KEY_UPDATE, 24). +%% %% Not really a message but special way to handle handshake hashes +%% %% when a "hello-retry-request" (special server_hello) is sent +-define(MESSAGE_HASH, 254). + +%% %% RFC 8446 B.3.1. +%% %% New extension types in TLS-1.3 +-define(PRE_SHARED_KEY_EXT, 41). +-define(EARLY_DATA_EXT, 42). +%%-define(SUPPORTED_VERSIONS_EXT, 43). %% Updates TLS 1.2 so defined in ssl_handshake.hrl +-define(COOKIE_EXT, 44). +-define(PSK_KEY_EXCHANGE_MODES_EXT, 45). +-define(CERTIFICATE_AUTHORITIES_EXT, 47). +-define(OID_FILTERS_EXT, 48). +-define(POST_HANDSHAKE_AUTH_EXT, 49). +%% -define(SIGNATURE_ALGORITHMS_CERT_EXT, 50). %% Updates TLS 1.2 so defined in ssl_handshake.hrl +-define(KEY_SHARE_EXT, 51). + +%% RFC 8446 B.3.1 +-record(key_share_entry, { + group, %NamedGroup + key_exchange %key_exchange<1..2^16-1>; + }). +-record(key_share_client_hello, { + client_shares %% KeyShareEntry client_shares<0..2^16-1>; + }). +-record(key_share_hello_retry_request, { + selected_group %% NamedGroup + }). +-record(key_share_server_hello, { + server_share %% KeyShareEntry server_share; + }). + +-record(uncompressed_point_representation, { + legacy_form = 4, % uint8 legacy_form = 4; + x, % opaque X[coordinate_length]; + y % opaque Y[coordinate_length]; + }). + +-define(PSK_KE, 0). +-define(PSK_DHE_KE, 1). + +-record(psk_keyexchange_modes, { + ke_modes % ke_modes<1..255> + }). +-record(empty, { + }). +-record(early_data_indication, { + indication % uint32 max_early_data_size (new_session_ticket) | + %% #empty{} (client_hello, encrypted_extensions) + }). +-record(psk_identity, { + identity, % opaque identity<1..2^16-1> + obfuscated_ticket_age % uint32 + }). +-record(offered_psks, { + psk_identity, %identities<7..2^16-1>; + psk_binder_entry %binders<33..2^16-1>, opaque PskBinderEntry<32..255> + }). +-record(pre_shared_keyextension,{ + extension %OfferedPsks (client_hello) | uint16 selected_identity (server_hello) + }). + +%% RFC 8446 B.3.1.2. +-record(cookie, { + cookie %cookie<1..2^16-1>; + }). + +%%% RFC 8446 B.3.1.3. Signature Algorithm Extension +%% Signature Schemes +%% RSASSA-PKCS1-v1_5 algorithms +-define(RSA_PKCS1_SHA256, 16#0401). +-define(RSA_PKCS1_SHA384, 16#0501). +-define(RSA_PKCS1_SHA512, 16#0601). + +%% ECDSA algorithms +-define(ECDSA_SECP256R1_SHA256, 16#0403). +-define(ECDSA_SECP384R1_SHA384, 16#0503). +-define(ECDSA_SECP521R1_SHA512, 16#0603). + +%% RSASSA-PSS algorithms with public key OID rsaEncryption +-define(RSA_PSS_RSAE_SHA256, 16#0804). +-define(RSA_PSS_RSAE_SHA384, 16#0805). +-define(RSA_PSS_RSAE_SHA512, 16#0806). + +%% EdDSA algorithms +-define(ED25519, 16#0807). +-define(ED448, 16#0808). + +%% RSASSA-PSS algorithms with public key OID RSASSA-PSS +-define(RSA_PSS_PSS_SHA256, 16#0809). +-define(RSA_PSS_PSS_SHA384, 16#080a). +-define(RSA_PSS_PSS_SHA512, 16#080b). + +%% Legacy algorithms +-define(RSA_PKCS1_SHA1, 16#201). +-define(ECDSA_SHA1, 16#0203). + +%% RFC 8446 B.3.1.4. Supported Groups Extension +%% Elliptic Curve Groups (ECDHE) +-define(SECP256R1, 16#0017). +-define(SECP384R1, 16#0018). +-define(SECP521R1, 16#0019). +-define(X25519, 16#001D). +-define(X448, 16#001E). + +%% RFC 8446 Finite Field Groups (DHE) +-define(FFDHE2048, 16#0100). +-define(FFDHE3072, 16#0101). +-define(FFDHE4096, 16#0102). +-define(FFDHE6144, 16#0103). +-define(FFDHE8192 ,16#0104). + +-record(named_group_list, { + named_group_list %named_group_list<2..2^16-1>; + }). + +%% RFC 8446 B.3.2 Server Parameters Messages +%% opaque DistinguishedName<1..2^16-1>;XS +-record(certificate_authoritie_sextension, { + authorities %DistinguishedName authorities<3..2^16-1>; + }). + +-record(oid_filter, { + certificate_extension_oid, % opaque certificate_extension_oid<1..2^8-1>; + certificate_extension_values % opaque certificate_extension_values<0..2^16-1>; + }). + +-record(oid_filter_extension, { + filters %OIDFilter filters<0..2^16-1>; + }). +-record(post_handshake_auth, { + }). + +-record(encrypted_extensions, { + extensions %extensions<0..2^16-1>; + }). + +-record(certificate_request_1_3, { + certificate_request_context, % opaque certificate_request_context<0..2^8-1>; + extensions %Extension extensions<2..2^16-1>; + }). + +%% RFC 8446 B.3.3 Authentication Messages + +%% Certificate Type +-define(X509, 0). +-define(OpenPGP_RESERVED, 1). +-define(RawPublicKey, 2). + +-record(certificate_entry, { + data, + %% select (certificate_type) { + %% case RawPublicKey: + %% /* From RFC 7250 ASN.1_subjectPublicKeyInfo */ + %% opaque ASN1_subjectPublicKeyInfo<1..2^24-1>; + %% + %% case X509: + %% opaque cert_data<1..2^24-1>; + %% }; + extensions %% Extension extensions<0..2^16-1>; + }). + +-record(certificate_1_3, { + certificate_request_context, % opaque certificate_request_context<0..2^8-1>; + certificate_list % CertificateEntry certificate_list<0..2^24-1>; + }). + +-record(certificate_verify_1_3, { + algorithm, % SignatureScheme + signature % signature<0..2^16-1> + }). + +%% RFC 8446 B.3.4. Ticket Establishment +-record(new_session_ticket, { + ticket_lifetime, %unit32 + ticket_age_add, %unit32 + ticket_nonce, %opaque ticket_nonce<0..255>; + ticket, %opaque ticket<1..2^16-1> + extensions %extensions<0..2^16-2> + }). + +%% RFC 8446 B.3.5. Updating Keys +-record(end_of_early_data, { + }). + +-define(UPDATE_NOT_REQUESTED, 0). +-define(UPDATE_REQUESTED, 1). + +-record(key_update, { + request_update + }). + +-type tls_handshake_1_3() :: #encrypted_extensions{} | + #certificate_request_1_3{} | + #certificate_1_3{} | + #certificate_verify_1_3{}. + +-export_type([tls_handshake_1_3/0]). + +-endif. % -ifdef(tls_handshake_1_3). diff --git a/lib/ssl/src/tls_record.erl b/lib/ssl/src/tls_record.erl index 38022030ee..a5c550a429 100644 --- a/lib/ssl/src/tls_record.erl +++ b/lib/ssl/src/tls_record.erl @@ -30,9 +30,10 @@ -include("ssl_alert.hrl"). -include("tls_handshake.hrl"). -include("ssl_cipher.hrl"). +-include_lib("kernel/include/logger.hrl"). %% Handling of incoming data --export([get_tls_records/3, init_connection_states/2]). +-export([get_tls_records/4, init_connection_states/2]). %% Encoding TLS records -export([encode_handshake/3, encode_alert_record/3, @@ -40,13 +41,16 @@ -export([encode_plain_text/4]). %% Decoding --export([decode_cipher_text/3]). +-export([decode_cipher_text/4]). + +%% Logging helper +-export([build_tls_record/1]). %% Protocol version handling -export([protocol_version/1, lowest_protocol_version/1, lowest_protocol_version/2, highest_protocol_version/1, highest_protocol_version/2, is_higher/2, supported_protocol_versions/0, - is_acceptable_version/1, is_acceptable_version/2, hello_version/2]). + is_acceptable_version/1, is_acceptable_version/2, hello_version/1]). -export_type([tls_version/0, tls_atom_version/0]). @@ -76,8 +80,10 @@ init_connection_states(Role, BeastMitigation) -> %%-------------------------------------------------------------------- -spec get_tls_records( - binary(), [tls_version()] | tls_version(), - Buffer0 :: binary() | {'undefined' | #ssl_tls{}, {[binary()],non_neg_integer(),[binary()]}}) -> + binary(), + [tls_version()] | tls_version(), + Buffer0 :: binary() | {'undefined' | #ssl_tls{}, {[binary()],non_neg_integer(),[binary()]}}, + #ssl_options{}) -> {Records :: [#ssl_tls{}], Buffer :: {'undefined' | #ssl_tls{}, {[binary()],non_neg_integer(),[binary()]}}} | #alert{}. @@ -86,11 +92,10 @@ init_connection_states(Role, BeastMitigation) -> %% Description: Given old buffer and new data from TCP, packs up a records %% data %%-------------------------------------------------------------------- - -get_tls_records(Data, Versions, Buffer) when is_binary(Buffer) -> - parse_tls_records(Versions, {[Data],byte_size(Data),[]}, undefined); -get_tls_records(Data, Versions, {Hdr, {Front,Size,Rear}}) -> - parse_tls_records(Versions, {Front,Size + byte_size(Data),[Data|Rear]}, Hdr). +get_tls_records(Data, Versions, Buffer, SslOpts) when is_binary(Buffer) -> + parse_tls_records(Versions, {[Data],byte_size(Data),[]}, SslOpts, undefined); +get_tls_records(Data, Versions, {Hdr, {Front,Size,Rear}}, SslOpts) -> + parse_tls_records(Versions, {Front,Size + byte_size(Data),[Data|Rear]}, SslOpts, Hdr). %%==================================================================== %% Encoding @@ -102,6 +107,8 @@ get_tls_records(Data, Versions, {Hdr, {Front,Size,Rear}}) -> % %% 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, @@ -122,6 +129,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_alert_record(Alert, ConnectionStates); encode_alert_record(#alert{level = Level, description = Description}, Version, ConnectionStates) -> encode_plain_text(?ALERT, Version, <<?BYTE(Level), ?BYTE(Description)>>, @@ -142,6 +151,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(Data, Version, #{current_write := #{beast_mitigation := BeastMitigation, security_parameters := @@ -155,12 +166,14 @@ encode_data(Data, 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(CipherText, +decode_cipher_text({3,4}, CipherTextRecord, ConnectionStates, _) -> + tls_record_1_3:decode_cipher_text(CipherTextRecord, ConnectionStates); +decode_cipher_text(_, CipherTextRecord, #{current_read := #{sequence_number := Seq, security_parameters := @@ -170,7 +183,7 @@ decode_cipher_text(CipherText, } } = ConnectionStates0, _) -> SeqBin = <<?UINT64(Seq)>>, - #ssl_tls{type = Type, version = {MajVer,MinVer} = Version, fragment = Fragment} = CipherText, + #ssl_tls{type = Type, version = {MajVer,MinVer} = Version, fragment = Fragment} = CipherTextRecord, StartAdditionalData = <<SeqBin/binary, ?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer)>>, CipherS = ssl_record:nonce_seed(BulkCipherAlgo, SeqBin, CipherS0), case ssl_record:decipher_aead( @@ -187,17 +200,17 @@ decode_cipher_text(CipherText, cipher_state => CipherS, sequence_number => Seq + 1, compression_state => CompressionS}}, - {CipherText#ssl_tls{fragment = Plain}, ConnectionStates}; + {CipherTextRecord#ssl_tls{fragment = Plain}, ConnectionStates}; #alert{} = Alert -> Alert end; -decode_cipher_text(#ssl_tls{version = Version, - fragment = CipherFragment} = CipherText, +decode_cipher_text(_, #ssl_tls{version = Version, + fragment = CipherFragment} = CipherTextRecord, #{current_read := ReadState0} = ConnnectionStates0, PaddingCheck) -> case ssl_record:decipher(Version, CipherFragment, ReadState0, PaddingCheck) of {PlainFragment, Mac, ReadState1} -> - MacHash = ssl_cipher:calc_mac_hash(CipherText#ssl_tls.type, Version, PlainFragment, ReadState1), + MacHash = ssl_cipher:calc_mac_hash(CipherTextRecord#ssl_tls.type, Version, PlainFragment, ReadState1), case ssl_record:is_correct_mac(Mac, MacHash) of true -> #{sequence_number := Seq, @@ -210,7 +223,7 @@ decode_cipher_text(#ssl_tls{version = Version, ConnnectionStates0#{current_read => ReadState1#{sequence_number => Seq + 1, compression_state => CompressionS1}}, - {CipherText#ssl_tls{fragment = Plain}, ConnnectionStates}; + {CipherTextRecord#ssl_tls{fragment = Plain}, ConnnectionStates}; false -> ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC) end; @@ -229,6 +242,8 @@ decode_cipher_text(#ssl_tls{version = Version, %% Description: Creates a protocol version record from a version atom %% or vice versa. %%-------------------------------------------------------------------- +protocol_version('tlsv1.3') -> + {3, 4}; protocol_version('tlsv1.2') -> {3, 3}; protocol_version('tlsv1.1') -> @@ -239,6 +254,8 @@ protocol_version(sslv3) -> {3, 0}; protocol_version(sslv2) -> %% Backwards compatibility {2, 0}; +protocol_version({3, 4}) -> + 'tlsv1.3'; protocol_version({3, 3}) -> 'tlsv1.2'; protocol_version({3, 2}) -> @@ -372,10 +389,10 @@ is_acceptable_version({N,_} = Version, Versions) is_acceptable_version(_,_) -> false. --spec hello_version(tls_version(), [tls_version()]) -> tls_version(). -hello_version(Version, _) when Version >= {3, 3} -> - Version; -hello_version(_, Versions) -> +-spec hello_version([tls_version()]) -> tls_version(). +hello_version([Highest|_]) when Highest >= {3,3} -> + Highest; +hello_version(Versions) -> lowest_protocol_version(Versions). %%-------------------------------------------------------------------- @@ -394,84 +411,91 @@ initial_connection_state(ConnectionEnd, BeastMitigation) -> server_verify_data => undefined }. +%% Used by logging to recreate the received bytes +build_tls_record(#ssl_tls{type = Type, version = {MajVer, MinVer}, fragment = Fragment}) -> + Length = byte_size(Fragment), + <<?BYTE(Type),?BYTE(MajVer),?BYTE(MinVer),?UINT16(Length), Fragment/binary>>. -parse_tls_records(Versions, Q, undefined) -> - decode_tls_records(Versions, Q, [], undefined, undefined, undefined); -parse_tls_records(Versions, Q, #ssl_tls{type = Type, version = Version, fragment = Length}) -> - decode_tls_records(Versions, Q, [], Type, Version, Length). + +parse_tls_records(Versions, Q, SslOpts, undefined) -> + decode_tls_records(Versions, Q, SslOpts, [], undefined, undefined, undefined); +parse_tls_records(Versions, Q, SslOpts, #ssl_tls{type = Type, version = Version, fragment = Length}) -> + decode_tls_records(Versions, Q, SslOpts, [], Type, Version, Length). %% Generic code path -decode_tls_records(Versions, {_,Size,_} = Q0, Acc, undefined, _Version, _Length) -> +decode_tls_records(Versions, {_,Size,_} = Q0, SslOpts, Acc, undefined, _Version, _Length) -> if 5 =< Size -> {<<?BYTE(Type),?BYTE(MajVer),?BYTE(MinVer), ?UINT16(Length)>>, Q} = binary_from_front(5, Q0), - validate_tls_records_type(Versions, Q, Acc, Type, {MajVer,MinVer}, Length); + validate_tls_records_type(Versions, Q, SslOpts, Acc, Type, {MajVer,MinVer}, Length); 3 =< Size -> {<<?BYTE(Type),?BYTE(MajVer),?BYTE(MinVer)>>, Q} = binary_from_front(3, Q0), - validate_tls_records_type(Versions, Q, Acc, Type, {MajVer,MinVer}, undefined); + validate_tls_records_type(Versions, Q, SslOpts, Acc, Type, {MajVer,MinVer}, undefined); 1 =< Size -> {<<?BYTE(Type)>>, Q} = binary_from_front(1, Q0), - validate_tls_records_type(Versions, Q, Acc, Type, undefined, undefined); + validate_tls_records_type(Versions, Q, SslOpts, Acc, Type, undefined, undefined); true -> - validate_tls_records_type(Versions, Q0, Acc, undefined, undefined, undefined) + validate_tls_records_type(Versions, Q0, SslOpts, Acc, undefined, undefined, undefined) end; -decode_tls_records(Versions, {_,Size,_} = Q0, Acc, Type, undefined, _Length) -> +decode_tls_records(Versions, {_,Size,_} = Q0, SslOpts, Acc, Type, undefined, _Length) -> if 4 =< Size -> {<<?BYTE(MajVer),?BYTE(MinVer), ?UINT16(Length)>>, Q} = binary_from_front(4, Q0), - validate_tls_record_version(Versions, Q, Acc, Type, {MajVer,MinVer}, Length); + validate_tls_record_version(Versions, Q, SslOpts, Acc, Type, {MajVer,MinVer}, Length); 2 =< Size -> {<<?BYTE(MajVer),?BYTE(MinVer)>>, Q} = binary_from_front(2, Q0), - validate_tls_record_version(Versions, Q, Acc, Type, {MajVer,MinVer}, undefined); + validate_tls_record_version(Versions, Q, SslOpts, Acc, Type, {MajVer,MinVer}, undefined); true -> - validate_tls_record_version(Versions, Q0, Acc, Type, undefined, undefined) + validate_tls_record_version(Versions, Q0, SslOpts, Acc, Type, undefined, undefined) end; -decode_tls_records(Versions, {_,Size,_} = Q0, Acc, Type, Version, undefined) -> +decode_tls_records(Versions, {_,Size,_} = Q0, SslOpts, Acc, Type, Version, undefined) -> if 2 =< Size -> {<<?UINT16(Length)>>, Q} = binary_from_front(2, Q0), - validate_tls_record_length(Versions, Q, Acc, Type, Version, Length); + validate_tls_record_length(Versions, Q, SslOpts, Acc, Type, Version, Length); true -> - validate_tls_record_length(Versions, Q0, Acc, Type, Version, undefined) + validate_tls_record_length(Versions, Q0, SslOpts, Acc, Type, Version, undefined) end; -decode_tls_records(Versions, Q, Acc, Type, Version, Length) -> - validate_tls_record_length(Versions, Q, Acc, Type, Version, Length). +decode_tls_records(Versions, Q, SslOpts, Acc, Type, Version, Length) -> + validate_tls_record_length(Versions, Q, SslOpts, Acc, Type, Version, Length). -validate_tls_records_type(_Versions, Q, Acc, undefined, _Version, _Length) -> +validate_tls_records_type(_Versions, Q, _SslOpts, Acc, undefined, _Version, _Length) -> {lists:reverse(Acc), {undefined, Q}}; -validate_tls_records_type(Versions, Q, Acc, Type, Version, Length) -> +validate_tls_records_type(Versions, Q, SslOpts, Acc, Type, Version, Length) -> if ?KNOWN_RECORD_TYPE(Type) -> - validate_tls_record_version(Versions, Q, Acc, Type, Version, Length); + validate_tls_record_version(Versions, Q, SslOpts, Acc, Type, Version, Length); true -> %% Not ?KNOWN_RECORD_TYPE(Type) ?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE) end. -validate_tls_record_version(_Versions, Q, Acc, Type, undefined, _Length) -> +validate_tls_record_version(_Versions, Q, _SslOpts, Acc, Type, undefined, _Length) -> {lists:reverse(Acc), {#ssl_tls{type = Type, version = undefined, fragment = undefined}, Q}}; -validate_tls_record_version(Versions, Q, Acc, Type, Version, Length) -> - if - is_list(Versions) -> +validate_tls_record_version(Versions, Q, SslOpts, Acc, Type, Version, Length) -> + case Versions of + _ when is_list(Versions) -> case is_acceptable_version(Version, Versions) of true -> - validate_tls_record_length(Versions, Q, Acc, Type, Version, Length); + validate_tls_record_length(Versions, Q, SslOpts, Acc, Type, Version, Length); false -> ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC) end; - Version =:= Versions -> + {3, 4} when Version =:= {3, 3} -> + validate_tls_record_length(Versions, Q, SslOpts, Acc, Type, Version, Length); + Version -> %% Exact version match - validate_tls_record_length(Versions, Q, Acc, Type, Version, Length); - true -> + validate_tls_record_length(Versions, Q, SslOpts, Acc, Type, Version, Length); + _ -> ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC) end. -validate_tls_record_length(_Versions, Q, Acc, Type, Version, undefined) -> +validate_tls_record_length(_Versions, Q, _SslOpts, Acc, Type, Version, undefined) -> {lists:reverse(Acc), {#ssl_tls{type = Type, version = Version, fragment = undefined}, Q}}; -validate_tls_record_length(Versions, {_,Size0,_} = Q0, Acc, Type, Version, Length) -> +validate_tls_record_length(Versions, {_,Size0,_} = Q0, SslOpts, Acc, Type, Version, Length) -> if Length =< ?MAX_CIPHER_TEXT_LENGTH -> if @@ -479,7 +503,8 @@ validate_tls_record_length(Versions, {_,Size0,_} = Q0, Acc, Type, Version, Lengt %% Complete record {Fragment, Q} = binary_from_front(Length, Q0), Record = #ssl_tls{type = Type, version = Version, fragment = Fragment}, - decode_tls_records(Versions, Q, [Record|Acc], undefined, undefined, undefined); + ssl_logger:debug(SslOpts#ssl_options.log_level, inbound, 'record', Record), + decode_tls_records(Versions, Q, SslOpts, [Record|Acc], undefined, undefined, undefined); true -> {lists:reverse(Acc), {#ssl_tls{type = Type, version = Version, fragment = Length}, Q0}} 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..74321a1ae2 --- /dev/null +++ b/lib/ssl/src/tls_record_1_3.erl @@ -0,0 +1,311 @@ +%% +%% %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 = encode_plain_text(Data, Write0), + {CipherText, Write} = encode_tls_cipher_text(CipherFragment, Write0), + {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 := #cipher_state{key = Key, + iv = IV, + tag_len = TagLen}, + security_parameters := + #security_parameters{ + cipher_type = ?AEAD, + bulk_cipher_algorithm = + BulkCipherAlgo} + } = ReadState0} = ConnectionStates0) -> + case decipher_aead(CipherFragment, BulkCipherAlgo, Key, Seq, IV, TagLen) of + #alert{} = Alert -> + Alert; + PlainFragment -> + ConnectionStates = + ConnectionStates0#{current_read => + ReadState0#{sequence_number => Seq + 1}}, + {decode_inner_plaintext(PlainFragment), ConnectionStates} + end; + + +%% RFC8446 - TLS 1.3 (OpenSSL compatibility) +%% Handle unencrypted Alerts from openssl s_client when server's +%% connection states are already stepped into traffic encryption. +%% (E.g. openssl s_client receives a CertificateRequest with +%% a signature_algorithms_cert extension that does not contain +%% the signature algorithm of the client's certificate.) +decode_cipher_text(#ssl_tls{type = ?ALERT, + version = ?LEGACY_VERSION, + fragment = <<2,47>>}, + ConnectionStates0) -> + {#ssl_tls{type = ?ALERT, + version = {3,4}, %% Internally use real version + fragment = <<2,47>>}, ConnectionStates0}; +%% RFC8446 - TLS 1.3 +%% D.4. Middlebox Compatibility Mode +%% - If not offering early data, the client sends a dummy +%% change_cipher_spec record (see the third paragraph of Section 5) +%% immediately before its second flight. This may either be before +%% its second ClientHello or before its encrypted handshake flight. +%% If offering early data, the record is placed immediately after the +%% first ClientHello. +decode_cipher_text(#ssl_tls{type = ?CHANGE_CIPHER_SPEC, + version = ?LEGACY_VERSION, + fragment = <<1>>}, + ConnectionStates0) -> + {#ssl_tls{type = ?CHANGE_CIPHER_SPEC, + version = {3,4}, %% Internally use real version + fragment = <<1>>}, ConnectionStates0}; +decode_cipher_text(#ssl_tls{type = Type, + version = ?LEGACY_VERSION, + fragment = CipherFragment}, + #{current_read := + #{security_parameters := + #security_parameters{ + cipher_suite = ?TLS_NULL_WITH_NULL_NULL} + }} = ConnnectionStates0) -> + {#ssl_tls{type = Type, + version = {3,4}, %% Internally use real version + fragment = CipherFragment}, ConnnectionStates0}; +decode_cipher_text(#ssl_tls{type = Type}, _) -> + %% Version mismatch is already asserted + ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC, {record_type_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 := #cipher_state{key= Key, + iv = IV, + tag_len = TagLen}, + sequence_number := Seq, + security_parameters := + #security_parameters{ + cipher_type = ?AEAD, + bulk_cipher_algorithm = BulkCipherAlgo} + }) -> + PlainText = [Data, Type, Zeros], + Encoded = cipher_aead(PlainText, BulkCipherAlgo, Key, Seq, IV, TagLen), + #tls_cipher_text{opaque_type = 23, %% 23 (application_data) for outward compatibility + legacy_version = {3,3}, + encoded_record = Encoded}; +encode_plain_text(#inner_plaintext{ + content = Data, + type = Type + }, #{security_parameters := + #security_parameters{ + cipher_suite = ?TLS_NULL_WITH_NULL_NULL} + }) -> + %% RFC8446 - 5.1. Record Layer + %% When record protection has not yet been engaged, TLSPlaintext + %% structures are written directly onto the wire. + #tls_cipher_text{opaque_type = Type, + legacy_version = {3,3}, + encoded_record = Data}; + +encode_plain_text(_, CS) -> + exit({cs, CS}). + +additional_data(Length) -> + <<?BYTE(?OPAQUE_TYPE), ?BYTE(3), ?BYTE(3),?UINT16(Length)>>. + +%% The per-record nonce for the AEAD construction is formed as +%% follows: +%% +%% 1. The 64-bit record sequence number is encoded in network byte +%% order and padded to the left with zeros to iv_length. +%% +%% 2. The padded sequence number is XORed with either the static +%% client_write_iv or server_write_iv (depending on the role). +%% +%% The resulting quantity (of length iv_length) is used as the +%% per-record nonce. +nonce(Seq, IV) -> + Padding = binary:copy(<<0>>, byte_size(IV) - 8), + crypto:exor(<<Padding/binary,?UINT64(Seq)>>, IV). + +cipher_aead(Fragment, BulkCipherAlgo, Key, Seq, IV, TagLen) -> + AAD = additional_data(erlang:iolist_size(Fragment) + TagLen), + Nonce = nonce(Seq, IV), + {Content, CipherTag} = + ssl_cipher:aead_encrypt(BulkCipherAlgo, Key, Nonce, Fragment, AAD, TagLen), + <<Content/binary, CipherTag/binary>>. + +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(CipherFragment, BulkCipherAlgo, Key, Seq, IV, TagLen) -> + try + AAD = additional_data(erlang:iolist_size(CipherFragment)), + Nonce = nonce(Seq, IV), + {CipherText, CipherTag} = aead_ciphertext_split(CipherFragment, TagLen), + case ssl_cipher:aead_decrypt(BulkCipherAlgo, Key, Nonce, CipherText, CipherTag, AAD) of + Content when is_binary(Content) -> + Content; + _ -> + ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC, decryption_failed) + end + catch + _:_ -> + ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC, decryption_failed) + end. + + +aead_ciphertext_split(CipherTextFragment, TagLen) + when is_binary(CipherTextFragment) -> + CipherLen = erlang:byte_size(CipherTextFragment) - TagLen, + <<CipherText:CipherLen/bytes, CipherTag:TagLen/bytes>> = CipherTextFragment, + {CipherText, CipherTag}; +aead_ciphertext_split(CipherTextFragment, TagLen) + when is_list(CipherTextFragment) -> + CipherLen = erlang:iolist_size(CipherTextFragment) - TagLen, + <<CipherText:CipherLen/bytes, CipherTag:TagLen/bytes>> = + erlang:iolist_to_binary(CipherTextFragment), + {CipherText, CipherTag}. + +decode_inner_plaintext(PlainText) -> + case binary:last(PlainText) of + 0 -> + decode_inner_plaintext(init_binary(PlainText)); + Type when Type =:= ?APPLICATION_DATA orelse + Type =:= ?HANDSHAKE orelse + Type =:= ?ALERT -> + #ssl_tls{type = Type, + version = {3,4}, %% Internally use real version + fragment = init_binary(PlainText)}; + _Else -> + ?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE, empty_alert) + end. + +init_binary(B) -> + {Init, _} = + split_binary(B, byte_size(B) - 1), + Init. 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 c07b7f49cd..d0604565e3 100644 --- a/lib/ssl/src/tls_sender.erl +++ b/lib/ssl/src/tls_sender.erl @@ -48,7 +48,8 @@ negotiated_version, renegotiate_at, connection_monitor, - dist_handle + dist_handle, + log_level }). -record(data, @@ -202,7 +203,8 @@ init({call, From}, {Pid, #{current_write := WriteState, tracker := Tracker, transport_cb := Transport, negotiated_version := Version, - renegotiate_at := RenegotiateAt}}, + renegotiate_at := RenegotiateAt, + log_level := LogLevel}}, #data{connection_states = ConnectionStates, static = Static0} = StateData0) -> Monitor = erlang:monitor(process, Pid), StateData = @@ -215,7 +217,8 @@ init({call, From}, {Pid, #{current_write := WriteState, tracker = Tracker, transport_cb = Transport, negotiated_version = Version, - renegotiate_at = RenegotiateAt}}, + renegotiate_at = RenegotiateAt, + log_level = LogLevel}}, {next_state, handshake, StateData, [{reply, From, ok}]}; init(_, _, _) -> %% Just in case anything else sneeks through @@ -406,11 +409,13 @@ handle_common(Type, Msg, _) -> send_tls_alert(#alert{} = Alert, #data{static = #static{negotiated_version = Version, socket = Socket, - transport_cb = Transport}, + transport_cb = Transport, + log_level = LogLevel}, connection_states = ConnectionStates0} = StateData0) -> {BinMsg, ConnectionStates} = tls_record:encode_alert_record(Alert, Version, ConnectionStates0), tls_socket:send(Transport, Socket, BinMsg), + ssl_logger:debug(LogLevel, outbound, 'record', BinMsg), StateData0#data{connection_states = ConnectionStates}. send_application_data(Data, From, StateName, @@ -419,7 +424,8 @@ send_application_data(Data, From, StateName, dist_handle = DistHandle, negotiated_version = Version, transport_cb = Transport, - renegotiate_at = RenegotiateAt}, + renegotiate_at = RenegotiateAt, + log_level = LogLevel}, connection_states = ConnectionStates0} = StateData0) -> case time_to_renegotiate(Data, ConnectionStates0, RenegotiateAt) of true -> @@ -431,10 +437,12 @@ send_application_data(Data, From, StateName, StateData = StateData0#data{connection_states = ConnectionStates}, case tls_socket:send(Transport, Socket, Msgs) of ok when DistHandle =/= undefined -> + ssl_logger:debug(LogLevel, outbound, 'record', Msgs), {next_state, StateName, StateData, []}; Reason when DistHandle =/= undefined -> {next_state, death_row, StateData, [{state_timeout, 5000, Reason}]}; ok -> + ssl_logger:debug(LogLevel, outbound, 'record', Msgs), {next_state, StateName, StateData, [{reply, From, ok}]}; Result -> {next_state, StateName, StateData, [{reply, From, Result}]} diff --git a/lib/ssl/src/tls_v1.erl b/lib/ssl/src/tls_v1.erl index 1bfd9a8b6d..f103f3218b 100644 --- a/lib/ssl/src/tls_v1.erl +++ b/lib/ssl/src/tls_v1.erl @@ -32,7 +32,19 @@ -export([master_secret/4, finished/5, certificate_verify/3, mac_hash/7, hmac_hash/3, 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_algs/1, signature_algs/2, + default_signature_schemes/1, signature_schemes/2, + groups/1, groups/2, group_to_enum/1, enum_to_group/1, default_groups/1]). + +-export([derive_secret/4, hkdf_expand_label/5, hkdf_extract/3, hkdf_expand/4, + key_schedule/3, key_schedule/4, create_info/3, + external_binder_key/2, resumption_binder_key/2, + client_early_traffic_secret/3, early_exporter_master_secret/3, + client_handshake_traffic_secret/3, server_handshake_traffic_secret/3, + client_application_traffic_secret_0/3, server_application_traffic_secret_0/3, + exporter_master_secret/3, resumption_master_secret/3, + update_traffic_secret/2, calculate_traffic_keys/3, + transcript_hash/2, finished_key/2, finished_verify_data/3]). -type named_curve() :: sect571r1 | sect571k1 | secp521r1 | brainpoolP512r1 | sect409k1 | sect409r1 | brainpoolP384r1 | secp384r1 | @@ -41,12 +53,68 @@ 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 %%==================================================================== +%% TLS 1.3 --------------------------------------------------- +-spec derive_secret(Secret::binary(), Label::binary(), + Messages::iodata(), Algo::ssl:hash()) -> Key::binary(). +derive_secret(Secret, Label, Messages, Algo) -> + Hash = crypto:hash(mac_algo(Algo), Messages), + hkdf_expand_label(Secret, Label, + Hash, ssl_cipher:hash_size(Algo), Algo). + +-spec hkdf_expand_label(Secret::binary(), Label0::binary(), + Context::binary(), Length::integer(), + Algo::ssl:hash()) -> KeyingMaterial::binary(). +hkdf_expand_label(Secret, Label0, Context, Length, Algo) -> + HkdfLabel = create_info(Label0, Context, Length), + hkdf_expand(Secret, HkdfLabel, Length, Algo). + +%% Create info parameter for HKDF-Expand: +%% HKDF-Expand(PRK, info, L) -> OKM +create_info(Label0, Context0, Length) -> + %% struct { + %% uint16 length = Length; + %% opaque label<7..255> = "tls13 " + Label; + %% opaque context<0..255> = Context; + %% } HkdfLabel; + Label1 = << <<"tls13 ">>/binary, Label0/binary>>, + LabelLen = size(Label1), + Label = <<?BYTE(LabelLen), Label1/binary>>, + ContextLen = size(Context0), + Context = <<?BYTE(ContextLen),Context0/binary>>, + Content = <<Label/binary, Context/binary>>, + <<?UINT16(Length), Content/binary>>. + +-spec hkdf_extract(MacAlg::ssl:hash(), Salt::binary(), + KeyingMaterial::binary()) -> PseudoRandKey::binary(). + +hkdf_extract(MacAlg, Salt, KeyingMaterial) -> + hmac_hash(MacAlg, Salt, KeyingMaterial). + + +-spec hkdf_expand(PseudoRandKey::binary(), ContextInfo::binary(), + Length::integer(), Algo::ssl:hash()) -> KeyingMaterial::binary(). + +hkdf_expand(PseudoRandKey, ContextInfo, Length, Algo) -> + Iterations = erlang:ceil(Length / ssl_cipher:hash_size(Algo)), + hkdf_expand(Algo, PseudoRandKey, ContextInfo, Length, 1, Iterations, <<>>, <<>>). + + +-spec transcript_hash(Messages::iodata(), Algo::ssl:hash()) -> Hash::binary(). + +transcript_hash(Messages, Algo) -> + crypto:hash(mac_algo(Algo), Messages). +%% TLS 1.3 --------------------------------------------------- + +%% TLS 1.0 -1.2 --------------------------------------------------- -spec master_secret(integer(), binary(), binary(), binary()) -> binary(). master_secret(PrfAlgo, PreMasterSecret, ClientRandom, ServerRandom) -> @@ -56,9 +124,10 @@ master_secret(PrfAlgo, PreMasterSecret, ClientRandom, ServerRandom) -> prf(PrfAlgo, PreMasterSecret, <<"master secret">>, [ClientRandom, ServerRandom], 48). +%% TLS 1.0 -1.2 --------------------------------------------------- -spec finished(client | server, integer(), integer(), binary(), [binary()]) -> binary(). - +%% TLS 1.0 -1.1 --------------------------------------------------- finished(Role, Version, PrfAlgo, MasterSecret, Handshake) when Version == 1; Version == 2; PrfAlgo == ?MD5SHA -> %% RFC 2246 & 4346 - 7.4.9. Finished @@ -72,7 +141,9 @@ finished(Role, Version, PrfAlgo, MasterSecret, Handshake) MD5 = crypto:hash(md5, Handshake), SHA = crypto:hash(sha, Handshake), prf(?MD5SHA, MasterSecret, finished_label(Role), [MD5, SHA], 12); +%% TLS 1.0 -1.1 --------------------------------------------------- +%% TLS 1.2 --------------------------------------------------- finished(Role, Version, PrfAlgo, MasterSecret, Handshake) when Version == 3 -> %% RFC 5246 - 7.4.9. Finished @@ -84,21 +155,28 @@ finished(Role, Version, PrfAlgo, MasterSecret, Handshake) %% PRF(master_secret, finished_label, Hash(handshake_messages)) [0..11]; Hash = crypto:hash(mac_algo(PrfAlgo), Handshake), prf(PrfAlgo, MasterSecret, finished_label(Role), Hash, 12). +%% TLS 1.2 --------------------------------------------------- + +%% TODO 1.3 finished -spec certificate_verify(md5sha | sha, integer(), [binary()]) -> binary(). +%% TLS 1.0 -1.1 --------------------------------------------------- certificate_verify(md5sha, _Version, Handshake) -> MD5 = crypto:hash(md5, Handshake), SHA = crypto:hash(sha, Handshake), <<MD5/binary, SHA/binary>>; +%% TLS 1.0 -1.1 --------------------------------------------------- +%% TLS 1.2 --------------------------------------------------- certificate_verify(HashAlgo, _Version, Handshake) -> crypto:hash(HashAlgo, Handshake). +%% TLS 1.2 --------------------------------------------------- -spec setup_keys(integer(), integer(), binary(), binary(), binary(), integer(), integer(), integer()) -> {binary(), binary(), binary(), binary(), binary(), binary()}. - +%% TLS v1.0 --------------------------------------------------- setup_keys(Version, _PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize, KeyMatLen, IVSize) when Version == 1 -> @@ -123,8 +201,9 @@ setup_keys(Version, _PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize ClientIV:IVSize/binary, ServerIV:IVSize/binary>> = KeyBlock, {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey, ServerWriteKey, ClientIV, ServerIV}; +%% TLS v1.0 --------------------------------------------------- -%% TLS v1.1 +%% TLS v1.1 --------------------------------------------------- setup_keys(Version, _PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize, KeyMatLen, IVSize) when Version == 2 -> @@ -150,11 +229,12 @@ setup_keys(Version, _PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize ClientIV:IVSize/binary, ServerIV:IVSize/binary>> = KeyBlock, {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey, ServerWriteKey, ClientIV, ServerIV}; +%% TLS v1.1 --------------------------------------------------- -%% TLS v1.2 +%% TLS v1.2 --------------------------------------------------- setup_keys(Version, PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize, KeyMatLen, IVSize) - when Version == 3 -> + when Version == 3; Version == 4 -> %% RFC 5246 - 6.3. Key calculation %% key_block = PRF(SecurityParameters.master_secret, %% "key expansion", @@ -176,8 +256,177 @@ setup_keys(Version, PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize, ClientIV:IVSize/binary, ServerIV:IVSize/binary>> = KeyBlock, {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey, ServerWriteKey, ClientIV, ServerIV}. +%% TLS v1.2 --------------------------------------------------- --spec mac_hash(integer(), binary(), integer(), integer(), tls_record:tls_version(), +%% TLS v1.3 --------------------------------------------------- +%% RFC 8446 - 7.1. Key Schedule +%% +%% 0 +%% | +%% v +%% PSK -> HKDF-Extract = Early Secret +%% | +%% +-----> Derive-Secret(., "ext binder" | "res binder", "") +%% | = binder_key +%% | +%% +-----> Derive-Secret(., "c e traffic", ClientHello) +%% | = client_early_traffic_secret +%% | +%% +-----> Derive-Secret(., "e exp master", ClientHello) +%% | = early_exporter_master_secret +%% v +%% Derive-Secret(., "derived", "") +%% | +%% v +%% (EC)DHE -> HKDF-Extract = Handshake Secret +%% | +%% +-----> Derive-Secret(., "c hs traffic", +%% | ClientHello...ServerHello) +%% | = client_handshake_traffic_secret +%% | +%% +-----> Derive-Secret(., "s hs traffic", +%% | ClientHello...ServerHello) +%% | = server_handshake_traffic_secret +%% v +%% Derive-Secret(., "derived", "") +%% | +%% v +%% 0 -> HKDF-Extract = Master Secret +%% | +%% +-----> Derive-Secret(., "c ap traffic", +%% | ClientHello...server Finished) +%% | = client_application_traffic_secret_0 +%% | +%% +-----> Derive-Secret(., "s ap traffic", +%% | ClientHello...server Finished) +%% | = server_application_traffic_secret_0 +%% | +%% +-----> Derive-Secret(., "exp master", +%% | ClientHello...server Finished) +%% | = exporter_master_secret +%% | +%% +-----> Derive-Secret(., "res master", +%% ClientHello...client Finished) +%% = resumption_master_secret +-spec key_schedule(early_secret | handshake_secret | master_secret, + atom(), {psk | early_secret | handshake_secret, binary()}) -> + {early_secret | handshake_secret | master_secret, binary()}. + +key_schedule(early_secret, Algo, {psk, PSK}) -> + Len = ssl_cipher:hash_size(Algo), + Salt = binary:copy(<<?BYTE(0)>>, Len), + {early_secret, hkdf_extract(Algo, Salt, PSK)}; +key_schedule(master_secret, Algo, {handshake_secret, Secret}) -> + Len = ssl_cipher:hash_size(Algo), + IKM = binary:copy(<<?BYTE(0)>>, Len), + Salt = derive_secret(Secret, <<"derived">>, <<>>, Algo), + {master_secret, hkdf_extract(Algo, Salt, IKM)}. +%% +key_schedule(handshake_secret, Algo, IKM, {early_secret, Secret}) -> + Salt = derive_secret(Secret, <<"derived">>, <<>>, Algo), + {handshake_secret, hkdf_extract(Algo, Salt, IKM)}. + +-spec external_binder_key(atom(), {early_secret, binary()}) -> binary(). +external_binder_key(Algo, {early_secret, Secret}) -> + derive_secret(Secret, <<"ext binder">>, <<>>, Algo). + +-spec resumption_binder_key(atom(), {early_secret, binary()}) -> binary(). +resumption_binder_key(Algo, {early_secret, Secret}) -> + derive_secret(Secret, <<"res binder">>, <<>>, Algo). + +-spec client_early_traffic_secret(atom(), {early_secret, binary()}, iodata()) -> binary(). +%% M = ClientHello +client_early_traffic_secret(Algo, {early_secret, Secret}, M) -> + derive_secret(Secret, <<"c e traffic">>, M, Algo). + +-spec early_exporter_master_secret(atom(), {early_secret, binary()}, iodata()) -> binary(). +%% M = ClientHello +early_exporter_master_secret(Algo, {early_secret, Secret}, M) -> + derive_secret(Secret, <<"e exp master">>, M, Algo). + +-spec client_handshake_traffic_secret(atom(), {handshake_secret, binary()}, iodata()) -> binary(). +%% M = ClientHello...ServerHello +client_handshake_traffic_secret(Algo, {handshake_secret, Secret}, M) -> + derive_secret(Secret, <<"c hs traffic">>, M, Algo). + +-spec server_handshake_traffic_secret(atom(), {handshake_secret, binary()}, iodata()) -> binary(). +%% M = ClientHello...ServerHello +server_handshake_traffic_secret(Algo, {handshake_secret, Secret}, M) -> + derive_secret(Secret, <<"s hs traffic">>, M, Algo). + +-spec client_application_traffic_secret_0(atom(), {master_secret, binary()}, iodata()) -> binary(). +%% M = ClientHello...server Finished +client_application_traffic_secret_0(Algo, {master_secret, Secret}, M) -> + derive_secret(Secret, <<"c ap traffic">>, M, Algo). + +-spec server_application_traffic_secret_0(atom(), {master_secret, binary()}, iodata()) -> binary(). +%% M = ClientHello...server Finished +server_application_traffic_secret_0(Algo, {master_secret, Secret}, M) -> + derive_secret(Secret, <<"s ap traffic">>, M, Algo). + +-spec exporter_master_secret(atom(), {master_secret, binary()}, iodata()) -> binary(). +%% M = ClientHello...server Finished +exporter_master_secret(Algo, {master_secret, Secret}, M) -> + derive_secret(Secret, <<"exp master">>, M, Algo). + +-spec resumption_master_secret(atom(), {master_secret, binary()}, iodata()) -> binary(). +%% M = ClientHello...client Finished +resumption_master_secret(Algo, {master_secret, Secret}, M) -> + derive_secret(Secret, <<"res master">>, M, Algo). + +-spec finished_key(binary(), atom()) -> binary(). +finished_key(BaseKey, Algo) -> + %% finished_key = + %% HKDF-Expand-Label(BaseKey, "finished", "", Hash.length) + ssl_cipher:hash_size(Algo), + hkdf_expand_label(BaseKey, <<"finished">>, <<>>, ssl_cipher:hash_size(Algo), Algo). + +-spec finished_verify_data(binary(), atom(), iodata()) -> binary(). +finished_verify_data(FinishedKey, HKDFAlgo, Messages) -> + %% The verify_data value is computed as follows: + %% + %% verify_data = + %% HMAC(finished_key, + %% Transcript-Hash(Handshake Context, + %% Certificate*, CertificateVerify*)) + Context = lists:reverse(Messages), + THash = tls_v1:transcript_hash(Context, HKDFAlgo), + tls_v1:hmac_hash(HKDFAlgo, FinishedKey, THash). + +%% The next-generation application_traffic_secret is computed as: +%% +%% application_traffic_secret_N+1 = +%% HKDF-Expand-Label(application_traffic_secret_N, +%% "traffic upd", "", Hash.length) +-spec update_traffic_secret(atom(), binary()) -> binary(). +update_traffic_secret(Algo, Secret) -> + hkdf_expand_label(Secret, <<"traffic upd">>, <<>>, ssl_cipher:hash_size(Algo), Algo). + +%% The traffic keying material is generated from the following input +%% values: +%% +%% - A secret value +%% +%% - A purpose value indicating the specific value being generated +%% +%% - The length of the key being generated +%% +%% The traffic keying material is generated from an input traffic secret +%% value using: +%% +%% [sender]_write_key = HKDF-Expand-Label(Secret, "key", "", key_length) +%% [sender]_write_iv = HKDF-Expand-Label(Secret, "iv", "", iv_length) +-spec calculate_traffic_keys(atom(), atom(), binary()) -> {binary(), binary()}. +calculate_traffic_keys(HKDFAlgo, Cipher, Secret) -> + Key = hkdf_expand_label(Secret, <<"key">>, <<>>, ssl_cipher:key_material(Cipher), HKDFAlgo), + %% TODO: remove hard coded IV size + IV = hkdf_expand_label(Secret, <<"iv">>, <<>>, 12, HKDFAlgo), + {Key, IV}. + +%% TLS v1.3 --------------------------------------------------- + +%% TLS 1.0 -1.2 --------------------------------------------------- +-spec mac_hash(integer() | atom(), binary(), integer(), integer(), tls_record:tls_version(), integer(), binary()) -> binary(). mac_hash(Method, Mac_write_secret, Seq_num, Type, {Major, Minor}, @@ -191,8 +440,11 @@ mac_hash(Method, Mac_write_secret, Seq_num, Type, {Major, Minor}, ?BYTE(Major), ?BYTE(Minor), ?UINT16(Length)>>, Fragment]), Mac. +%% TLS 1.0 -1.2 --------------------------------------------------- + +%% TODO 1.3 same as above? --spec suites(1|2|3) -> [ssl_cipher_format:cipher_suite()]. +-spec suites(1|2|3|4|'TLS_v1.3') -> [ssl_cipher_format:cipher_suite()]. suites(Minor) when Minor == 1; Minor == 2 -> [ @@ -244,8 +496,29 @@ suites(3) -> %% ?TLS_DH_DSS_WITH_AES_256_GCM_SHA384, %% ?TLS_DH_RSA_WITH_AES_128_GCM_SHA256, %% ?TLS_DH_DSS_WITH_AES_128_GCM_SHA256 - ] ++ suites(2). - + ] ++ suites(2); + +suites(4) -> + [?TLS_AES_256_GCM_SHA384, + ?TLS_AES_128_GCM_SHA256, + ?TLS_CHACHA20_POLY1305_SHA256 + %% Not supported + %% ?TLS_AES_128_CCM_SHA256, + %% ?TLS_AES_128_CCM_8_SHA256 + ] ++ suites(3); + +suites('TLS_v1.3') -> + [?TLS_AES_256_GCM_SHA384, + ?TLS_AES_128_GCM_SHA256, + ?TLS_CHACHA20_POLY1305_SHA256 + %% Not supported + %% ?TLS_AES_128_CCM_SHA256, + %% ?TLS_AES_128_CCM_8_SHA256 + ]. + + +signature_algs({3, 4}, HashSigns) -> + signature_algs({3, 3}, HashSigns); signature_algs({3, 3}, HashSigns) -> CryptoSupports = crypto:supports(), Hashes = proplists:get_value(hashs, CryptoSupports), @@ -273,6 +546,10 @@ signature_algs({3, 3}, HashSigns) -> end, [], HashSigns), lists:reverse(Supported). +default_signature_algs({3, 4} = Version) -> + %% TLS 1.3 servers shall be prepared to process TLS 1.2 ClientHellos + %% containing legacy hash-sign tuples. + default_signature_schemes(Version) ++ default_signature_algs({3,3}); default_signature_algs({3, 3} = Version) -> Default = [%% SHA2 {sha512, ecdsa}, @@ -291,15 +568,99 @@ default_signature_algs({3, 3} = Version) -> default_signature_algs(_) -> undefined. + +signature_schemes(Version, SignatureSchemes) when is_tuple(Version) + andalso Version >= {3, 3} -> + CryptoSupports = crypto:supports(), + Hashes = proplists:get_value(hashs, CryptoSupports), + PubKeys = proplists:get_value(public_keys, CryptoSupports), + Curves = proplists:get_value(curves, CryptoSupports), + RSAPSSSupported = lists:member(rsa_pkcs1_pss_padding, + proplists:get_value(rsa_opts, CryptoSupports)), + Fun = fun (Scheme, Acc) when is_atom(Scheme) -> + {Hash0, Sign0, Curve} = + ssl_cipher:scheme_to_components(Scheme), + Sign = case Sign0 of + rsa_pkcs1 -> + rsa; + rsa_pss_rsae when RSAPSSSupported -> + rsa; + rsa_pss_pss when RSAPSSSupported -> + rsa; + S -> S + end, + Hash = case Hash0 of + sha1 -> + sha; + H -> H + end, + case proplists:get_bool(Sign, PubKeys) + andalso proplists:get_bool(Hash, Hashes) + andalso (Curve =:= undefined orelse + proplists:get_bool(Curve, Curves)) + andalso is_pair(Hash, Sign, Hashes) + of + true -> + [Scheme | Acc]; + false -> + Acc + end; + %% Special clause for filtering out the legacy hash-sign tuples. + (_ , Acc) -> + Acc + end, + Supported = lists:foldl(Fun, [], SignatureSchemes), + lists:reverse(Supported); +signature_schemes(_, _) -> + []. + +default_signature_schemes(Version) -> + Default = [ + ecdsa_secp521r1_sha512, + ecdsa_secp384r1_sha384, + ecdsa_secp256r1_sha256, + rsa_pss_pss_sha512, + rsa_pss_pss_sha384, + rsa_pss_pss_sha256, + rsa_pss_rsae_sha512, + rsa_pss_rsae_sha384, + rsa_pss_rsae_sha256, + %% ed25519, + %% ed448, + + %% These values refer solely to signatures + %% which appear in certificates (see Section 4.4.2.2) and are not + %% defined for use in signed TLS handshake messages, although they + %% MAY appear in "signature_algorithms" and + %% "signature_algorithms_cert" for backward compatibility with + %% TLS 1.2. + rsa_pkcs1_sha512, + rsa_pkcs1_sha384, + rsa_pkcs1_sha256, + ecdsa_sha1, + rsa_pkcs1_sha1 + ], + signature_schemes(Version, Default). + + %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- +hkdf_expand(Algo, PseudoRandKey, ContextInfo, Length, N, N, Prev, Acc) -> + Keyingmaterial = hmac_hash(Algo, PseudoRandKey, <<Prev/binary, ContextInfo/binary, ?BYTE(N)>>), + binary:part(<<Acc/binary, Keyingmaterial/binary>>, {0, Length}); +hkdf_expand(Algo, PseudoRandKey, ContextInfo, Length, M, N, Prev, Acc) -> + Keyingmaterial = hmac_hash(Algo, PseudoRandKey, <<Prev/binary, ContextInfo/binary, ?BYTE(M)>>), + hkdf_expand(Algo, PseudoRandKey, ContextInfo, Length, M + 1, N, Keyingmaterial, <<Acc/binary, Keyingmaterial/binary>>). + %%%% HMAC and the Pseudorandom Functions RFC 2246 & 4346 - 5.%%%% hmac_hash(?NULL, _, _) -> <<>>; hmac_hash(Alg, Key, Value) -> crypto:hmac(mac_algo(Alg), Key, Value). +mac_algo(Alg) when is_atom(Alg) -> + Alg; mac_algo(?MD5) -> md5; mac_algo(?SHA) -> sha; mac_algo(?SHA256) -> sha256; @@ -395,6 +756,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). @@ -409,6 +771,63 @@ ecc_curves(_Minor, TLSCurves) -> end end, [], TLSCurves). +-spec groups(4 | all | default) -> [group()]. +groups(all) -> + [x25519, + x448, + secp256r1, + secp384r1, + secp521r1, + ffdhe2048, + ffdhe3072, + ffdhe4096, + ffdhe6144, + ffdhe8192]; +groups(default) -> + [x25519, + x448, + secp256r1, + secp384r1]; +groups(Minor) -> + TLSGroups = groups(all), + groups(Minor, TLSGroups). +%% +-spec groups(4, [group()]) -> [group()]. +groups(_Minor, TLSGroups) -> + CryptoGroups = supported_groups(), + lists:filter(fun(Group) -> proplists:get_bool(Group, CryptoGroups) end, TLSGroups). + +default_groups(Minor) -> + TLSGroups = groups(default), + groups(Minor, TLSGroups). + +supported_groups() -> + %% TODO: Add new function to crypto? + proplists:get_value(curves, crypto:supports()) ++ + [ffdhe2048,ffdhe3072,ffdhe4096,ffdhe6144,ffdhe8192]. + +group_to_enum(secp256r1) -> 23; +group_to_enum(secp384r1) -> 24; +group_to_enum(secp521r1) -> 25; +group_to_enum(x25519) -> 29; +group_to_enum(x448) -> 30; +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(29) -> x25519; +enum_to_group(30) -> x448; +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/Makefile b/lib/ssl/test/Makefile index a10f71a3de..f7fae16088 100644 --- a/lib/ssl/test/Makefile +++ b/lib/ssl/test/Makefile @@ -43,6 +43,7 @@ MODULES = \ ssl_basic_SUITE \ ssl_bench_SUITE \ ssl_cipher_SUITE \ + ssl_cipher_suite_SUITE \ ssl_certificate_verify_SUITE\ ssl_crl_SUITE\ ssl_dist_SUITE \ @@ -61,8 +62,11 @@ MODULES = \ ssl_ECC\ ssl_upgrade_SUITE\ ssl_sni_SUITE \ - make_certs\ - x509_test + ssl_eqc_SUITE \ + ssl_rfc_5869_SUITE \ + make_certs \ + x509_test \ + inet_crypto_dist ERL_FILES = $(MODULES:%=%.erl) @@ -144,7 +148,7 @@ release_tests_spec: opt $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(HRL_FILES_NEEDED_IN_TEST) $(COVER_FILE) "$(RELSYSDIR)" $(INSTALL_DATA) ssl.spec ssl_bench.spec ssl.cover "$(RELSYSDIR)" chmod -R u+w "$(RELSYSDIR)" - @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) + @tar cf - *_SUITE_data property_test | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/ssl/test/inet_crypto_dist.erl b/lib/ssl/test/inet_crypto_dist.erl new file mode 100644 index 0000000000..5aafaac983 --- /dev/null +++ b/lib/ssl/test/inet_crypto_dist.erl @@ -0,0 +1,1323 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2019. 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 for encrypted Erlang protocol - a minimal encrypted +%% distribution protocol based on only a shared secret +%% and the crypto application +%% +-module(inet_crypto_dist). +-define(DIST_NAME, inet_crypto). +-define(DIST_PROTO, crypto). +-define(DRIVER, inet_tcp). +-define(FAMILY, inet). + +-define(PROTOCOL, inet_crypto_dist_v1). +-define(DEFAULT_BLOCK_CRYPTO, aes_128_gcm). +-define(DEFAULT_HASH_ALGORITHM, sha256). +-define(DEFAULT_REKEY_INTERVAL, 32768). + +-export([listen/1, accept/1, accept_connection/5, + setup/5, close/1, select/1, is_node_name/1]). +-export([is_supported/0]). + +%% Generalized dist API, for sibling IPv6 module inet6_crypto_dist +-export([gen_listen/2, gen_accept/2, gen_accept_connection/6, + gen_setup/6, gen_close/2, gen_select/2]). + +-export([nodelay/0]). + +%% Debug +%%%-compile(export_all). +-export([dbg/0, test_server/0, test_client/1]). + +-include_lib("kernel/include/net_address.hrl"). +-include_lib("kernel/include/dist.hrl"). +-include_lib("kernel/include/dist_util.hrl"). + +%% Test if crypto has got enough capabilities for this module to run +%% +is_supported() -> + try {crypto:cipher_info(?DEFAULT_BLOCK_CRYPTO), + crypto:hash_info(?DEFAULT_HASH_ALGORITHM)} + of + {#{block_size := _, iv_length := _, key_length := _}, + #{size := _}} -> + true + catch + error:undef -> + false + end. + +%% ------------------------------------------------------------------------- +%% Erlang distribution plugin structure explained to myself +%% ------- +%% These are the processes involved in the distribution: +%% * net_kernel +%% * The Acceptor +%% * The Controller | Handshaker | Ticker +%% * The DistCtrl process that may be split into: +%% + The Output controller +%% + The Input controller +%% For the regular inet_tcp_dist distribution module, DistCtrl +%% is not one or two processes, but one port - a gen_tcp socket +%% +%% When the VM is started with the argument "-proto_dist inet_crypto" +%% net_kernel registers the module inet_crypto_dist as distribution +%% module. net_kernel calls listen/1 to create a listen socket +%% and then accept/1 with the listen socket as argument to spawn +%% the Acceptor process, which is linked to net_kernel. Apparently +%% the listen socket is owned by net_kernel - I wonder if it could +%% be owned by the Acceptor process instead... +%% +%% The Acceptor process calls blocking accept on the listen socket +%% and when an incoming socket is returned it spawns the DistCtrl +%% process a linked to the Acceptor. The ownership of the accepted +%% socket is transferred to the DistCtrl process. +%% A message is sent to net_kernel to inform it that an incoming +%% connection has appeared and the Acceptor awaits a reply from net_kernel. +%% +%% net_kernel then calls accept_connection/5 to spawn the Controller | +%% Handshaker | Ticker process that is linked to net_kernel. +%% The Controller then awaits a message from the Acceptor process. +%% +%% When net_kernel has spawned the Controller it replies with a message +%% to the Acceptor that then calls DistCtrl to changes its links +%% so DistCtrl ends up linked to the Controller and not to the Acceptor. +%% The Acceptor then sends a message to the Controller. The Controller +%% then changes role into the Handshaker creates a #hs_data{} record +%% and calls dist_util:handshake_other_started/1. After this +%% the Acceptor goes back into a blocking accept on the listen socket. +%% +%% For the regular distribution inet_tcp_dist DistCtrl is a gen_tcp socket +%% and when it is a process it also acts as a socket. The #hs_data{} +%% record used by dist_util presents a set of funs that are used +%% by dist_util to perform the distribution handshake. These funs +%% make sure to transfer the handshake messages through the DistCtrl +%% "socket". +%% +%% When the handshake is finished a fun for this purpose in #hs_data{} +%% is called, which tells DistCtrl that it does not need to be prepared +%% for any more #hs_data{} handshake calls. The DistCtrl process in this +%% module then spawns the Input controller process that gets ownership +%% of the connection's gen_tcp socket and changes into {active, N} mode +%% so now it gets all incoming traffic and delivers that to the VM. +%% The original DistCtrl process changes role into the Output controller +%% process and starts asking the VM for outbound messages and transfers +%% them on the connection socket. +%% +%% The Handshaker now changes into the Ticker role, and uses only two +%% functions in the #hs_data{} record; one to get socket statistics +%% and one to send a tick. None of these may block for any reason +%% in particular not for a congested socket since that would destroy +%% connection supervision. +%% +%% +%% For an connection net_kernel calls setup/5 which spawns the +%% Controller process as linked to net_kernel. This Controller process +%% connects to the other node's listen socket and when that is succesful +%% spawns the DistCtrl process as linked to the controller and transfers +%% socket ownership to it. +%% +%% Then the Controller creates the #hs_data{} record and calls +%% dist_util:handshake_we_started/1 which changes the process role +%% into Handshaker. +%% +%% When the distribution handshake is finished the procedure is just +%% as for an incoming connection above. +%% +%% +%% To sum it up. +%% +%% There is an Acceptor process that is linked to net_kernel and +%% informs it when new connections arrive. +%% +%% net_kernel spawns Controllers for incoming and for outgoing connections. +%% these Controllers use the DistCtrl processes to do distribution +%% handshake and after that becomes Tickers that supervise the connection. +%% +%% The Controller | Handshaker | Ticker is linked to net_kernel, and to +%% DistCtrl, one or both. If any of these connection processes would die +%% all others should be killed by the links. Therefore none of them may +%% terminate with reason 'normal'. +%% ------------------------------------------------------------------------- + +%% ------------------------------------------------------------------------- +%% select/1 is called by net_kernel to ask if this distribution protocol +%% is willing to handle Node +%% + +select(Node) -> + gen_select(Node, ?DRIVER). + +gen_select(Node, Driver) -> + case dist_util:split_node(Node) of + {node, _, Host} -> + case Driver:getaddr(Host) of + {ok, _} -> true; + _ -> false + end; + _ -> + false + end. + +%% ------------------------------------------------------------------------- + +is_node_name(Node) -> + dist_util:is_node_name(Node). + +%% ------------------------------------------------------------------------- +%% Called by net_kernel to create a listen socket for this +%% distribution protocol. This listen socket is used by +%% the Acceptor process. +%% + +listen(Name) -> + gen_listen(Name, ?DRIVER). + +gen_listen(Name, Driver) -> + case inet_tcp_dist:gen_listen(Driver, Name) of + {ok, {Socket, Address, Creation}} -> + inet:setopts(Socket, [binary, {nodelay, true}]), + {ok, + {Socket, Address#net_address{protocol = ?DIST_PROTO}, Creation}}; + Other -> + Other + end. + +%% ------------------------------------------------------------------------- +%% Called by net_kernel to spawn the Acceptor process that awaits +%% new connection in a blocking accept and informs net_kernel +%% when a new connection has appeared, and starts the DistCtrl +%% "socket" process for the connection. +%% + +accept(Listen) -> + gen_accept(Listen, ?DRIVER). + +gen_accept(Listen, Driver) -> + NetKernel = self(), + %% + %% Spawn Acceptor process + %% + Config = config(), + monitor_dist_proc( + spawn_opt( + fun () -> + accept_loop(Listen, Driver, NetKernel, Config) + end, + [link, {priority, max}])). + +accept_loop(Listen, Driver, NetKernel, Config) -> + case Driver:accept(Listen) of + {ok, Socket} -> + wait_for_code_server(), + Timeout = net_kernel:connecttime(), + DistCtrl = start_dist_ctrl(Socket, Config, Timeout), + %% DistCtrl is a "socket" + NetKernel ! + {accept, + self(), DistCtrl, Driver:family(), ?DIST_PROTO}, + receive + {NetKernel, controller, Controller} -> + call_dist_ctrl(DistCtrl, {controller, Controller, self()}), + Controller ! {self(), controller, Socket}; + {NetKernel, unsupported_protocol} -> + exit(unsupported_protocol) + end, + accept_loop(Listen, Driver, NetKernel, Config); + AcceptError -> + exit({accept, AcceptError}) + end. + +wait_for_code_server() -> + %% This is an ugly hack. Starting encryption on a connection + %% requires the crypto module to be loaded. Loading the crypto + %% module triggers its on_load function, which calls + %% code:priv_dir/1 to find the directory where its NIF library is. + %% However, distribution is started earlier than the code server, + %% so the code server is not necessarily started yet, and + %% code:priv_dir/1 might fail because of that, if we receive + %% an incoming connection on the distribution port early enough. + %% + %% If the on_load function of a module fails, the module is + %% unloaded, and the function call that triggered loading it fails + %% with 'undef', which is rather confusing. + %% + %% So let's avoid that by waiting for the code server to start. + %% + case whereis(code_server) of + undefined -> + timer:sleep(10), + wait_for_code_server(); + Pid when is_pid(Pid) -> + ok + end. + +%% ------------------------------------------------------------------------- +%% Called by net_kernel when a new connection has appeared, to spawn +%% a Controller process that performs the handshake with the new node, +%% and then becomes the Ticker connection supervisor. +%% ------------------------------------------------------------------------- + +accept_connection(Acceptor, DistCtrl, MyNode, Allowed, SetupTime) -> + gen_accept_connection( + Acceptor, DistCtrl, MyNode, Allowed, SetupTime, ?DRIVER). + +gen_accept_connection( + Acceptor, DistCtrl, MyNode, Allowed, SetupTime, Driver) -> + NetKernel = self(), + %% + %% Spawn Controller/handshaker/ticker process + %% + monitor_dist_proc( + spawn_opt( + fun() -> + do_accept( + Acceptor, DistCtrl, + MyNode, Allowed, SetupTime, Driver, NetKernel) + end, + [link, {priority, max}])). + +do_accept( + Acceptor, DistCtrl, MyNode, Allowed, SetupTime, Driver, NetKernel) -> + receive + {Acceptor, controller, Socket} -> + Timer = dist_util:start_timer(SetupTime), + HSData = + hs_data_common( + NetKernel, MyNode, DistCtrl, Timer, + Socket, Driver:family()), + HSData_1 = + HSData#hs_data{ + this_node = MyNode, + this_flags = 0, + allowed = Allowed}, + dist_util:handshake_other_started(trace(HSData_1)) + end. + +%% ------------------------------------------------------------------------- +%% Called by net_kernel to spawn a Controller process that sets up +%% a new connection to another Erlang node, performs the handshake +%% with the other it, and then becomes the Ticker process +%% that supervises the connection. +%% ------------------------------------------------------------------------- + +setup(Node, Type, MyNode, LongOrShortNames, SetupTime) -> + gen_setup(Node, Type, MyNode, LongOrShortNames, SetupTime, ?DRIVER). + +gen_setup(Node, Type, MyNode, LongOrShortNames, SetupTime, Driver) -> + NetKernel = self(), + %% + %% Spawn Controller/handshaker/ticker process + %% + monitor_dist_proc( + spawn_opt( + setup_fun( + Node, Type, MyNode, LongOrShortNames, SetupTime, Driver, NetKernel), + [link, {priority, max}])). + +-spec setup_fun(_,_,_,_,_,_,_) -> fun(() -> no_return()). +setup_fun( + Node, Type, MyNode, LongOrShortNames, SetupTime, Driver, NetKernel) -> + fun() -> + do_setup( + Node, Type, MyNode, LongOrShortNames, SetupTime, + Driver, NetKernel) + end. + +-spec do_setup(_,_,_,_,_,_,_) -> no_return(). +do_setup( + Node, Type, MyNode, LongOrShortNames, SetupTime, Driver, NetKernel) -> + {Name, Address} = split_node(Driver, Node, LongOrShortNames), + ErlEpmd = net_kernel:epmd_module(), + {ARMod, ARFun} = get_address_resolver(ErlEpmd, Driver), + Timer = trace(dist_util:start_timer(SetupTime)), + case ARMod:ARFun(Name, Address, Driver:family()) of + {ok, Ip, TcpPort, Version} -> + do_setup_connect( + Node, Type, MyNode, Timer, Driver, NetKernel, + Ip, TcpPort, Version); + {ok, Ip} -> + case ErlEpmd:port_please(Name, Ip) of + {port, TcpPort, Version} -> + do_setup_connect( + Node, Type, MyNode, Timer, Driver, NetKernel, + Ip, TcpPort, Version); + Other -> + ?shutdown2( + Node, + trace( + {port_please_failed, ErlEpmd, Name, Ip, Other})) + end; + Other -> + ?shutdown2( + Node, + trace({getaddr_failed, Driver, Address, Other})) + end. + +-spec do_setup_connect(_,_,_,_,_,_,_,_,_) -> no_return(). + +do_setup_connect( + Node, Type, MyNode, Timer, Driver, NetKernel, + Ip, TcpPort, Version) -> + dist_util:reset_timer(Timer), + ConnectOpts = + trace( + connect_options( + [binary, {active, false}, {packet, 2}, {nodelay, true}])), + case Driver:connect(Ip, TcpPort, ConnectOpts) of + {ok, Socket} -> + Config = config(), + DistCtrl = + start_dist_ctrl(Socket, Config, net_kernel:connecttime()), + %% DistCtrl is a "socket" + HSData = + hs_data_common( + NetKernel, MyNode, DistCtrl, Timer, + Socket, Driver:family()), + HSData_1 = + HSData#hs_data{ + other_node = Node, + this_flags = 0, + other_version = Version, + request_type = Type}, + dist_util:handshake_we_started(trace(HSData_1)); + ConnectError -> + ?shutdown2(Node, + trace({connect_failed, Ip, TcpPort, ConnectError})) + end. + +%% ------------------------------------------------------------------------- +%% close/1 is only called by net_kernel on the socket returned by listen/1. + +close(Socket) -> + gen_close(Socket, ?DRIVER). + +gen_close(Socket, Driver) -> + trace(Driver:close(Socket)). + +%% ------------------------------------------------------------------------- + + +hs_data_common(NetKernel, MyNode, DistCtrl, Timer, Socket, Family) -> + %% Field 'socket' below is set to DistCtrl, which makes + %% the distribution handshake process (ticker) call + %% the funs below with DistCtrl as the S argument. + %% So, S =:= DistCtrl below... + #hs_data{ + kernel_pid = NetKernel, + this_node = MyNode, + socket = DistCtrl, + timer = Timer, + %% + f_send = + fun (S, Packet) when S =:= DistCtrl -> + call_dist_ctrl(S, {send, Packet}) + end, + f_recv = + fun (S, 0, infinity) when S =:= DistCtrl -> + case call_dist_ctrl(S, recv) of + {ok, Bin} when is_binary(Bin) -> + {ok, binary_to_list(Bin)}; + Error -> + Error + end + end, + f_setopts_pre_nodeup = + fun (S) when S =:= DistCtrl -> + ok + end, + f_setopts_post_nodeup = + fun (S) when S =:= DistCtrl -> + ok + end, + f_getll = + fun (S) when S =:= DistCtrl -> + {ok, S} %% DistCtrl is the distribution port + end, + f_address = + fun (S, Node) when S =:= DistCtrl -> + case call_dist_ctrl(S, peername) of + {ok, Address} -> + case dist_util:split_node(Node) of + {node, _, Host} -> + #net_address{ + address = Address, + host = Host, + protocol = ?DIST_PROTO, + family = Family}; + _ -> + {error, no_node} + end + end + end, + f_handshake_complete = + fun (S, _Node, DistHandle) when S =:= DistCtrl -> + call_dist_ctrl(S, {handshake_complete, DistHandle}) + end, + %% + %% mf_tick/1, mf_getstat/1, mf_setopts/2 and mf_getopts/2 + %% are called by the ticker any time after f_handshake_complete/3 + %% so they may not block the caller even for congested socket + mf_tick = + fun (S) when S =:= DistCtrl -> + S ! dist_tick + end, + mf_getstat = + fun (S) when S =:= DistCtrl -> + case + inet:getstat(Socket, [recv_cnt, send_cnt, send_pend]) + of + {ok, Stat} -> + split_stat(Stat, 0, 0, 0); + Error -> + Error + end + end, + mf_setopts = + fun (S, Opts) when S =:= DistCtrl -> + inet:setopts(Socket, setopts_filter(Opts)) + end, + mf_getopts = + fun (S, Opts) when S =:= DistCtrl -> + inet:getopts(Socket, Opts) + end}. + +setopts_filter(Opts) -> + [Opt || + Opt <- Opts, + case Opt of + {K, _} when K =:= active; K =:= deliver; K =:= packet -> false; + K when K =:= list; K =:= binary -> false; + K when K =:= inet; K =:= inet6 -> false; + _ -> true + end]. + +split_stat([{recv_cnt, R}|Stat], _, W, P) -> + split_stat(Stat, R, W, P); +split_stat([{send_cnt, W}|Stat], R, _, P) -> + split_stat(Stat, R, W, P); +split_stat([{send_pend, P}|Stat], R, W, _) -> + split_stat(Stat, R, W, P); +split_stat([], R, W, P) -> + {ok, R, W, P}. + +%% ------------------------------------------------------------ +%% Determine if EPMD module supports address resolving. Default +%% is to use inet_tcp:getaddr/2. +%% ------------------------------------------------------------ +get_address_resolver(EpmdModule, _Driver) -> + case erlang:function_exported(EpmdModule, address_please, 3) of + true -> {EpmdModule, address_please}; + _ -> {erl_epmd, address_please} + end. + + +%% If Node is illegal terminate the connection setup!! +split_node(Driver, Node, LongOrShortNames) -> + case dist_util:split_node(Node) of + {node, Name, Host} -> + check_node(Driver, Node, Name, Host, LongOrShortNames); + {host, _} -> + error_logger:error_msg( + "** Nodename ~p illegal, no '@' character **~n", + [Node]), + ?shutdown2(Node, trace({illegal_node_n@me, Node})); + _ -> + error_logger:error_msg( + "** Nodename ~p illegal **~n", [Node]), + ?shutdown2(Node, trace({illegal_node_name, Node})) + end. + +check_node(Driver, Node, Name, Host, LongOrShortNames) -> + case string:split(Host, ".", all) of + [_] when LongOrShortNames =:= longnames -> + case Driver:parse_address(Host) of + {ok, _} -> + {Name, Host}; + _ -> + error_logger:error_msg( + "** System running to use " + "fully qualified hostnames **~n" + "** Hostname ~s is illegal **~n", + [Host]), + ?shutdown2(Node, trace({not_longnames, Host})) + end; + [_, _|_] when LongOrShortNames =:= shortnames -> + error_logger:error_msg( + "** System NOT running to use " + "fully qualified hostnames **~n" + "** Hostname ~s is illegal **~n", + [Host]), + ?shutdown2(Node, trace({not_shortnames, Host})); + _ -> + {Name, Host} + end. + +%% ------------------------------------------------------------------------- + +connect_options(Opts) -> + case application:get_env(kernel, inet_dist_connect_options) of + {ok, ConnectOpts} -> + Opts ++ setopts_filter(ConnectOpts); + _ -> + Opts + end. + +%% we may not always want the nodelay behaviour +%% for performance reasons +nodelay() -> + case application:get_env(kernel, dist_nodelay) of + undefined -> + {nodelay, true}; + {ok, true} -> + {nodelay, true}; + {ok, false} -> + {nodelay, false}; + _ -> + {nodelay, true} + end. + +config() -> + case init:get_argument(?DIST_NAME) of + error -> + error({missing_argument, ?DIST_NAME}); + {ok, [[String]]} -> + {ok, Tokens, _} = erl_scan:string(String ++ "."), + case erl_parse:parse_term(Tokens) of + {ok, #{secret := Secret} = Config} + when is_binary(Secret); is_list(Secret) -> + Config; + {ok, #{} = Config} -> + error({missing_secret, [{?DIST_NAME,Config}]}); + _ -> + error({bad_argument_value, [{?DIST_NAME,String}]}) + end; + {ok, Value} -> + error({malformed_argument, [{?DIST_NAME,Value}]}) + end. + +%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% The DistCtrl process(es). +%% +%% At net_kernel handshake_complete spawns off the input controller that +%% takes over the socket ownership, and itself becomes the output controller +%% +%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%% XXX Missing to "productified": +%%% * Cryptoanalysis by experts +%%% * Proof of usefulness +%%% * Unifying exit reasons using a death_row() function +%%% * Verification (and rejection) of other end's crypto parameters +%%% * OTP:ification (proc_lib?) +%%% * An application to belong to (crypto|kernel?) +%%% * Secret on file (cookie as default?), parameter handling +%%% * Restart and/or code reload policy + +%% Debug client and server + +test_config() -> + #{secret => <<"Secret Cluster Password 123456">>}. + +test_server() -> + {ok, Listen} = gen_tcp:listen(0, [{packet, 2}, {active, false}, binary]), + {ok, Port} = inet:port(Listen), + io:format(?MODULE_STRING":test_client(~w).~n", [Port]), + {ok, Socket} = gen_tcp:accept(Listen), + test(Socket). + +test_client(Port) -> + {ok, Socket} = + gen_tcp:connect( + localhost, Port, [{packet, 2}, {active, false}, binary]), + test(Socket). + +test(Socket) -> + start_dist_ctrl(Socket, test_config(), 10000). + +%% ------------------------------------------------------------------------- + +start_dist_ctrl(Socket, Config, Timeout) -> + Protocol = ?PROTOCOL, + Controller = self(), + Server = + monitor_dist_proc( + spawn_opt( + fun () -> + receive + {?MODULE, From, start} -> + {SendParams, RecvParams} = + init(Socket, Config, Protocol), + reply(From, self()), + handshake(SendParams, 0, RecvParams, 0, Controller) + end + end, + [link, + {priority, max}, + {message_queue_data, off_heap}, + {fullsweep_after, 0}])), + ok = gen_tcp:controlling_process(Socket, Server), + call_dist_ctrl(Server, start, Timeout). + + +call_dist_ctrl(Server, Msg) -> + call_dist_ctrl(Server, Msg, infinity). +%% +call_dist_ctrl(Server, Msg, Timeout) -> + Ref = erlang:monitor(process, Server), + Server ! {?MODULE, {Ref, self()}, Msg}, + receive + {Ref, Res} -> + erlang:demonitor(Ref, [flush]), + Res; + {'DOWN', Ref, process, Server, Reason} -> + exit({?PROTOCOL, Reason}) + after Timeout -> + exit(Server, timeout), + receive + {'DOWN', Ref, process, Server, _} -> + exit({?PROTOCOL, timeout}) + end + end. + +reply({Ref, Pid}, Msg) -> + Pid ! {Ref, Msg}, + ok. + +%% ------------------------------------------------------------------------- + +-record(params, + {protocol, % Encryption protocol tag + socket, + dist_handle, + hash_algorithm, + block_crypto, + rekey_interval, + iv, + key, + tag_len}). + +-define(TCP_ACTIVE, 64). +-define(CHUNK_SIZE, (65536 - 512)). +%% The start chunk starts with zeros, so it seems logical to not have +%% a chunk type with value 0 +-define(HANDSHAKE_CHUNK, 1). +-define(DATA_CHUNK, 2). +-define(TICK_CHUNK, 3). +-define(REKEY_CHUNK, 4). + +%% ------------------------------------------------------------------------- +%% Crypto strategy +%% ------- +%% The crypto strategy is as simple as possible to get an encrypted +%% connection as benchmark reference. It is geared around AEAD +%% ciphers in particular AES-GCM. +%% +%% The init message and the start message must fit in the TCP buffers +%% since both sides start with sending the init message, waits +%% for the other end's init message, sends the start message +%% and waits for the other end's start message. So if the send +%% blocks we have a deadlock. +%% +%% The init message is unencrypted and contains the block cipher and hash +%% algorithms the sender will use, the IV and a key salt. Both sides' +%% key salt is used with the mutual secret as input to the hash algorithm +%% to create different encryption/decryption keys for both directions. +%% +%% The start message is the first encrypted message and contains just +%% encrypted zeros the width of the key, with the header of the init +%% message as AAD data. Successfully decrypting this message +%% verifies that we have an encrypted channel. +%% +%% Subsequent encrypted messages has the sequence number and the length +%% of the message as AAD data. These messages has got a message type +%% differentiating data from ticks. Ticks have a random size in an +%% attempt to make them less obvious to spot. +%% +%% The only reaction to errors is to crash noisily wich will bring +%% down the connection and hopefully produce something useful +%% in the local log, but all the other end sees is a closed connection. +%% ------------------------------------------------------------------------- + +init(Socket, Config, Protocol) -> + Secret = maps:get(secret, Config), + HashAlgorithm = + maps:get(hash_algorithm, Config, ?DEFAULT_HASH_ALGORITHM), + BlockCrypto = + maps:get(block_crypto, Config, ?DEFAULT_BLOCK_CRYPTO), + RekeyInterval = + maps:get(rekey_interval, Config, ?DEFAULT_REKEY_INTERVAL), + %% + SendParams = + init_params( + Socket, Protocol, HashAlgorithm, BlockCrypto, RekeyInterval), + send_init(SendParams, Secret). + +send_init( + #params{ + protocol = Protocol, + socket = Socket, + block_crypto = BlockCrypto, + iv = IVLen, + key = KeyLen, + hash_algorithm = HashAlgorithm} = SendParams, + Secret) -> + %% + ProtocolString = atom_to_binary(Protocol, utf8), + BlockCryptoString = atom_to_binary(BlockCrypto, utf8), + HashAlgorithmString = atom_to_binary(HashAlgorithm, utf8), + SendHeader = + <<ProtocolString/binary, 0, + HashAlgorithmString/binary, 0, + BlockCryptoString/binary, 0>>, + <<IV:IVLen/binary, KeySalt:KeyLen/binary>> = IV_KeySalt = + crypto:strong_rand_bytes(IVLen + KeyLen), + InitPacket = [SendHeader, IV_KeySalt], + ok = gen_tcp:send(Socket, InitPacket), + recv_init(SendParams#params{iv = IV, key = KeySalt}, Secret, SendHeader). + +recv_init( + #params{ + socket = Socket, + hash_algorithm = SendHashAlgorithm, + key = SendKeySalt} = SendParams, Secret, SendHeader) -> + %% + {ok, InitPacket} = gen_tcp:recv(Socket, 0), + [ProtocolString, Rest_1] = binary:split(InitPacket, <<0>>), + Protocol = binary_to_existing_atom(ProtocolString, utf8), + case Protocol of + ?PROTOCOL -> + [HashAlgorithmString, Rest_2] = binary:split(Rest_1, <<0>>), + HashAlgorithm = binary_to_existing_atom(HashAlgorithmString, utf8), + [BlockCryptoString, Rest_3] = binary:split(Rest_2, <<0>>), + BlockCrypto = binary_to_existing_atom(BlockCryptoString, utf8), + #params{ + hash_algorithm = RecvHashAlgorithm, + iv = RecvIVLen, + key = RecvKeyLen} = RecvParams = + init_params( + Socket, Protocol, HashAlgorithm, BlockCrypto, undefined), + <<RecvIV:RecvIVLen/binary, + RecvKeySalt:RecvKeyLen/binary>> = Rest_3, + SendKey = + hash_key(SendHashAlgorithm, SendKeySalt, [RecvKeySalt, Secret]), + RecvKey = + hash_key(RecvHashAlgorithm, RecvKeySalt, [SendKeySalt, Secret]), + SendParams_1 = SendParams#params{key = SendKey}, + RecvParams_1 = RecvParams#params{iv = RecvIV, key = RecvKey}, + RecvHeaderLen = byte_size(InitPacket) - RecvIVLen - RecvKeyLen, + <<RecvHeader:RecvHeaderLen/binary, _/binary>> = InitPacket, + send_start(SendParams_1, SendHeader), + RecvRekeyInterval = recv_start(RecvParams_1, RecvHeader), + {SendParams_1, + RecvParams_1#params{rekey_interval = RecvRekeyInterval}} + end. + +send_start( + #params{ + socket = Socket, + block_crypto = BlockCrypto, + rekey_interval= RekeyInterval, + iv = IV, + key = Key, + tag_len = TagLen}, AAD) -> + %% + KeyLen = byte_size(Key), + Zeros = binary:copy(<<0>>, KeyLen), + {Ciphertext, CipherTag} = + crypto:block_encrypt( + crypto_cipher_name(BlockCrypto), + Key, IV, {AAD, [Zeros, <<RekeyInterval:32>>], TagLen}), + ok = gen_tcp:send(Socket, [Ciphertext, CipherTag]). + +recv_start( + #params{ + socket = Socket, + block_crypto = BlockCrypto, + iv = IV, + key = Key, + tag_len = TagLen}, AAD) -> + {ok, Packet} = gen_tcp:recv(Socket, 0), + KeyLen = byte_size(Key), + PacketLen = KeyLen + 4, + <<Ciphertext:PacketLen/binary, CipherTag:TagLen/binary>> = Packet, + Zeros = binary:copy(<<0>>, KeyLen), + case + crypto:block_decrypt( + crypto_cipher_name(BlockCrypto), + Key, IV, {AAD, Ciphertext, CipherTag}) + of + <<Zeros:KeyLen/binary, RekeyInterval:32>> + when 1 =< RekeyInterval -> + RekeyInterval; + _ -> + error(decrypt_error) + end. + +init_params(Socket, Protocol, HashAlgorithm, BlockCrypto, RekeyInterval) -> + #{block_size := 1, + iv_length := IVLen, + key_length := KeyLen} = crypto:cipher_info(BlockCrypto), + case crypto:hash_info(HashAlgorithm) of + #{size := HashSize} when HashSize >= KeyLen -> + #params{ + socket = Socket, + protocol = Protocol, + hash_algorithm = HashAlgorithm, + block_crypto = BlockCrypto, + rekey_interval = RekeyInterval, + iv = IVLen, + key = KeyLen, + tag_len = 16} + end. + +crypto_cipher_name(BlockCrypto) -> + case BlockCrypto of + aes_128_gcm -> aes_gcm; + aes_192_gcm -> aes_gcm; + aes_256_gcm -> aes_gcm + end. + +hash_key(HashAlgorithm, KeySalt, OtherSalt) -> + KeyLen = byte_size(KeySalt), + <<Key:KeyLen/binary, _/binary>> = + crypto:hash(HashAlgorithm, [KeySalt, OtherSalt]), + Key. + +%% ------------------------------------------------------------------------- +%% net_kernel distribution handshake in progress +%% + +handshake( + SendParams, SendSeq, + #params{socket = Socket} = RecvParams, RecvSeq, Controller) -> + receive + {?MODULE, From, {controller, Controller_1, Parent}} -> + Result = link(Controller_1), + true = unlink(Parent), + reply(From, Result), + handshake(SendParams, SendSeq, RecvParams, RecvSeq, Controller_1); + {?MODULE, From, {handshake_complete, DistHandle}} -> + reply(From, ok), + InputHandler = + monitor_dist_proc( + spawn_opt( + fun () -> + link(Controller), + receive + DistHandle -> + ok = + inet:setopts( + Socket, + [{active, ?TCP_ACTIVE}, + nodelay()]), + input_handler( + RecvParams#params{ + dist_handle = DistHandle}, + RecvSeq, empty_q(), infinity) + end + end, + [link, + {priority, normal}, + {message_queue_data, off_heap}, + {fullsweep_after, 0}])), + _ = monitor(process, InputHandler), % For the benchmark test + ok = gen_tcp:controlling_process(Socket, InputHandler), + ok = erlang:dist_ctrl_input_handler(DistHandle, InputHandler), + InputHandler ! DistHandle, + process_flag(priority, normal), + erlang:dist_ctrl_get_data_notification(DistHandle), + crypto:rand_seed_alg(crypto_cache), + output_handler( + SendParams#params{dist_handle = DistHandle}, SendSeq); + %% + {?MODULE, From, {send, Data}} -> + {SendParams_1, SendSeq_1} = + encrypt_and_send_chunk( + SendParams, SendSeq, [?HANDSHAKE_CHUNK, Data]), + reply(From, ok), + handshake( + SendParams_1, SendSeq_1, RecvParams, RecvSeq, Controller); + {?MODULE, From, recv} -> + {RecvParams_1, RecvSeq_1, Reply} = + recv_and_decrypt_chunk(RecvParams, RecvSeq), + reply(From, Reply), + handshake( + SendParams, SendSeq, RecvParams_1, RecvSeq_1, Controller); + {?MODULE, From, peername} -> + reply(From, inet:peername(Socket)), + handshake(SendParams, SendSeq, RecvParams, RecvSeq, Controller); + %% + _Alien -> + handshake(SendParams, SendSeq, RecvParams, RecvSeq, Controller) + end. + +recv_and_decrypt_chunk(#params{socket = Socket} = RecvParams, RecvSeq) -> + case gen_tcp:recv(Socket, 0) of + {ok, Chunk} -> + case decrypt_chunk(RecvParams, RecvSeq, Chunk) of + <<?HANDSHAKE_CHUNK, Cleartext/binary>> -> + {RecvParams, RecvSeq + 1, {ok, Cleartext}}; + #params{} = RecvParams_1 -> + recv_and_decrypt_chunk(RecvParams_1, 0); + _ -> + error(decrypt_error) + end; + Error -> + {RecvParams, RecvSeq, Error} + end. + +%% ------------------------------------------------------------------------- +%% Output handler process +%% +%% The game here is to flush all dist_data and dist_tick messages, +%% prioritize dist_data over dist_tick, and to not use selective receive + +output_handler(Params, Seq) -> + receive + Msg -> + case Msg of + dist_data -> + output_handler_data(Params, Seq); + dist_tick -> + output_handler_tick(Params, Seq); + _Other -> + %% Ignore + output_handler(Params, Seq) + end + end. + +output_handler_data(Params, Seq) -> + receive + Msg -> + case Msg of + dist_data -> + output_handler_data(Params, Seq); + dist_tick -> + output_handler_data(Params, Seq); + _Other -> + %% Ignore + output_handler_data(Params, Seq) + end + after 0 -> + DistHandle = Params#params.dist_handle, + Q = get_data(DistHandle, empty_q()), + {Params_1, Seq_1} = output_handler_send(Params, Seq, Q, true), + erlang:dist_ctrl_get_data_notification(DistHandle), + output_handler(Params_1, Seq_1) + end. + +output_handler_tick(Params, Seq) -> + receive + Msg -> + case Msg of + dist_data -> + output_handler_data(Params, Seq); + dist_tick -> + output_handler_tick(Params, Seq); + _Other -> + %% Ignore + output_handler_tick(Params, Seq) + end + after 0 -> + TickSize = 8 + rand:uniform(56), + TickData = binary:copy(<<0>>, TickSize), + {Params_1, Seq_1} = + encrypt_and_send_chunk(Params, Seq, [?TICK_CHUNK, TickData]), + output_handler(Params_1, Seq_1) + end. + +output_handler_send( + #params{dist_handle = DistHandle} = Params, Seq, {_, Size, _} = Q, Retry) -> + %% + if + ?CHUNK_SIZE < Size -> + {Cleartext, Q_1} = deq_iovec(?CHUNK_SIZE, Q), + {Params_1, Seq_1} = + encrypt_and_send_chunk(Params, Seq, [?DATA_CHUNK, Cleartext]), + output_handler_send(Params_1, Seq_1, Q_1, Retry); + Retry -> + Q_1 = get_data(DistHandle, Q), + output_handler_send(Params, Seq, Q_1, false); + true -> + {Cleartext, _} = deq_iovec(Size, Q), + encrypt_and_send_chunk(Params, Seq, [?DATA_CHUNK, Cleartext]) + end. + +%% ------------------------------------------------------------------------- +%% Input handler process +%% +%% Here is T 0 or infinity to steer if we should try to receive +%% more data or not; start with infinity, and when we get some +%% data try with 0 to see if more is waiting + +input_handler(#params{socket = Socket} = Params, Seq, Q, T) -> + receive + Msg -> + case Msg of + {tcp_passive, Socket} -> + ok = inet:setopts(Socket, [{active, ?TCP_ACTIVE}]), + Q_1 = + case T of + 0 -> + deliver_data(Params#params.dist_handle, Q); + infinity -> + Q + end, + input_handler(Params, Seq, Q_1, infinity); + {tcp, Socket, Chunk} -> + input_chunk(Params, Seq, Q, Chunk); + {tcp_closed, Socket} -> + error(connection_closed); + _Other -> + %% Ignore... + input_handler(Params, Seq, Q, T) + end + after T -> + Q_1 = deliver_data(Params#params.dist_handle, Q), + input_handler(Params, Seq, Q_1, infinity) + end. + +input_chunk(Params, Seq, Q, Chunk) -> + case decrypt_chunk(Params, Seq, Chunk) of + <<?DATA_CHUNK, Cleartext/binary>> -> + input_handler(Params, Seq + 1, enq_binary(Cleartext, Q), 0); + <<?TICK_CHUNK, _/binary>> -> + input_handler(Params, Seq + 1, Q, 0); + #params{} = Params_1 -> + input_handler(Params_1, 0, Q, 0); + _ -> + error(decrypt_error) + end. + +%% ------------------------------------------------------------------------- +%% erlang:dist_ctrl_* helpers + +%% Get data for sending from the VM and place it in a queue +%% +get_data(DistHandle, {Front, Size, Rear}) -> + get_data(DistHandle, Front, Size, Rear). +%% +get_data(DistHandle, Front, Size, Rear) -> + case erlang:dist_ctrl_get_data(DistHandle) of + none -> + {Front, Size, Rear}; + Bin when is_binary(Bin) -> + Len = byte_size(Bin), + get_data( + DistHandle, Front, Size + 4 + Len, + [Bin, <<Len:32>>|Rear]); + [Bin1, Bin2] -> + Len = byte_size(Bin1) + byte_size(Bin2), + get_data( + DistHandle, Front, Size + 4 + Len, + [Bin2, Bin1, <<Len:32>>|Rear]); + Iovec -> + Len = iolist_size(Iovec), + get_data( + DistHandle, Front, Size + 4 + Len, + lists:reverse(Iovec, [<<Len:32>>|Rear])) + end. + +%% De-packet and deliver received data to the VM from a queue +%% +deliver_data(DistHandle, Q) -> + case Q of + {[], Size, []} -> + Size = 0, % Assert + Q; + {[], Size, Rear} -> + [Bin|Front] = lists:reverse(Rear), + deliver_data(DistHandle, Front, Size, [], Bin); + {[Bin|Front], Size, Rear} -> + deliver_data(DistHandle, Front, Size, Rear, Bin) + end. +%% +deliver_data(DistHandle, Front, Size, Rear, Bin) -> + case Bin of + <<DataSizeA:32, DataA:DataSizeA/binary, + DataSizeB:32, DataB:DataSizeB/binary, Rest/binary>> -> + erlang:dist_ctrl_put_data(DistHandle, DataA), + erlang:dist_ctrl_put_data(DistHandle, DataB), + deliver_data( + DistHandle, + Front, Size - (4 + DataSizeA + 4 + DataSizeB), Rear, + Rest); + <<DataSize:32, Data:DataSize/binary, Rest/binary>> -> + erlang:dist_ctrl_put_data(DistHandle, Data), + deliver_data(DistHandle, Front, Size - (4 + DataSize), Rear, Rest); + <<DataSize:32, FirstData/binary>> -> + TotalSize = 4 + DataSize, + if + TotalSize =< Size -> + BinSize = byte_size(Bin), + {MoreData, Q} = + deq_iovec( + TotalSize - BinSize, + Front, Size - BinSize, Rear), + erlang:dist_ctrl_put_data(DistHandle, [FirstData|MoreData]), + deliver_data(DistHandle, Q); + true -> % Incomplete data + {[Bin|Front], Size, Rear} + end; + <<_/binary>> -> + BinSize = byte_size(Bin), + if + 4 =< Size -> % Fragmented header - extract a header bin + {RestHeader, {Front_1, _Size_1, Rear_1}} = + deq_iovec(4 - BinSize, Front, Size - BinSize, Rear), + Header = iolist_to_binary([Bin|RestHeader]), + deliver_data(DistHandle, Front_1, Size, Rear_1, Header); + true -> % Incomplete header + {[Bin|Front], Size, Rear} + end + end. + +%% ------------------------------------------------------------------------- +%% Encryption and decryption helpers + +encrypt_and_send_chunk( + #params{ + socket = Socket, rekey_interval = Seq, + key = Key, iv = IV, hash_algorithm = HashAlgorithm} = Params, + Seq, Cleartext) -> + %% + KeyLen = byte_size(Key), + IVLen = byte_size(IV), + Chunk = <<IV_1:IVLen/binary, KeySalt:KeyLen/binary>> = + crypto:strong_rand_bytes(IVLen + KeyLen), + ok = gen_tcp:send(Socket, encrypt_chunk(Params, Seq, [?REKEY_CHUNK, Chunk])), + Key_1 = hash_key(HashAlgorithm, Key, KeySalt), + Params_1 = Params#params{key = Key_1, iv = IV_1}, + ok = gen_tcp:send(Socket, encrypt_chunk(Params_1, 0, Cleartext)), + {Params_1, 1}; +encrypt_and_send_chunk(#params{socket = Socket} = Params, Seq, Cleartext) -> + ok = gen_tcp:send(Socket, encrypt_chunk(Params, Seq, Cleartext)), + {Params, Seq + 1}. + +encrypt_chunk( + #params{ + block_crypto = BlockCrypto, + iv = IV, key = Key, tag_len = TagLen}, Seq, Cleartext) -> + %% + ChunkLen = iolist_size(Cleartext) + TagLen, + AAD = <<Seq:32, ChunkLen:32>>, + {Ciphertext, CipherTag} = + crypto:block_encrypt( + crypto_cipher_name(BlockCrypto), Key, IV, {AAD, Cleartext, TagLen}), + Chunk = [Ciphertext,CipherTag], + Chunk. + +decrypt_chunk( + #params{ + block_crypto = BlockCrypto, + iv = IV, key = Key, tag_len = TagLen} = Params, Seq, Chunk) -> + %% + ChunkLen = byte_size(Chunk), + true = TagLen =< ChunkLen, % Assert + AAD = <<Seq:32, ChunkLen:32>>, + CiphertextLen = ChunkLen - TagLen, + <<Ciphertext:CiphertextLen/binary, CipherTag:TagLen/binary>> = Chunk, + block_decrypt( + Params, Seq, crypto_cipher_name(BlockCrypto), + Key, IV, {AAD, Ciphertext, CipherTag}). + +block_decrypt( + #params{rekey_interval = Seq} = Params, Seq, CipherName, Key, IV, Data) -> + %% + KeyLen = byte_size(Key), + IVLen = byte_size(IV), + case crypto:block_decrypt(CipherName, Key, IV, Data) of + <<?REKEY_CHUNK, IV_1:IVLen/binary, KeySalt:KeyLen/binary>> -> + Key_1 = hash_key(Params#params.hash_algorithm, Key, KeySalt), + Params#params{iv = IV_1, key = Key_1}; + _ -> + error(decrypt_error) + end; +block_decrypt(_Params, _Seq, CipherName, Key, IV, Data) -> + crypto:block_decrypt(CipherName, Key, IV, Data). + +%% ------------------------------------------------------------------------- +%% Queue of binaries i.e an iovec queue + +empty_q() -> + {[], 0, []}. + +enq_binary(Bin, {Front, Size, Rear}) -> + {Front, Size + byte_size(Bin), [Bin|Rear]}. + +deq_iovec(GetSize, {Front, Size, Rear}) when GetSize =< Size -> + deq_iovec(GetSize, Front, Size, Rear, []). +%% +deq_iovec(GetSize, Front, Size, Rear) -> + deq_iovec(GetSize, Front, Size, Rear, []). +%% +deq_iovec(GetSize, [], Size, Rear, Acc) -> + deq_iovec(GetSize, lists:reverse(Rear), Size, [], Acc); +deq_iovec(GetSize, [Bin|Front], Size, Rear, Acc) -> + BinSize = byte_size(Bin), + if + BinSize < GetSize -> + deq_iovec( + GetSize - BinSize, Front, Size - BinSize, Rear, [Bin|Acc]); + GetSize < BinSize -> + {Bin1,Bin2} = erlang:split_binary(Bin, GetSize), + {lists:reverse(Acc, [Bin1]), {[Bin2|Front], Size - GetSize, Rear}}; + true -> + {lists:reverse(Acc, [Bin]), {Front, Size - BinSize, Rear}} + end. + +%% ------------------------------------------------------------------------- + +%% Trace point +trace(Term) -> Term. + +%% Keep an eye on this Pid (debug) +monitor_dist_proc(Pid) -> +%%% spawn( +%%% fun () -> +%%% MRef = erlang:monitor(process, Pid), +%%% receive +%%% {'DOWN', MRef, _, _, normal} -> +%%% error_logger:error_report( +%%% [dist_proc_died, +%%% {reason, normal}, +%%% {pid, Pid}]); +%%% {'DOWN', MRef, _, _, Reason} -> +%%% error_logger:info_report( +%%% [dist_proc_died, +%%% {reason, Reason}, +%%% {pid, Pid}]) +%%% end +%%% end), + Pid. + +dbg() -> + dbg:stop(), + dbg:tracer(), + dbg:p(all, c), + dbg:tpl(?MODULE, cx), + dbg:tpl(erlang, dist_ctrl_get_data_notification, cx), + dbg:tpl(erlang, dist_ctrl_get_data, cx), + dbg:tpl(erlang, dist_ctrl_put_data, cx), + ok. diff --git a/lib/ssl/test/make_certs.erl b/lib/ssl/test/make_certs.erl index 7f3371da9a..76bf0fa895 100644 --- a/lib/ssl/test/make_certs.erl +++ b/lib/ssl/test/make_certs.erl @@ -377,7 +377,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" @@ -427,7 +427,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" @@ -511,7 +511,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 new file mode 100644 index 0000000000..38a4b7fb11 --- /dev/null +++ b/lib/ssl/test/property_test/ssl_eqc_handshake.erl @@ -0,0 +1,812 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2018-2018. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% + +-module(ssl_eqc_handshake). + +-compile(export_all). + +-proptest(eqc). +-proptest([triq,proper]). + +-ifndef(EQC). +-ifndef(PROPER). +-ifndef(TRIQ). +-define(EQC,true). +-endif. +-endif. +-endif. + +-ifdef(EQC). +-include_lib("eqc/include/eqc.hrl"). +-define(MOD_eqc,eqc). + +-else. +-ifdef(PROPER). +-include_lib("proper/include/proper.hrl"). +-define(MOD_eqc,proper). + +-else. +-ifdef(TRIQ). +-define(MOD_eqc,triq). +-include_lib("triq/include/triq.hrl"). + +-endif. +-endif. +-endif. + +-include_lib("kernel/include/inet.hrl"). +-include_lib("ssl/src/tls_handshake_1_3.hrl"). +-include_lib("ssl/src/tls_handshake.hrl"). +-include_lib("ssl/src/ssl_handshake.hrl"). +-include_lib("ssl/src/ssl_alert.hrl"). +-include_lib("ssl/src/ssl_internal.hrl"). + +-define('TLS_v1.3', {3,4}). +-define('TLS_v1.2', {3,3}). +-define('TLS_v1.1', {3,2}). +-define('TLS_v1', {3,1}). +-define('SSL_v3', {3,0}). + +%%-------------------------------------------------------------------- +%% Properties -------------------------------------------------------- +%%-------------------------------------------------------------------- + +prop_tls_hs_encode_decode() -> + ?FORALL({Handshake, TLSVersion}, ?LET(Version, tls_version(), {tls_msg(Version), Version}), + try + [Type, _Length, Data] = tls_handshake:encode_handshake(Handshake, TLSVersion), + case tls_handshake:decode_handshake(TLSVersion, Type, Data) of + Handshake -> + true; + _ -> + false + end + catch + throw:#alert{} -> + true + end + ). + +%%-------------------------------------------------------------------- +%% Message Generators ----------------------------------------------- +%%-------------------------------------------------------------------- + +tls_msg(?'TLS_v1.3'= Version) -> + oneof([client_hello(Version), + server_hello(Version), + %%new_session_ticket() + #end_of_early_data{}, + encrypted_extensions(), + certificate_1_3(), + %%certificate_request_1_3, + certificate_verify_1_3(), + finished(), + key_update() + ]); +tls_msg(Version) -> + oneof([ + #hello_request{}, + client_hello(Version), + server_hello(Version), + certificate(), + %%server_key_exchange() + certificate_request(Version), + #server_hello_done{}, + %%certificate_verify() + %%client_key_exchange() + finished() + ]). + +%% +%% Shared messages +%% +client_hello(?'TLS_v1.3' = Version) -> + #client_hello{session_id = session_id(), + client_version = ?'TLS_v1.2', + cipher_suites = cipher_suites(Version), + compression_methods = compressions(Version), + random = client_random(Version), + extensions = client_hello_extensions(Version) + }; +client_hello(Version) -> + #client_hello{session_id = session_id(), + client_version = Version, + cipher_suites = cipher_suites(Version), + compression_methods = compressions(Version), + random = client_random(Version), + extensions = client_hello_extensions(Version) + }. + +server_hello(?'TLS_v1.3' = Version) -> + #server_hello{server_version = ?'TLS_v1.2', + session_id = session_id(), + random = server_random(Version), + cipher_suite = cipher_suite(Version), + compression_method = compression(Version), + extensions = server_hello_extensions(Version) + }; +server_hello(Version) -> + #server_hello{server_version = Version, + session_id = session_id(), + random = server_random(Version), + cipher_suite = cipher_suite(Version), + compression_method = compression(Version), + extensions = server_hello_extensions(Version) + }. + +certificate() -> + #certificate{ + asn1_certificates = certificate_chain() + }. + +certificate_1_3() -> + ?LET(Certs, certificate_chain(), + #certificate_1_3{ + certificate_request_context = certificate_request_context(), + certificate_list = certificate_entries(Certs, []) + }). + +certificate_verify_1_3() -> + ?LET(Certs, certificate_chain(), + #certificate_verify_1_3{ + algorithm = sig_scheme(), + signature = signature() + }). + +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)). + +cipher_suites(Version) -> + ssl_cipher:suites(Version). + +session_id() -> + crypto:strong_rand_bytes(?NUM_OF_SESSION_ID_BYTES). + +compression(Version) -> + oneof(compressions(Version)). + +compressions(_) -> + ssl_record:compressions(). + +client_random(_) -> + crypto:strong_rand_bytes(32). + +server_random(_) -> + crypto:strong_rand_bytes(32). + + +client_hello_extensions(Version) -> + ?LET(Exts, extensions(Version, client_hello), + maps:merge(ssl_handshake:empty_extensions(Version, client_hello), + Exts)). + +server_hello_extensions(Version) -> + ?LET(Exts, extensions(Version, server_hello), + maps:merge(ssl_handshake:empty_extensions(Version, server_hello), + Exts)). + +key_share_client_hello() -> + oneof([undefined]). + %%oneof([#key_share_client_hello{}, undefined]). + +key_share_server_hello() -> + oneof([undefined]). + %%oneof([#key_share_server_hello{}, undefined]). + +pre_shared_keyextension() -> + oneof([undefined]). + %%oneof([#pre_shared_keyextension{},undefined]). + +%% +--------------------------------------------------+-------------+ +%% | 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(client_hello), undefined]), + %% oneof([pre_shared_key(), undefined]), + %% oneof([psk_key_exchange_modes(), undefined]), + %% oneof([early_data(), undefined]), + %% oneof([cookie(), undefined]), + oneof([client_hello_versions(Version)]), + %% 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, 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([renegotiation_info(), undefined]) + }, + maps:filter(fun(_, undefined) -> + false; + (_,_) -> + true + end, + #{ + sni => SNI, + ec_point_formats => ECPoitF, + elliptic_curves => ECCurves, + alpn => ALPN, + next_protocol_negotiation => NextP, + srp => SRP + %% renegotiation_info => RenegotiationInfo + })); +extensions(?'TLS_v1.3' = Version, server_hello) -> + ?LET({ + KeyShare, + %% PreSharedKeys, + SupportedVersions + }, + { + oneof([key_share(server_hello), undefined]), + %% oneof([pre_shared_keys(), undefined]), + oneof([server_hello_selected_version()]) + }, + 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(List, sig_scheme_list(), + #signature_algorithms_cert{signature_scheme_list = List}). + +signature_algorithms() -> + ?LET(List, sig_scheme_list(), + #signature_algorithms{signature_scheme_list = List}). + +sig_scheme_list() -> + oneof([[rsa_pkcs1_sha256], + [rsa_pkcs1_sha256, ecdsa_sha1], + [rsa_pkcs1_sha256, + rsa_pkcs1_sha384, + rsa_pkcs1_sha512, + ecdsa_secp256r1_sha256, + ecdsa_secp384r1_sha384, + ecdsa_secp521r1_sha512, + rsa_pss_rsae_sha256, + rsa_pss_rsae_sha384, + rsa_pss_rsae_sha512, + rsa_pss_pss_sha256, + rsa_pss_pss_sha384, + rsa_pss_pss_sha512, + rsa_pkcs1_sha1, + ecdsa_sha1] + ]). + +sig_scheme() -> + oneof([rsa_pkcs1_sha256, + rsa_pkcs1_sha384, + rsa_pkcs1_sha512, + ecdsa_secp256r1_sha256, + ecdsa_secp384r1_sha384, + ecdsa_secp521r1_sha512, + rsa_pss_rsae_sha256, + rsa_pss_rsae_sha384, + rsa_pss_rsae_sha512, + rsa_pss_pss_sha256, + rsa_pss_pss_sha384, + rsa_pss_pss_sha512, + rsa_pkcs1_sha1, + ecdsa_sha1]). + +signature() -> + <<44,119,215,137,54,84,156,26,121,212,64,173,189,226, + 191,46,76,89,204,2,78,79,163,228,90,21,89,179,4,198, + 109,14,52,26,230,22,56,8,170,129,86,0,7,132,245,81, + 181,131,62,70,79,167,112,85,14,171,175,162,110,29, + 212,198,45,188,83,176,251,197,224,104,95,74,89,59, + 26,60,63,79,238,196,137,65,23,199,127,145,176,184, + 216,3,48,116,172,106,97,83,227,172,246,137,91,79, + 173,119,169,60,67,1,177,117,9,93,38,86,232,253,73, + 140,17,147,130,110,136,245,73,10,91,70,105,53,225, + 158,107,60,190,30,14,26,92,147,221,60,117,104,53,70, + 142,204,7,131,11,183,192,120,246,243,68,99,147,183, + 49,149,48,188,8,218,17,150,220,121,2,99,194,140,35, + 13,249,201,37,216,68,45,87,58,18,10,106,11,132,241, + 71,170,225,216,197,212,29,107,36,80,189,184,202,56, + 86,213,45,70,34,74,71,48,137,79,212,194,172,151,57, + 57,30,126,24,157,198,101,220,84,162,89,105,185,245, + 76,105,212,176,25,6,148,49,194,106,253,241,212,200, + 37,154,227,53,49,216,72,82,163>>. + +client_hello_versions(?'TLS_v1.3') -> + ?LET(SupportedVersions, + oneof([[{3,4}], + %% This list breaks the property but can be used for negative tests + %% [{3,3},{3,4}], + [{3,4},{3,3}], + [{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]). + +certificate_chain()-> + Conf = cert_conf(), + ?LET(Chain, + choose_certificate_chain(Conf), + Chain). + +choose_certificate_chain(#{server_config := ServerConf, + client_config := ClientConf}) -> + oneof([certificate_chain(ServerConf), certificate_chain(ClientConf)]). + +certificate_request_context() -> + oneof([<<>>, + <<1>>, + <<"foobar">> + ]). +certificate_entries([], Acc) -> + lists:reverse(Acc); +certificate_entries([Cert | Rest], Acc) -> + certificate_entries(Rest, [certificate_entry(Cert) | Acc]). + +certificate_entry(Cert) -> + #certificate_entry{data = Cert, + extensions = certificate_entry_extensions() + }. +certificate_entry_extensions() -> + #{}. + +certificate_chain(Conf) -> + CAs = proplists:get_value(cacerts, Conf), + Cert = proplists:get_value(cert, Conf), + %% Middle argument are of correct type but will not be used + {ok, _, Chain} = ssl_certificate:certificate_chain(Cert, ets:new(foo, []), make_ref(), CAs), + Chain. + +cert_conf()-> + Hostname = net_adm:localhost(), + {ok, #hostent{h_addr_list = [_IP |_]}} = inet:gethostbyname(net_adm:localhost()), + public_key:pkix_test_data(#{server_chain => + #{root => [{key, ssl_test_lib:hardcode_rsa_key(1)}], + intermediates => [[{key, ssl_test_lib:hardcode_rsa_key(2)}]], + peer => [{extensions, [#'Extension'{extnID = + ?'id-ce-subjectAltName', + extnValue = [{dNSName, Hostname}], + critical = false}]}, + {key, ssl_test_lib:hardcode_rsa_key(3)} + ]}, + client_chain => + #{root => [{key, ssl_test_lib:hardcode_rsa_key(4)}], + intermediates => [[{key, ssl_test_lib:hardcode_rsa_key(5)}]], + peer => [{key, ssl_test_lib:hardcode_rsa_key(6)}]}}). + +certificate_request(Version) -> + #certificate_request{certificate_types = certificate_types(Version), + hashsign_algorithms = hashsign_algorithms(Version), + certificate_authorities = certificate_authorities()}. + +certificate_types(?'TLS_v1.3') -> + iolist_to_binary([<<?BYTE(?ECDSA_SIGN)>>, <<?BYTE(?RSA_SIGN)>>]); +certificate_types(?'TLS_v1.2') -> + iolist_to_binary([<<?BYTE(?ECDSA_SIGN)>>, <<?BYTE(?RSA_SIGN)>>, <<?BYTE(?DSS_SIGN)>>]); +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(1,15), + ?LET(List, [hash_alg(Version) || _ <- lists:seq(1,NumOf)], + lists:usort(List) + )). + +hash_alg(Version) -> + ?LET(Alg, sign_algorithm(Version), + {hash_algorithm(Version, Alg), Alg} + ). + +hash_algorithm(?'TLS_v1.3', _) -> + oneof([sha, sha224, sha256, sha384, sha512]); +hash_algorithm(?'TLS_v1.2', rsa) -> + oneof([sha, sha224, sha256, sha384, sha512]); +hash_algorithm(_, rsa) -> + oneof([md5, sha, sha224, sha256, sha384, sha512]); +hash_algorithm(_, ecdsa) -> + oneof([sha, sha224, sha256, sha384, sha512]); +hash_algorithm(_, dsa) -> + sha. + +sign_algorithm(?'TLS_v1.3') -> + oneof([rsa, ecdsa]); +sign_algorithm(_) -> + oneof([rsa, dsa, ecdsa]). + +certificate_authorities() -> + #{server_config := ServerConf} = cert_conf(), + Authorities = proplists:get_value(cacerts, ServerConf), + Enc = fun(#'OTPCertificate'{tbsCertificate=TBSCert}) -> + OTPSubj = TBSCert#'OTPTBSCertificate'.subject, + DNEncodedBin = public_key:pkix_encode('Name', OTPSubj, otp), + DNEncodedLen = byte_size(DNEncodedBin), + <<?UINT16(DNEncodedLen), DNEncodedBin/binary>> + end, + list_to_binary([Enc(public_key:pkix_decode_cert(DERCert, otp)) || DERCert <- Authorities]). + +digest_size()-> + oneof([160,224,256,384,512]). + +key_share_entry() -> + undefined. + %%#key_share_entry{}. + +server_hello_selected_version(Version) -> + #server_hello_selected_version{selected_version = Version}. + +sni() -> + #sni{hostname = net_adm:localhost()}. + +ec_point_formats() -> + #ec_point_formats{ec_point_format_list = ec_point_format_list()}. + +ec_point_format_list() -> + [?ECPOINT_UNCOMPRESSED]. + +elliptic_curves({_, Minor}) when Minor < 4 -> + Curves = tls_v1:ecc_curves(Minor), + #elliptic_curves{elliptic_curve_list = Curves}. + +%% 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}). + +alpn_protocols() -> + oneof([<<"spdy/2">>, <<"spdy/3">>, <<"http/2">>, <<"http/1.0">>, <<"http/1.1">>]). + +next_protocol_negotiation() -> + %% Predecessor to APLN + ?LET(ExtD, alpn_protocols(), #next_protocol_negotiation{extension_data = ExtD}). + +srp() -> + ?LET(Name, gen_name(), #srp{username = list_to_binary(Name)}). + +renegotiation_info() -> + #renegotiation_info{renegotiated_connection = 0}. + +gen_name() -> + ?LET(Size, choose(1,10), gen_string(Size)). + +gen_char() -> + choose($a,$z). + +gen_string(N) -> + gen_string(N, []). + +gen_string(0, Acc) -> + Acc; +gen_string(N, Acc) -> + ?LET(Char, gen_char(), gen_string(N-1, [Char | Acc])). + +key_share(client_hello) -> + ?LET(ClientShares, key_share_entry_list(), + #key_share_client_hello{ + client_shares = ClientShares}); +key_share(server_hello) -> + ?LET([ServerShare], key_share_entry_list(1), + #key_share_server_hello{ + server_share = ServerShare}). + +key_share_entry_list() -> + Max = length(ssl:groups()), + ?LET(Size, choose(1,Max), key_share_entry_list(Size)). +%% +key_share_entry_list(N) -> + key_share_entry_list(N, ssl:groups(), []). +%% +key_share_entry_list(0, _Pool, Acc) -> + Acc; +key_share_entry_list(N, Pool, Acc) -> + R = rand:uniform(length(Pool)), + G = lists:nth(R, Pool), + P = generate_public_key(G), + KeyShareEntry = + #key_share_entry{ + group = G, + key_exchange = P}, + key_share_entry_list(N - 1, Pool -- [G], [KeyShareEntry|Acc]). + +%% TODO: fix curve generation +generate_public_key(Group) + when Group =:= secp256r1 orelse + Group =:= secp384r1 orelse + Group =:= secp521r1 orelse + Group =:= x448 orelse + Group =:= x25519 -> + #'ECPrivateKey'{publicKey = PublicKey} = + public_key:generate_key({namedCurve, secp256r1}), + PublicKey; +generate_public_key(Group) -> + {PublicKey, _} = + public_key:generate_key(ssl_dh_groups:dh_params(Group)), + PublicKey. + +groups() -> + Max = length(ssl:groups()), + ?LET(Size, choose(1,Max), group_list(Size)). + +group_list(N) -> + group_list(N, ssl:groups(), []). +%% +group_list(0, _Pool, Acc) -> + Acc; +group_list(N, Pool, Acc) -> + R = rand:uniform(length(Pool)), + G = lists:nth(R, Pool), + group_list(N - 1, Pool -- [G], [G|Acc]). diff --git a/lib/ssl/test/ssl.spec b/lib/ssl/test/ssl.spec index cb54168d36..15587abecd 100644 --- a/lib/ssl/test/ssl.spec +++ b/lib/ssl/test/ssl.spec @@ -1,4 +1,12 @@ -{suites,"../ssl_test",all}. -{skip_suites, "../ssl_test", - [ssl_bench_SUITE, ssl_dist_bench_SUITE], - "Benchmarks run separately"}. +% {merge_tests,false}. +{alias,dir,"../ssl_test"}. + +{suites,dir,all}. +{skip_groups,dir,ssl_bench_SUITE,setup,"Benchmarks run separately"}. +{skip_groups,dir,ssl_bench_SUITE,payload,"Benchmarks run separately"}. +{skip_groups,dir,ssl_bench_SUITE,pem_cache,"Benchmarks run separately"}. +{skip_groups,dir,ssl_dist_bench_SUITE,setup,"Benchmarks run separately"}. +{skip_groups,dir,ssl_dist_bench_SUITE,roundtrip,"Benchmarks run separately"}. +{skip_groups,dir,ssl_dist_bench_SUITE,throughput,"Benchmarks run separately"}. +{skip_groups,dir,ssl_dist_bench_SUITE,sched_utilization,"Benchmarks run separately"}. + diff --git a/lib/ssl/test/ssl_ECC_SUITE.erl b/lib/ssl/test/ssl_ECC_SUITE.erl index ca8d0ec70c..c64358960c 100644 --- a/lib/ssl/test/ssl_ECC_SUITE.erl +++ b/lib/ssl/test/ssl_ECC_SUITE.erl @@ -212,7 +212,7 @@ client_ecdsa_server_ecdsa_with_raw_key(Config) when is_list(Config) -> ecc_default_order(Config) -> Default = ssl_test_lib:default_cert_chain_conf(), - DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(0))), + DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(1))), {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default}, {client_chain, Default}], ecdhe_ecdsa, ecdhe_ecdsa, @@ -227,7 +227,7 @@ ecc_default_order(Config) -> ecc_default_order_custom_curves(Config) -> Default = ssl_test_lib:default_cert_chain_conf(), - DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(0))), + DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(1))), {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default}, {client_chain, Default}], ecdhe_ecdsa, ecdhe_ecdsa, @@ -242,7 +242,7 @@ ecc_default_order_custom_curves(Config) -> ecc_client_order(Config) -> Default = ssl_test_lib:default_cert_chain_conf(), - DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(0))), + DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(1))), {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default}, {client_chain, Default}], ecdhe_ecdsa, ecdhe_ecdsa, @@ -257,7 +257,7 @@ ecc_client_order(Config) -> ecc_client_order_custom_curves(Config) -> Default = ssl_test_lib:default_cert_chain_conf(), - DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(0))), + DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(1))), {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default}, {client_chain, Default}], ecdhe_ecdsa, ecdhe_ecdsa, @@ -282,7 +282,7 @@ ecc_unknown_curve(Config) -> client_ecdh_rsa_server_ecdhe_ecdsa_server_custom(Config) -> Default = ssl_test_lib:default_cert_chain_conf(), - DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(0))), + DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(1))), {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default}, {client_chain, Default}], ecdh_rsa, ecdhe_ecdsa, Config), @@ -296,7 +296,7 @@ client_ecdh_rsa_server_ecdhe_ecdsa_server_custom(Config) -> client_ecdh_rsa_server_ecdhe_rsa_server_custom(Config) -> Default = ssl_test_lib:default_cert_chain_conf(), - DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(0))), + DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(1))), {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default}, {client_chain, Default}], ecdh_rsa, ecdhe_rsa, Config), @@ -311,7 +311,7 @@ client_ecdh_rsa_server_ecdhe_rsa_server_custom(Config) -> client_ecdhe_rsa_server_ecdhe_ecdsa_server_custom(Config) -> Default = ssl_test_lib:default_cert_chain_conf(), - DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(0))), + DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(1))), {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default}, {client_chain, Default}], ecdhe_rsa, ecdhe_ecdsa, Config), @@ -325,7 +325,7 @@ client_ecdhe_rsa_server_ecdhe_ecdsa_server_custom(Config) -> client_ecdhe_rsa_server_ecdhe_rsa_server_custom(Config) -> Default = ssl_test_lib:default_cert_chain_conf(), - DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(0))), + DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(1))), {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default}, {client_chain, Default}], ecdhe_rsa, ecdhe_rsa, Config), @@ -339,7 +339,7 @@ client_ecdhe_rsa_server_ecdhe_rsa_server_custom(Config) -> end. client_ecdhe_rsa_server_ecdh_rsa_server_custom(Config) -> Default = ssl_test_lib:default_cert_chain_conf(), - DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(0))), + DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(1))), Ext = x509_test:extensions([{key_usage, [keyEncipherment]}]), {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, [[], [], [{extensions, Ext}]]}, {client_chain, Default}], @@ -357,7 +357,7 @@ client_ecdhe_rsa_server_ecdh_rsa_server_custom(Config) -> client_ecdhe_ecdsa_server_ecdhe_ecdsa_server_custom(Config) -> Default = ssl_test_lib:default_cert_chain_conf(), - DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(0))), + DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(1))), {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default}, {client_chain, Default}], ecdhe_ecdsa, ecdhe_ecdsa, Config), @@ -371,7 +371,7 @@ client_ecdhe_ecdsa_server_ecdhe_ecdsa_server_custom(Config) -> client_ecdhe_ecdsa_server_ecdhe_rsa_server_custom(Config) -> Default = ssl_test_lib:default_cert_chain_conf(), - DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(0))), + DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(1))), {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default}, {client_chain, Default}], ecdhe_ecdsa, ecdhe_rsa, Config), @@ -385,7 +385,7 @@ client_ecdhe_ecdsa_server_ecdhe_rsa_server_custom(Config) -> client_ecdhe_ecdsa_server_ecdhe_ecdsa_client_custom(Config) -> Default = ssl_test_lib:default_cert_chain_conf(), - DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(0))), + DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(1))), {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default}, {client_chain, Default}], ecdhe_ecdsa, ecdhe_ecdsa, Config), @@ -399,7 +399,7 @@ client_ecdhe_ecdsa_server_ecdhe_ecdsa_client_custom(Config) -> client_ecdhe_rsa_server_ecdhe_ecdsa_client_custom(Config) -> Default = ssl_test_lib:default_cert_chain_conf(), - DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(0))), + DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(1))), {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default}, {client_chain, Default}], ecdhe_rsa, ecdhe_ecdsa, Config), diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index f55d1c8ea7..20d9f28512 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -29,6 +29,7 @@ -include_lib("public_key/include/public_key.hrl"). -include("ssl_api.hrl"). +-include("ssl_cipher.hrl"). -include("ssl_internal.hrl"). -include("ssl_alert.hrl"). -include("ssl_internal.hrl"). @@ -53,7 +54,8 @@ all() -> {group, options_tls}, {group, session}, {group, 'dtlsv1.2'}, - {group, 'dtlsv1'}, + {group, 'dtlsv1'}, + {group, 'tlsv1.3'}, {group, 'tlsv1.2'}, {group, 'tlsv1.1'}, {group, 'tlsv1'}, @@ -67,17 +69,16 @@ groups() -> {options_tls, [], options_tests_tls()}, {'dtlsv1.2', [], all_versions_groups()}, {'dtlsv1', [], all_versions_groups()}, + {'tlsv1.3', [], tls13_test_group()}, {'tlsv1.2', [], all_versions_groups() ++ tls_versions_groups() ++ [conf_signature_algs, no_common_signature_algs]}, {'tlsv1.1', [], all_versions_groups() ++ tls_versions_groups()}, {'tlsv1', [], all_versions_groups() ++ tls_versions_groups() ++ rizzo_tests()}, {'sslv3', [], all_versions_groups() ++ tls_versions_groups() ++ rizzo_tests() ++ [tls_ciphersuite_vs_version]}, {api,[], api_tests()}, {api_tls,[], api_tests_tls()}, - {tls_ciphers,[], tls_cipher_tests()}, {session, [], session_tests()}, {renegotiate, [], renegotiate_tests()}, {ciphers, [], cipher_tests()}, - {ciphers_ec, [], cipher_tests_ec()}, {error_handling_tests, [], error_handling_tests()}, {error_handling_tests_tls, [], error_handling_tests_tls()} ]. @@ -85,14 +86,12 @@ groups() -> tls_versions_groups ()-> [ {group, api_tls}, - {group, tls_ciphers}, {group, error_handling_tests_tls}]. all_versions_groups ()-> [{group, api}, {group, renegotiate}, {group, ciphers}, - {group, ciphers_ec}, {group, error_handling_tests}]. @@ -208,38 +207,11 @@ renegotiate_tests() -> renegotiate_dos_mitigate_passive, renegotiate_dos_mitigate_absolute]. -tls_cipher_tests() -> - [rc4_rsa_cipher_suites, - rc4_ecdh_rsa_cipher_suites, - rc4_ecdsa_cipher_suites]. - cipher_tests() -> [old_cipher_suites, - cipher_suites_mix, - ciphers_rsa_signed_certs, - ciphers_rsa_signed_certs_openssl_names, - ciphers_dsa_signed_certs, - ciphers_dsa_signed_certs_openssl_names, - chacha_rsa_cipher_suites, - chacha_ecdsa_cipher_suites, - anonymous_cipher_suites, - psk_cipher_suites, - psk_with_hint_cipher_suites, - psk_anon_cipher_suites, - psk_anon_with_hint_cipher_suites, - srp_cipher_suites, - srp_anon_cipher_suites, - srp_dsa_cipher_suites, - des_rsa_cipher_suites, - des_ecdh_rsa_cipher_suites, + cipher_suites_mix, default_reject_anonymous]. -cipher_tests_ec() -> - [ciphers_ecdsa_signed_certs, - ciphers_ecdsa_signed_certs_openssl_names, - ciphers_ecdh_rsa_signed_certs, - ciphers_ecdh_rsa_signed_certs_openssl_names]. - error_handling_tests()-> [close_transport_accept, recv_active, @@ -269,6 +241,26 @@ rizzo_tests() -> rizzo_zero_n, rizzo_disabled]. +%% For testing TLS 1.3 features and possible regressions +tls13_test_group() -> + [tls13_enable_client_side, + tls13_enable_server_side, + tls_record_1_3_encode_decode, + tls13_finished_verify_data, + tls13_1_RTT_handshake, + tls13_basic_ssl_server_openssl_client, + tls13_custom_groups_ssl_server_openssl_client, + tls13_hello_retry_request_ssl_server_openssl_client, + tls13_client_auth_empty_cert_alert_ssl_server_openssl_client, + tls13_client_auth_empty_cert_ssl_server_openssl_client, + tls13_client_auth_ssl_server_openssl_client, + tls13_hrr_client_auth_empty_cert_alert_ssl_server_openssl_client, + tls13_hrr_client_auth_empty_cert_ssl_server_openssl_client, + tls13_hrr_client_auth_ssl_server_openssl_client, + tls13_unsupported_sign_algo_client_auth_ssl_server_openssl_client, + tls13_unsupported_sign_algo_cert_client_auth_ssl_server_openssl_client, + tls13_connection_information]. + %%-------------------------------------------------------------------- init_per_suite(Config0) -> catch crypto:stop(), @@ -298,7 +290,8 @@ init_per_group(GroupName, Config) when GroupName == basic_tls; GroupName == options; GroupName == basic; GroupName == session; - GroupName == error_handling_tests_tls + GroupName == error_handling_tests_tls; + GroupName == tls13_test_group -> ssl_test_lib:clean_tls_version(Config); init_per_group(GroupName, Config) -> @@ -386,26 +379,7 @@ init_per_testcase(TestCase, Config) when TestCase == client_renegotiate; ct:timetrap({seconds, ?SEC_RENEGOTIATION_TIMEOUT + 5}), Config; -init_per_testcase(TestCase, Config) when TestCase == psk_cipher_suites; - TestCase == psk_with_hint_cipher_suites; - TestCase == ciphers_rsa_signed_certs; - TestCase == ciphers_rsa_signed_certs_openssl_names; - TestCase == ciphers_ecdh_rsa_signed_certs_openssl_names; - TestCase == ciphers_ecdh_rsa_signed_certs; - TestCase == ciphers_dsa_signed_certs; - TestCase == ciphers_dsa_signed_certs_openssl_names; - TestCase == anonymous_cipher_suites; - TestCase == ciphers_ecdsa_signed_certs; - TestCase == ciphers_ecdsa_signed_certs_openssl_names; - TestCase == anonymous_cipher_suites; - TestCase == psk_anon_cipher_suites; - TestCase == psk_anon_with_hint_cipher_suites; - TestCase == srp_cipher_suites; - TestCase == srp_anon_cipher_suites; - TestCase == srp_dsa_cipher_suites; - TestCase == des_rsa_cipher_suites; - TestCase == des_ecdh_rsa_cipher_suites; - TestCase == versions_option; +init_per_testcase(TestCase, Config) when TestCase == versions_option; TestCase == tls_tcp_connect_big -> ssl_test_lib:ct_log_supported_protocol_versions(Config), ct:timetrap({seconds, 60}), @@ -575,11 +549,10 @@ alerts(Config) when is_list(Config) -> Alerts = [?ALERT_REC(?WARNING, ?CLOSE_NOTIFY) | [?ALERT_REC(?FATAL, Desc) || Desc <- Descriptions]], lists:foreach(fun(Alert) -> - case ssl_alert:alert_txt(Alert) of - Txt when is_list(Txt) -> - ok; - Other -> - ct:fail({unexpected, Other}) + try ssl_alert:alert_txt(Alert) + catch + C:E:T -> + ct:fail({unexpected, {C, E, T}}) end end, Alerts). %%-------------------------------------------------------------------- @@ -1859,14 +1832,12 @@ eccs() -> eccs(Config) when is_list(Config) -> [_|_] = All = ssl:eccs(), - [] = SSL3 = ssl:eccs({3,0}), - [_|_] = Tls = ssl:eccs({3,1}), - [_|_] = Tls1 = ssl:eccs({3,2}), - [_|_] = Tls2 = ssl:eccs({3,3}), [] = SSL3 = ssl:eccs(sslv3), [_|_] = Tls = ssl:eccs(tlsv1), [_|_] = Tls1 = ssl:eccs('tlsv1.1'), [_|_] = Tls2 = ssl:eccs('tlsv1.2'), + [_|_] = Tls1 = ssl:eccs('dtlsv1'), + [_|_] = Tls2 = ssl:eccs('dtlsv1.2'), %% ordering is currently unverified by the test true = lists:sort(All) =:= lists:usort(SSL3 ++ Tls ++ Tls1 ++ Tls2), ok. @@ -2685,144 +2656,6 @@ tls_shutdown_error(Config) when is_list(Config) -> ok = ssl:close(Listen), {error, closed} = ssl:shutdown(Listen, read_write). -%%------------------------------------------------------------------- -ciphers_rsa_signed_certs() -> - [{doc,"Test all rsa ssl cipher suites in highest support ssl/tls version"}]. - -ciphers_rsa_signed_certs(Config) when is_list(Config) -> - Ciphers = ssl_test_lib:rsa_suites(crypto), - run_suites(Ciphers, Config, rsa). -%%------------------------------------------------------------------- -ciphers_rsa_signed_certs_openssl_names() -> - [{doc,"Test all rsa ssl cipher suites in highest support ssl/tls version"}]. - -ciphers_rsa_signed_certs_openssl_names(Config) when is_list(Config) -> - Ciphers = ssl_test_lib:openssl_rsa_suites(), - run_suites(Ciphers, Config, rsa). - -%%------------------------------------------------------------------- -ciphers_dsa_signed_certs() -> - [{doc,"Test all dsa ssl cipher suites in highest support ssl/tls version"}]. - -ciphers_dsa_signed_certs(Config) when is_list(Config) -> - NVersion = ssl_test_lib:protocol_version(Config, tuple), - Ciphers = ssl_test_lib:dsa_suites(NVersion), - run_suites(Ciphers, Config, dsa). -%%------------------------------------------------------------------- -ciphers_dsa_signed_certs_openssl_names() -> - [{doc,"Test all dsa ssl cipher suites in highest support ssl/tls version"}]. - -ciphers_dsa_signed_certs_openssl_names(Config) when is_list(Config) -> - Ciphers = ssl_test_lib:openssl_dsa_suites(), - run_suites(Ciphers, Config, dsa). - -%%------------------------------------------------------------------- -chacha_rsa_cipher_suites()-> - [{doc,"Test the cacha with ECDSA signed certs ciphersuites"}]. -chacha_rsa_cipher_suites(Config) when is_list(Config) -> - NVersion = ssl_test_lib:protocol_version(Config, tuple), - Ciphers = [S || {KeyEx,_,_} = S <- ssl_test_lib:chacha_suites(NVersion), - KeyEx == ecdhe_rsa, KeyEx == dhe_rsa], - run_suites(Ciphers, Config, chacha_ecdsa). - -%%------------------------------------------------------------------- -chacha_ecdsa_cipher_suites()-> - [{doc,"Test the cacha with ECDSA signed certs ciphersuites"}]. -chacha_ecdsa_cipher_suites(Config) when is_list(Config) -> - NVersion = ssl_test_lib:protocol_version(Config, tuple), - Ciphers = [S || {ecdhe_ecdsa,_,_} = S <- ssl_test_lib:chacha_suites(NVersion)], - run_suites(Ciphers, Config, chacha_rsa). -%%----------------------------------------------------------------- -anonymous_cipher_suites()-> - [{doc,"Test the anonymous ciphersuites"}]. -anonymous_cipher_suites(Config) when is_list(Config) -> - NVersion = ssl_test_lib:protocol_version(Config, tuple), - Ciphers = ssl_test_lib:ecdh_dh_anonymous_suites(NVersion), - run_suites(Ciphers, Config, anonymous). -%%------------------------------------------------------------------- -psk_cipher_suites() -> - [{doc, "Test the PSK ciphersuites WITHOUT server supplied identity hint"}]. -psk_cipher_suites(Config) when is_list(Config) -> - NVersion = ssl_test_lib:protocol_version(Config, tuple), - Ciphers = ssl_test_lib:psk_suites(NVersion), - run_suites(Ciphers, Config, psk). -%%------------------------------------------------------------------- -psk_with_hint_cipher_suites()-> - [{doc, "Test the PSK ciphersuites WITH server supplied identity hint"}]. -psk_with_hint_cipher_suites(Config) when is_list(Config) -> - NVersion = ssl_test_lib:protocol_version(Config, tuple), - Ciphers = ssl_test_lib:psk_suites(NVersion), - run_suites(Ciphers, Config, psk_with_hint). -%%------------------------------------------------------------------- -psk_anon_cipher_suites() -> - [{doc, "Test the anonymous PSK ciphersuites WITHOUT server supplied identity hint"}]. -psk_anon_cipher_suites(Config) when is_list(Config) -> - NVersion = ssl_test_lib:protocol_version(Config, tuple), - Ciphers = ssl_test_lib:psk_anon_suites(NVersion), - run_suites(Ciphers, Config, psk_anon). -%%------------------------------------------------------------------- -psk_anon_with_hint_cipher_suites()-> - [{doc, "Test the anonymous PSK ciphersuites WITH server supplied identity hint"}]. -psk_anon_with_hint_cipher_suites(Config) when is_list(Config) -> - NVersion = ssl_test_lib:protocol_version(Config, tuple), - Ciphers = ssl_test_lib:psk_anon_suites(NVersion), - run_suites(Ciphers, Config, psk_anon_with_hint). -%%------------------------------------------------------------------- -srp_cipher_suites()-> - [{doc, "Test the SRP ciphersuites"}]. -srp_cipher_suites(Config) when is_list(Config) -> - Ciphers = ssl_test_lib:srp_suites(), - run_suites(Ciphers, Config, srp). -%%------------------------------------------------------------------- -srp_anon_cipher_suites()-> - [{doc, "Test the anonymous SRP ciphersuites"}]. -srp_anon_cipher_suites(Config) when is_list(Config) -> - Ciphers = ssl_test_lib:srp_anon_suites(), - run_suites(Ciphers, Config, srp_anon). -%%------------------------------------------------------------------- -srp_dsa_cipher_suites()-> - [{doc, "Test the SRP DSA ciphersuites"}]. -srp_dsa_cipher_suites(Config) when is_list(Config) -> - Ciphers = ssl_test_lib:srp_dss_suites(), - run_suites(Ciphers, Config, srp_dsa). -%%------------------------------------------------------------------- -rc4_rsa_cipher_suites()-> - [{doc, "Test the RC4 ciphersuites"}]. -rc4_rsa_cipher_suites(Config) when is_list(Config) -> - NVersion = ssl_test_lib:protocol_version(Config, tuple), - Ciphers = [S || {rsa,_,_} = S <- ssl_test_lib:rc4_suites(NVersion)], - run_suites(Ciphers, Config, rc4_rsa). -%------------------------------------------------------------------- -rc4_ecdh_rsa_cipher_suites()-> - [{doc, "Test the RC4 ciphersuites"}]. -rc4_ecdh_rsa_cipher_suites(Config) when is_list(Config) -> - NVersion = ssl_test_lib:protocol_version(Config, tuple), - Ciphers = [S || {ecdh_rsa,_,_} = S <- ssl_test_lib:rc4_suites(NVersion)], - run_suites(Ciphers, Config, rc4_ecdh_rsa). - -%%------------------------------------------------------------------- -rc4_ecdsa_cipher_suites()-> - [{doc, "Test the RC4 ciphersuites"}]. -rc4_ecdsa_cipher_suites(Config) when is_list(Config) -> - NVersion = tls_record:highest_protocol_version([]), - Ciphers = [S || {ecdhe_ecdsa,_,_} = S <- ssl_test_lib:rc4_suites(NVersion)], - run_suites(Ciphers, Config, rc4_ecdsa). - -%%------------------------------------------------------------------- -des_rsa_cipher_suites()-> - [{doc, "Test the des_rsa ciphersuites"}]. -des_rsa_cipher_suites(Config) when is_list(Config) -> - NVersion = tls_record:highest_protocol_version([]), - Ciphers = [S || {rsa,_,_} = S <- ssl_test_lib:des_suites(NVersion)], - run_suites(Ciphers, Config, des_rsa). -%------------------------------------------------------------------- -des_ecdh_rsa_cipher_suites()-> - [{doc, "Test ECDH rsa signed ciphersuites"}]. -des_ecdh_rsa_cipher_suites(Config) when is_list(Config) -> - NVersion = ssl_test_lib:protocol_version(Config, tuple), - Ciphers = [S || {dhe_rsa,_,_} = S <- ssl_test_lib:des_suites(NVersion)], - run_suites(Ciphers, Config, des_dhe_rsa). - %%-------------------------------------------------------------------- default_reject_anonymous()-> [{doc,"Test that by default anonymous cipher suites are rejected "}]. @@ -2849,36 +2682,6 @@ default_reject_anonymous(Config) when is_list(Config) -> ssl_test_lib:check_server_alert(Server, Client, insufficient_security). %%-------------------------------------------------------------------- -ciphers_ecdsa_signed_certs() -> - [{doc, "Test all ecdsa ssl cipher suites in highest support ssl/tls version"}]. - -ciphers_ecdsa_signed_certs(Config) when is_list(Config) -> - NVersion = ssl_test_lib:protocol_version(Config, tuple), - Ciphers = ssl_test_lib:ecdsa_suites(NVersion), - run_suites(Ciphers, Config, ecdsa). -%%-------------------------------------------------------------------- -ciphers_ecdsa_signed_certs_openssl_names() -> - [{doc, "Test all ecdsa ssl cipher suites in highest support ssl/tls version"}]. - -ciphers_ecdsa_signed_certs_openssl_names(Config) when is_list(Config) -> - Ciphers = ssl_test_lib:openssl_ecdsa_suites(), - run_suites(Ciphers, Config, ecdsa). -%%-------------------------------------------------------------------- -ciphers_ecdh_rsa_signed_certs() -> - [{doc, "Test all ecdh_rsa ssl cipher suites in highest support ssl/tls version"}]. - -ciphers_ecdh_rsa_signed_certs(Config) when is_list(Config) -> - NVersion = ssl_test_lib:protocol_version(Config, tuple), - Ciphers = ssl_test_lib:ecdh_rsa_suites(NVersion), - run_suites(Ciphers, Config, ecdh_rsa). -%%-------------------------------------------------------------------- -ciphers_ecdh_rsa_signed_certs_openssl_names() -> - [{doc, "Test all ecdh_rsa ssl cipher suites in highest support ssl/tls version"}]. - -ciphers_ecdh_rsa_signed_certs_openssl_names(Config) when is_list(Config) -> - Ciphers = ssl_test_lib:openssl_ecdh_rsa_suites(), - run_suites(Ciphers, Config, ecdh_rsa). -%%-------------------------------------------------------------------- reuse_session() -> [{doc,"Test reuse of sessions (short handshake)"}]. reuse_session(Config) when is_list(Config) -> @@ -2969,8 +2772,8 @@ make_sure_expired(Host, Port, Id) -> server_does_not_want_to_reuse_session() -> [{doc,"Test reuse of sessions (short handshake)"}]. server_does_not_want_to_reuse_session(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = @@ -3440,9 +3243,9 @@ defaults(Config) when is_list(Config)-> true = lists:member(sslv3, proplists:get_value(available, Versions)), false = lists:member(sslv3, proplists:get_value(supported, Versions)), true = lists:member('tlsv1', proplists:get_value(available, Versions)), - true = lists:member('tlsv1', proplists:get_value(supported, Versions)), + false = lists:member('tlsv1', proplists:get_value(supported, Versions)), true = lists:member('tlsv1.1', proplists:get_value(available, Versions)), - true = lists:member('tlsv1.1', proplists:get_value(supported, Versions)), + false = lists:member('tlsv1.1', proplists:get_value(supported, Versions)), true = lists:member('tlsv1.2', proplists:get_value(available, Versions)), true = lists:member('tlsv1.2', proplists:get_value(supported, Versions)), false = lists:member({rsa,rc4_128,sha}, ssl:cipher_suites()), @@ -3454,7 +3257,7 @@ defaults(Config) when is_list(Config)-> true = lists:member('dtlsv1.2', proplists:get_value(available_dtls, Versions)), true = lists:member('dtlsv1', proplists:get_value(available_dtls, Versions)), true = lists:member('dtlsv1.2', proplists:get_value(supported_dtls, Versions)), - true = lists:member('dtlsv1', proplists:get_value(supported_dtls, Versions)). + false = lists:member('dtlsv1', proplists:get_value(supported_dtls, Versions)). %%-------------------------------------------------------------------- reuseaddr() -> @@ -3615,7 +3418,7 @@ honor_cipher_order(Config, Honor, ServerCiphers, ClientCiphers, Expected) -> %%-------------------------------------------------------------------- tls_ciphersuite_vs_version() -> - [{doc,"Test a SSLv3 client can not negotiate a TLSv* cipher suite."}]. + [{doc,"Test a SSLv3 client cannot negotiate a TLSv* cipher suite."}]. tls_ciphersuite_vs_version(Config) when is_list(Config) -> {_ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -3658,14 +3461,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]), @@ -3849,7 +3652,7 @@ listen_socket(Config) -> {error, enotconn} = ssl:peername(ListenSocket), {error, enotconn} = ssl:peercert(ListenSocket), {error, enotconn} = ssl:renegotiate(ListenSocket), - {error, enotconn} = ssl:prf(ListenSocket, 'master_secret', <<"Label">>, client_random, 256), + {error, enotconn} = ssl:prf(ListenSocket, 'master_secret', <<"Label">>, [client_random], 256), {error, enotconn} = ssl:shutdown(ListenSocket, read_write), ok = ssl:close(ListenSocket). @@ -4562,6 +4365,1292 @@ accept_pool(Config) when is_list(Config) -> ssl_test_lib:close(Client1), ssl_test_lib:close(Client2). +%%-------------------------------------------------------------------- +%% TLS 1.3 +%%-------------------------------------------------------------------- + +tls13_enable_client_side() -> + [{doc,"Test that a TLS 1.3 client can connect to a TLS 1.2 server."}]. + +tls13_enable_client_side(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, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, protocol_info_result, []}}, + {options, [{versions, + ['tlsv1.1', 'tlsv1.2']} | ServerOpts] }]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, protocol_info_result, []}}, + {options, [{versions, + ['tlsv1.2', 'tlsv1.3']} | ClientOpts]}]), + + ServerMsg = ClientMsg = {ok, 'tlsv1.2'}, + ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg). + +tls13_enable_server_side() -> + [{doc,"Test that a TLS 1.2 client can connect to a TLS 1.3 server."}]. + +tls13_enable_server_side(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, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, protocol_info_result, []}}, + {options, [{versions, + ['tlsv1.2', 'tlsv1.3']} | ServerOpts] }]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, protocol_info_result, []}}, + {options, [{versions, + ['tlsv1.2', 'tlsv1.1']} | ClientOpts]}]), + + ServerMsg = ClientMsg = {ok, 'tlsv1.2'}, + ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg). + +tls_record_1_3_encode_decode() -> + [{doc,"Test TLS 1.3 record encode/decode functions"}]. + +tls_record_1_3_encode_decode(_Config) -> + ConnectionStates = + #{current_read => + #{beast_mitigation => one_n_minus_one, + cipher_state => + {cipher_state, + <<14,172,111,243,199,170,242,203,126,205,34,93,122,115,226,14, + 15,117,155,48,24,112,61,15,113,208,127,51,179,227,194,232>>, + <<197,54,168,218,54,91,157,58,30,201,197,142,51,58,53,231,228, + 131,57,122,170,78,82,196,30,48,23,16,95,255,185,236>>, + undefined,undefined,undefined,16}, + client_verify_data => undefined,compression_state => undefined, + mac_secret => undefined,secure_renegotiation => undefined, + security_parameters => + {security_parameters, + <<19,2>>, + 0,8,2,undefined,undefined,undefined,undefined,undefined, + sha384,undefined,undefined, + {handshake_secret, + <<128,229,186,211,62,127,182,20,62,166,233,23,135,64,121, + 3,104,251,214,161,253,31,3,2,232,37,8,221,189,72,64,218, + 121,41,112,148,254,34,68,164,228,60,161,201,132,55,56, + 157>>}, + undefined, + <<92,24,205,75,244,60,136,212,250,32,214,20,37,3,213,87,61,207, + 147,61,168,145,177,118,160,153,33,53,48,108,191,174>>, + undefined}, + sequence_number => 0,server_verify_data => undefined}, + current_write => + #{beast_mitigation => one_n_minus_one, + cipher_state => + {cipher_state, + <<14,172,111,243,199,170,242,203,126,205,34,93,122,115,226,14, + 15,117,155,48,24,112,61,15,113,208,127,51,179,227,194,232>>, + <<197,54,168,218,54,91,157,58,30,201,197,142,51,58,53,231,228, + 131,57,122,170,78,82,196,30,48,23,16,95,255,185,236>>, + undefined,undefined,undefined,16}, + client_verify_data => undefined,compression_state => undefined, + mac_secret => undefined,secure_renegotiation => undefined, + security_parameters => + {security_parameters, + <<19,2>>, + 0,8,2,undefined,undefined,undefined,undefined,undefined, + sha384,undefined,undefined, + {handshake_secret, + <<128,229,186,211,62,127,182,20,62,166,233,23,135,64,121, + 3,104,251,214,161,253,31,3,2,232,37,8,221,189,72,64,218, + 121,41,112,148,254,34,68,164,228,60,161,201,132,55,56, + 157>>}, + undefined, + <<92,24,205,75,244,60,136,212,250,32,214,20,37,3,213,87,61,207, + 147,61,168,145,177,118,160,153,33,53,48,108,191,174>>, + undefined}, + sequence_number => 0,server_verify_data => undefined}}, + + PlainText = [11, + <<0,2,175>>, + <<0,0,2,171,0,2,166,48,130,2,162,48,130,1,138,2,9,0,186,57,220,137,88,255, + 191,235,48,13,6,9,42,134,72,134,247,13,1,1,11,5,0,48,18,49,16,48,14,6,3,85, + 4,3,12,7,84,101,115,116,32,67,65,48,30,23,13,49,56,48,53,48,52,49,52,49,50, + 51,56,90,23,13,50,56,48,50,48,52,49,52,49,50,51,56,90,48,20,49,18,48,16,6, + 3,85,4,3,12,9,108,111,99,97,108,104,111,115,116,48,130,1,34,48,13,6,9,42, + 134,72,134,247,13,1,1,1,5,0,3,130,1,15,0,48,130,1,10,2,130,1,1,0,169,40, + 144,176,121,63,134,97,144,126,243,183,225,157,37,131,183,225,87,243,23,88, + 230,70,9,134,32,147,7,27,167,98,51,81,224,75,199,12,229,251,195,207,75,179, + 181,78,128,3,255,44,58,39,43,172,142,45,186,58,51,65,187,199,154,153,245, + 70,133,137,1,27,87,42,116,65,251,129,109,145,233,97,171,71,54,213,185,74, + 209,166,11,218,189,119,206,86,170,60,212,213,85,189,30,50,215,23,185,53, + 132,238,132,176,198,250,139,251,198,221,225,128,109,113,23,220,39,143,71, + 30,59,189,51,244,61,158,214,146,180,196,103,169,189,221,136,78,129,216,148, + 2,9,8,65,37,224,215,233,13,209,21,235,20,143,33,74,59,53,208,90,152,94,251, + 54,114,171,39,88,230,227,158,211,135,37,182,67,205,161,59,20,138,58,253,15, + 53,48,8,157,9,95,197,9,177,116,21,54,9,125,78,109,182,83,20,16,234,223,116, + 41,155,123,87,77,17,120,153,246,239,124,130,105,219,166,146,242,151,66,198, + 75,72,63,28,246,86,16,244,223,22,36,50,15,247,222,98,6,152,136,154,72,150, + 73,127,2,3,1,0,1,48,13,6,9,42,134,72,134,247,13,1,1,11,5,0,3,130,1,1,0,76, + 33,54,160,229,219,219,193,150,116,245,252,18,39,235,145,86,12,167,171,52, + 117,166,30,83,5,216,245,177,217,247,95,1,136,94,246,212,108,248,230,111, + 225,202,189,6,129,8,70,128,245,18,204,215,87,82,129,253,227,122,66,182,184, + 189,30,193,169,144,218,216,109,105,110,215,144,60,104,162,178,101,164,218, + 122,60,37,41,143,57,150,52,59,51,112,238,113,239,168,114,69,183,143,154,73, + 61,58,80,247,172,95,251,55,28,186,28,200,206,230,118,243,92,202,189,49,76, + 124,252,76,0,247,112,85,194,69,59,222,163,228,103,49,110,104,109,251,155, + 138,9,37,167,49,189,48,134,52,158,185,129,24,96,153,196,251,90,206,76,239, + 175,119,174,165,133,108,222,125,237,125,187,149,152,83,190,16,202,94,202, + 201,40,218,22,254,63,189,41,174,97,140,203,70,18,196,118,237,175,134,79,78, + 246,2,61,54,77,186,112,32,17,193,192,188,217,252,215,200,7,245,180,179,132, + 183,212,229,155,15,152,206,135,56,81,88,3,123,244,149,110,182,72,109,70,62, + 146,152,146,151,107,126,216,210,9,93,0,0>>], + + {[_Header|Encoded], _} = tls_record_1_3:encode_plain_text(22, PlainText, ConnectionStates), + CipherText = #ssl_tls{type = 23, version = {3,3}, fragment = Encoded}, + + {#ssl_tls{type = 22, version = {3,4}, fragment = DecodedText}, _} = + tls_record_1_3:decode_cipher_text(CipherText, ConnectionStates), + + DecodedText = iolist_to_binary(PlainText), + ct:log("Decoded: ~p ~n", [DecodedText]), + ok. + +tls13_1_RTT_handshake() -> + [{doc,"Test TLS 1.3 1-RTT Handshake"}]. + +tls13_1_RTT_handshake(_Config) -> + %% ConnectionStates with NULL cipher + ConnStatesNull = + #{current_write => + #{security_parameters => + #security_parameters{cipher_suite = ?TLS_NULL_WITH_NULL_NULL}, + sequence_number => 0 + } + }, + + %% {client} construct a ClientHello handshake message: + %% + %% ClientHello (196 octets): 01 00 00 c0 03 03 cb 34 ec b1 e7 81 63 + %% ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12 ec 18 a2 ef 62 83 + %% 02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00 00 91 00 00 00 0b + %% 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 00 00 0a 00 14 00 + %% 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 23 + %% 00 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d e5 60 e4 bd 43 d2 + %% 3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d 54 13 69 1e 52 9a + %% af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e 04 03 05 03 06 03 + %% 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02 01 04 02 05 02 06 + %% 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01 + %% + %% {client} send handshake record: + %% + %% payload (196 octets): 01 00 00 c0 03 03 cb 34 ec b1 e7 81 63 ba + %% 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12 ec 18 a2 ef 62 83 02 + %% 4d ec e7 00 00 06 13 01 13 03 13 02 01 00 00 91 00 00 00 0b 00 + %% 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 00 00 0a 00 14 00 12 + %% 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 23 00 + %% 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d e5 60 e4 bd 43 d2 3d + %% 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d 54 13 69 1e 52 9a af + %% 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e 04 03 05 03 06 03 02 + %% 03 08 04 08 05 08 06 04 01 05 01 06 01 02 01 04 02 05 02 06 02 + %% 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01 + %% + %% complete record (201 octets): 16 03 01 00 c4 01 00 00 c0 03 03 cb + %% 34 ec b1 e7 81 63 ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12 + %% ec 18 a2 ef 62 83 02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00 + %% 00 91 00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 + %% 00 00 0a 00 14 00 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 + %% 01 03 01 04 00 23 00 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d + %% e5 60 e4 bd 43 d2 3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d + %% 54 13 69 1e 52 9a af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e + %% 04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02 + %% 01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01 + ClientHello = + hexstr2bin("01 00 00 c0 03 03 cb 34 ec b1 e7 81 63 + ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12 ec 18 a2 ef 62 83 + 02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00 00 91 00 00 00 0b + 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 00 00 0a 00 14 00 + 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 23 + 00 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d e5 60 e4 bd 43 d2 + 3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d 54 13 69 1e 52 9a + af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e 04 03 05 03 06 03 + 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02 01 04 02 05 02 06 + 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01"), + + ClientHelloRecord = + %% Current implementation always sets + %% legacy_record_version to Ox0303 + hexstr2bin("16 03 03 00 c4 01 00 00 c0 03 03 cb + 34 ec b1 e7 81 63 ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12 + ec 18 a2 ef 62 83 02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00 + 00 91 00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 + 00 00 0a 00 14 00 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 + 01 03 01 04 00 23 00 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d + e5 60 e4 bd 43 d2 3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d + 54 13 69 1e 52 9a af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e + 04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02 + 01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01"), + + {CHEncrypted, _} = + tls_record:encode_handshake(ClientHello, {3,4}, ConnStatesNull), + ClientHelloRecord = iolist_to_binary(CHEncrypted), + + %% {server} extract secret "early": + %% + %% salt: 0 (all zero octets) + %% + %% IKM (32 octets): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + %% 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + %% + %% secret (32 octets): 33 ad 0a 1c 60 7e c0 3b 09 e6 cd 98 93 68 0c + %% e2 10 ad f3 00 aa 1f 26 60 e1 b2 2e 10 f1 70 f9 2a + HKDFAlgo = sha256, + Salt = binary:copy(<<?BYTE(0)>>, 32), + IKM = binary:copy(<<?BYTE(0)>>, 32), + EarlySecret = + hexstr2bin("33 ad 0a 1c 60 7e c0 3b 09 e6 cd 98 93 68 0c + e2 10 ad f3 00 aa 1f 26 60 e1 b2 2e 10 f1 70 f9 2a"), + + {early_secret, EarlySecret} = tls_v1:key_schedule(early_secret, HKDFAlgo, {psk, Salt}), + + %% {client} create an ephemeral x25519 key pair: + %% + %% private key (32 octets): 49 af 42 ba 7f 79 94 85 2d 71 3e f2 78 + %% 4b cb ca a7 91 1d e2 6a dc 56 42 cb 63 45 40 e7 ea 50 05 + %% + %% public key (32 octets): 99 38 1d e5 60 e4 bd 43 d2 3d 8e 43 5a 7d + %% ba fe b3 c0 6e 51 c1 3c ae 4d 54 13 69 1e 52 9a af 2c + CPublicKey = + hexstr2bin("99 38 1d e5 60 e4 bd 43 d2 3d 8e 43 5a 7d + ba fe b3 c0 6e 51 c1 3c ae 4d 54 13 69 1e 52 9a af 2c"), + + %% {server} create an ephemeral x25519 key pair: + %% + %% private key (32 octets): b1 58 0e ea df 6d d5 89 b8 ef 4f 2d 56 + %% 52 57 8c c8 10 e9 98 01 91 ec 8d 05 83 08 ce a2 16 a2 1e + %% + %% public key (32 octets): c9 82 88 76 11 20 95 fe 66 76 2b db f7 c6 + %% 72 e1 56 d6 cc 25 3b 83 3d f1 dd 69 b1 b0 4e 75 1f 0f + SPrivateKey = + hexstr2bin("b1 58 0e ea df 6d d5 89 b8 ef 4f 2d 56 + 52 57 8c c8 10 e9 98 01 91 ec 8d 05 83 08 ce a2 16 a2 1e"), + + SPublicKey = + hexstr2bin("c9 82 88 76 11 20 95 fe 66 76 2b db f7 c6 + 72 e1 56 d6 cc 25 3b 83 3d f1 dd 69 b1 b0 4e 75 1f 0f"), + + %% {server} construct a ServerHello handshake message: + %% + %% ServerHello (90 octets): 02 00 00 56 03 03 a6 af 06 a4 12 18 60 + %% dc 5e 6e 60 24 9c d3 4c 95 93 0c 8a c5 cb 14 34 da c1 55 77 2e + %% d3 e2 69 28 00 13 01 00 00 2e 00 33 00 24 00 1d 00 20 c9 82 88 + %% 76 11 20 95 fe 66 76 2b db f7 c6 72 e1 56 d6 cc 25 3b 83 3d f1 + %% dd 69 b1 b0 4e 75 1f 0f 00 2b 00 02 03 04 + ServerHello = + hexstr2bin("02 00 00 56 03 03 a6 af 06 a4 12 18 60 + dc 5e 6e 60 24 9c d3 4c 95 93 0c 8a c5 cb 14 34 da c1 55 77 2e + d3 e2 69 28 00 13 01 00 00 2e 00 33 00 24 00 1d 00 20 c9 82 88 + 76 11 20 95 fe 66 76 2b db f7 c6 72 e1 56 d6 cc 25 3b 83 3d f1 + dd 69 b1 b0 4e 75 1f 0f 00 2b 00 02 03 04"), + + %% {server} derive secret for handshake "tls13 derived": + %% + %% PRK (32 octets): 33 ad 0a 1c 60 7e c0 3b 09 e6 cd 98 93 68 0c e2 + %% 10 ad f3 00 aa 1f 26 60 e1 b2 2e 10 f1 70 f9 2a + %% + %% hash (32 octets): e3 b0 c4 42 98 fc 1c 14 9a fb f4 c8 99 6f b9 24 + %% 27 ae 41 e4 64 9b 93 4c a4 95 99 1b 78 52 b8 55 + %% + %% info (49 octets): 00 20 0d 74 6c 73 31 33 20 64 65 72 69 76 65 64 + %% 20 e3 b0 c4 42 98 fc 1c 14 9a fb f4 c8 99 6f b9 24 27 ae 41 e4 + %% 64 9b 93 4c a4 95 99 1b 78 52 b8 55 + %% + %% expanded (32 octets): 6f 26 15 a1 08 c7 02 c5 67 8f 54 fc 9d ba + %% b6 97 16 c0 76 18 9c 48 25 0c eb ea c3 57 6c 36 11 ba + Hash = + hexstr2bin("e3 b0 c4 42 98 fc 1c 14 9a fb f4 c8 99 6f b9 24 + 27 ae 41 e4 64 9b 93 4c a4 95 99 1b 78 52 b8 55"), + + Hash = crypto:hash(HKDFAlgo, <<>>), + + Info = + hexstr2bin("00 20 0d 74 6c 73 31 33 20 64 65 72 69 76 65 64 + 20 e3 b0 c4 42 98 fc 1c 14 9a fb f4 c8 99 6f b9 24 27 ae 41 e4 + 64 9b 93 4c a4 95 99 1b 78 52 b8 55"), + + Info = tls_v1:create_info(<<"derived">>, Hash, ssl_cipher:hash_size(HKDFAlgo)), + + Expanded = + hexstr2bin("6f 26 15 a1 08 c7 02 c5 67 8f 54 fc 9d ba + b6 97 16 c0 76 18 9c 48 25 0c eb ea c3 57 6c 36 11 ba"), + + Expanded = tls_v1:derive_secret(EarlySecret, <<"derived">>, <<>>, HKDFAlgo), + + %% {server} extract secret "handshake": + %% + %% salt (32 octets): 6f 26 15 a1 08 c7 02 c5 67 8f 54 fc 9d ba b6 97 + %% 16 c0 76 18 9c 48 25 0c eb ea c3 57 6c 36 11 ba + %% + %% IKM (32 octets): 8b d4 05 4f b5 5b 9d 63 fd fb ac f9 f0 4b 9f 0d + %% 35 e6 d6 3f 53 75 63 ef d4 62 72 90 0f 89 49 2d + %% + %% secret (32 octets): 1d c8 26 e9 36 06 aa 6f dc 0a ad c1 2f 74 1b + %% 01 04 6a a6 b9 9f 69 1e d2 21 a9 f0 ca 04 3f be ac + + %% salt = Expanded + HandshakeIKM = + hexstr2bin("8b d4 05 4f b5 5b 9d 63 fd fb ac f9 f0 4b 9f 0d + 35 e6 d6 3f 53 75 63 ef d4 62 72 90 0f 89 49 2d"), + + HandshakeSecret = + hexstr2bin("1d c8 26 e9 36 06 aa 6f dc 0a ad c1 2f 74 1b + 01 04 6a a6 b9 9f 69 1e d2 21 a9 f0 ca 04 3f be ac"), + + HandshakeIKM = crypto:compute_key(ecdh, CPublicKey, SPrivateKey, x25519), + + {handshake_secret, HandshakeSecret} = + tls_v1:key_schedule(handshake_secret, HKDFAlgo, HandshakeIKM, + {early_secret, EarlySecret}), + + %% {server} derive secret "tls13 c hs traffic": + %% + %% PRK (32 octets): 1d c8 26 e9 36 06 aa 6f dc 0a ad c1 2f 74 1b 01 + %% 04 6a a6 b9 9f 69 1e d2 21 a9 f0 ca 04 3f be ac + %% + %% hash (32 octets): 86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 ed + %% d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8 + %% + %% info (54 octets): 00 20 12 74 6c 73 31 33 20 63 20 68 73 20 74 72 + %% 61 66 66 69 63 20 86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 + %% ed d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8 + %% + %% expanded (32 octets): b3 ed db 12 6e 06 7f 35 a7 80 b3 ab f4 5e + %% 2d 8f 3b 1a 95 07 38 f5 2e 96 00 74 6a 0e 27 a5 5a 21 + + %% PRK = HandshakeSecret + CHSTHash = + hexstr2bin("86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 ed + d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8"), + + CHSTInfo = + hexstr2bin("00 20 12 74 6c 73 31 33 20 63 20 68 73 20 74 72 + 61 66 66 69 63 20 86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 + ed d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8"), + + CHSTrafficSecret = + hexstr2bin(" b3 ed db 12 6e 06 7f 35 a7 80 b3 ab f4 5e + 2d 8f 3b 1a 95 07 38 f5 2e 96 00 74 6a 0e 27 a5 5a 21"), + + CHSH = <<ClientHello/binary,ServerHello/binary>>, + CHSTHash = crypto:hash(HKDFAlgo, CHSH), + CHSTInfo = tls_v1:create_info(<<"c hs traffic">>, CHSTHash, ssl_cipher:hash_size(HKDFAlgo)), + + CHSTrafficSecret = + tls_v1:client_handshake_traffic_secret(HKDFAlgo, {handshake_secret, HandshakeSecret}, CHSH), + + %% {server} derive secret "tls13 s hs traffic": + %% + %% PRK (32 octets): 1d c8 26 e9 36 06 aa 6f dc 0a ad c1 2f 74 1b 01 + %% 04 6a a6 b9 9f 69 1e d2 21 a9 f0 ca 04 3f be ac + %% + %% hash (32 octets): 86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 ed + %% d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8 + %% + %% info (54 octets): 00 20 12 74 6c 73 31 33 20 73 20 68 73 20 74 72 + %% 61 66 66 69 63 20 86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 + %% ed d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8 + %% + %% expanded (32 octets): b6 7b 7d 69 0c c1 6c 4e 75 e5 42 13 cb 2d + %% 37 b4 e9 c9 12 bc de d9 10 5d 42 be fd 59 d3 91 ad 38 + + %% PRK = HandshakeSecret + %% hash = CHSTHash + SHSTInfo = + hexstr2bin("00 20 12 74 6c 73 31 33 20 73 20 68 73 20 74 72 + 61 66 66 69 63 20 86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 + ed d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8"), + + SHSTrafficSecret = + hexstr2bin("b6 7b 7d 69 0c c1 6c 4e 75 e5 42 13 cb 2d + 37 b4 e9 c9 12 bc de d9 10 5d 42 be fd 59 d3 91 ad 38"), + + SHSTInfo = tls_v1:create_info(<<"s hs traffic">>, CHSTHash, ssl_cipher:hash_size(HKDFAlgo)), + + SHSTrafficSecret = + tls_v1:server_handshake_traffic_secret(HKDFAlgo, {handshake_secret, HandshakeSecret}, CHSH), + + + %% {server} derive secret for master "tls13 derived": + %% + %% PRK (32 octets): 1d c8 26 e9 36 06 aa 6f dc 0a ad c1 2f 74 1b 01 + %% 04 6a a6 b9 9f 69 1e d2 21 a9 f0 ca 04 3f be ac + %% + %% hash (32 octets): e3 b0 c4 42 98 fc 1c 14 9a fb f4 c8 99 6f b9 24 + %% 27 ae 41 e4 64 9b 93 4c a4 95 99 1b 78 52 b8 55 + %% + %% info (49 octets): 00 20 0d 74 6c 73 31 33 20 64 65 72 69 76 65 64 + %% 20 e3 b0 c4 42 98 fc 1c 14 9a fb f4 c8 99 6f b9 24 27 ae 41 e4 + %% 64 9b 93 4c a4 95 99 1b 78 52 b8 55 + %% + %% expanded (32 octets): 43 de 77 e0 c7 77 13 85 9a 94 4d b9 db 25 + %% 90 b5 31 90 a6 5b 3e e2 e4 f1 2d d7 a0 bb 7c e2 54 b4 + + %% PRK = HandshakeSecret + %% hash = Hash + %% info = Info + MasterDeriveSecret = + hexstr2bin("43 de 77 e0 c7 77 13 85 9a 94 4d b9 db 25 + 90 b5 31 90 a6 5b 3e e2 e4 f1 2d d7 a0 bb 7c e2 54 b4"), + + MasterDeriveSecret = tls_v1:derive_secret(HandshakeSecret, <<"derived">>, <<>>, HKDFAlgo), + + %% {server} extract secret "master": + %% + %% salt (32 octets): 43 de 77 e0 c7 77 13 85 9a 94 4d b9 db 25 90 b5 + %% 31 90 a6 5b 3e e2 e4 f1 2d d7 a0 bb 7c e2 54 b4 + %% + %% IKM (32 octets): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + %% 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + %% + %% secret (32 octets): 18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a + %% 47 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19 + + %% salt = MasterDeriveSecret + %% IKM = IKM + MasterSecret = + hexstr2bin("18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a + 47 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19"), + + {master_secret, MasterSecret} = + tls_v1:key_schedule(master_secret, HKDFAlgo, {handshake_secret, HandshakeSecret}), + + %% {server} send handshake record: + %% + %% payload (90 octets): 02 00 00 56 03 03 a6 af 06 a4 12 18 60 dc 5e + %% 6e 60 24 9c d3 4c 95 93 0c 8a c5 cb 14 34 da c1 55 77 2e d3 e2 + %% 69 28 00 13 01 00 00 2e 00 33 00 24 00 1d 00 20 c9 82 88 76 11 + %% 20 95 fe 66 76 2b db f7 c6 72 e1 56 d6 cc 25 3b 83 3d f1 dd 69 + %% b1 b0 4e 75 1f 0f 00 2b 00 02 03 04 + %% + %% complete record (95 octets): 16 03 03 00 5a 02 00 00 56 03 03 a6 + %% af 06 a4 12 18 60 dc 5e 6e 60 24 9c d3 4c 95 93 0c 8a c5 cb 14 + %% 34 da c1 55 77 2e d3 e2 69 28 00 13 01 00 00 2e 00 33 00 24 00 + %% 1d 00 20 c9 82 88 76 11 20 95 fe 66 76 2b db f7 c6 72 e1 56 d6 + %% cc 25 3b 83 3d f1 dd 69 b1 b0 4e 75 1f 0f 00 2b 00 02 03 04 + + %% payload = ServerHello + ServerHelloRecord = + hexstr2bin("16 03 03 00 5a 02 00 00 56 03 03 a6 + af 06 a4 12 18 60 dc 5e 6e 60 24 9c d3 4c 95 93 0c 8a c5 cb 14 + 34 da c1 55 77 2e d3 e2 69 28 00 13 01 00 00 2e 00 33 00 24 00 + 1d 00 20 c9 82 88 76 11 20 95 fe 66 76 2b db f7 c6 72 e1 56 d6 + cc 25 3b 83 3d f1 dd 69 b1 b0 4e 75 1f 0f 00 2b 00 02 03 04"), + + {SHEncrypted, _} = + tls_record:encode_handshake(ServerHello, {3,4}, ConnStatesNull), + ServerHelloRecord = iolist_to_binary(SHEncrypted), + + %% {server} derive write traffic keys for handshake data: + %% + %% PRK (32 octets): b6 7b 7d 69 0c c1 6c 4e 75 e5 42 13 cb 2d 37 b4 + %% e9 c9 12 bc de d9 10 5d 42 be fd 59 d3 91 ad 38 + %% + %% key info (13 octets): 00 10 09 74 6c 73 31 33 20 6b 65 79 00 + %% + %% key expanded (16 octets): 3f ce 51 60 09 c2 17 27 d0 f2 e4 e8 6e + %% e4 03 bc + %% + %% iv info (12 octets): 00 0c 08 74 6c 73 31 33 20 69 76 00 + %% + %% iv expanded (12 octets): 5d 31 3e b2 67 12 76 ee 13 00 0b 30 + + %% PRK = SHSTrafficSecret + WriteKeyInfo = + hexstr2bin("00 10 09 74 6c 73 31 33 20 6b 65 79 00"), + + WriteKey = + hexstr2bin("3f ce 51 60 09 c2 17 27 d0 f2 e4 e8 6e e4 03 bc"), + + WriteIVInfo = + hexstr2bin("00 0c 08 74 6c 73 31 33 20 69 76 00"), + + WriteIV = + hexstr2bin(" 5d 31 3e b2 67 12 76 ee 13 00 0b 30"), + + Cipher = aes_128_gcm, %% TODO: get from ServerHello + + WriteKeyInfo = tls_v1:create_info(<<"key">>, <<>>, ssl_cipher:key_material(Cipher)), + %% TODO: remove hardcoded IV size + WriteIVInfo = tls_v1:create_info(<<"iv">>, <<>>, 12), + + {WriteKey, WriteIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, Cipher, SHSTrafficSecret), + + %% {server} construct an EncryptedExtensions handshake message: + %% + %% EncryptedExtensions (40 octets): 08 00 00 24 00 22 00 0a 00 14 00 + %% 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 1c + %% 00 02 40 01 00 00 00 00 + %% + %% {server} construct a Certificate handshake message: + %% + %% Certificate (445 octets): 0b 00 01 b9 00 00 01 b5 00 01 b0 30 82 + %% 01 ac 30 82 01 15 a0 03 02 01 02 02 01 02 30 0d 06 09 2a 86 48 + %% 86 f7 0d 01 01 0b 05 00 30 0e 31 0c 30 0a 06 03 55 04 03 13 03 + %% 72 73 61 30 1e 17 0d 31 36 30 37 33 30 30 31 32 33 35 39 5a 17 + %% 0d 32 36 30 37 33 30 30 31 32 33 35 39 5a 30 0e 31 0c 30 0a 06 + %% 03 55 04 03 13 03 72 73 61 30 81 9f 30 0d 06 09 2a 86 48 86 f7 + %% 0d 01 01 01 05 00 03 81 8d 00 30 81 89 02 81 81 00 b4 bb 49 8f + %% 82 79 30 3d 98 08 36 39 9b 36 c6 98 8c 0c 68 de 55 e1 bd b8 26 + %% d3 90 1a 24 61 ea fd 2d e4 9a 91 d0 15 ab bc 9a 95 13 7a ce 6c + %% 1a f1 9e aa 6a f9 8c 7c ed 43 12 09 98 e1 87 a8 0e e0 cc b0 52 + %% 4b 1b 01 8c 3e 0b 63 26 4d 44 9a 6d 38 e2 2a 5f da 43 08 46 74 + %% 80 30 53 0e f0 46 1c 8c a9 d9 ef bf ae 8e a6 d1 d0 3e 2b d1 93 + %% ef f0 ab 9a 80 02 c4 74 28 a6 d3 5a 8d 88 d7 9f 7f 1e 3f 02 03 + %% 01 00 01 a3 1a 30 18 30 09 06 03 55 1d 13 04 02 30 00 30 0b 06 + %% 03 55 1d 0f 04 04 03 02 05 a0 30 0d 06 09 2a 86 48 86 f7 0d 01 + %% 01 0b 05 00 03 81 81 00 85 aa d2 a0 e5 b9 27 6b 90 8c 65 f7 3a + %% 72 67 17 06 18 a5 4c 5f 8a 7b 33 7d 2d f7 a5 94 36 54 17 f2 ea + %% e8 f8 a5 8c 8f 81 72 f9 31 9c f3 6b 7f d6 c5 5b 80 f2 1a 03 01 + %% 51 56 72 60 96 fd 33 5e 5e 67 f2 db f1 02 70 2e 60 8c ca e6 be + %% c1 fc 63 a4 2a 99 be 5c 3e b7 10 7c 3c 54 e9 b9 eb 2b d5 20 3b + %% 1c 3b 84 e0 a8 b2 f7 59 40 9b a3 ea c9 d9 1d 40 2d cc 0c c8 f8 + %% 96 12 29 ac 91 87 b4 2b 4d e1 00 00 + %% + %% {server} construct a CertificateVerify handshake message: + %% + %% CertificateVerify (136 octets): 0f 00 00 84 08 04 00 80 5a 74 7c + %% 5d 88 fa 9b d2 e5 5a b0 85 a6 10 15 b7 21 1f 82 4c d4 84 14 5a + %% b3 ff 52 f1 fd a8 47 7b 0b 7a bc 90 db 78 e2 d3 3a 5c 14 1a 07 + %% 86 53 fa 6b ef 78 0c 5e a2 48 ee aa a7 85 c4 f3 94 ca b6 d3 0b + %% be 8d 48 59 ee 51 1f 60 29 57 b1 54 11 ac 02 76 71 45 9e 46 44 + %% 5c 9e a5 8c 18 1e 81 8e 95 b8 c3 fb 0b f3 27 84 09 d3 be 15 2a + %% 3d a5 04 3e 06 3d da 65 cd f5 ae a2 0d 53 df ac d4 2f 74 f3 + EncryptedExtensions = + hexstr2bin("08 00 00 24 00 22 00 0a 00 14 00 + 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 1c + 00 02 40 01 00 00 00 00"), + + Certificate = + hexstr2bin("0b 00 01 b9 00 00 01 b5 00 01 b0 30 82 + 01 ac 30 82 01 15 a0 03 02 01 02 02 01 02 30 0d 06 09 2a 86 48 + 86 f7 0d 01 01 0b 05 00 30 0e 31 0c 30 0a 06 03 55 04 03 13 03 + 72 73 61 30 1e 17 0d 31 36 30 37 33 30 30 31 32 33 35 39 5a 17 + 0d 32 36 30 37 33 30 30 31 32 33 35 39 5a 30 0e 31 0c 30 0a 06 + 03 55 04 03 13 03 72 73 61 30 81 9f 30 0d 06 09 2a 86 48 86 f7 + 0d 01 01 01 05 00 03 81 8d 00 30 81 89 02 81 81 00 b4 bb 49 8f + 82 79 30 3d 98 08 36 39 9b 36 c6 98 8c 0c 68 de 55 e1 bd b8 26 + d3 90 1a 24 61 ea fd 2d e4 9a 91 d0 15 ab bc 9a 95 13 7a ce 6c + 1a f1 9e aa 6a f9 8c 7c ed 43 12 09 98 e1 87 a8 0e e0 cc b0 52 + 4b 1b 01 8c 3e 0b 63 26 4d 44 9a 6d 38 e2 2a 5f da 43 08 46 74 + 80 30 53 0e f0 46 1c 8c a9 d9 ef bf ae 8e a6 d1 d0 3e 2b d1 93 + ef f0 ab 9a 80 02 c4 74 28 a6 d3 5a 8d 88 d7 9f 7f 1e 3f 02 03 + 01 00 01 a3 1a 30 18 30 09 06 03 55 1d 13 04 02 30 00 30 0b 06 + 03 55 1d 0f 04 04 03 02 05 a0 30 0d 06 09 2a 86 48 86 f7 0d 01 + 01 0b 05 00 03 81 81 00 85 aa d2 a0 e5 b9 27 6b 90 8c 65 f7 3a + 72 67 17 06 18 a5 4c 5f 8a 7b 33 7d 2d f7 a5 94 36 54 17 f2 ea + e8 f8 a5 8c 8f 81 72 f9 31 9c f3 6b 7f d6 c5 5b 80 f2 1a 03 01 + 51 56 72 60 96 fd 33 5e 5e 67 f2 db f1 02 70 2e 60 8c ca e6 be + c1 fc 63 a4 2a 99 be 5c 3e b7 10 7c 3c 54 e9 b9 eb 2b d5 20 3b + 1c 3b 84 e0 a8 b2 f7 59 40 9b a3 ea c9 d9 1d 40 2d cc 0c c8 f8 + 96 12 29 ac 91 87 b4 2b 4d e1 00 00"), + + CertificateVerify = + hexstr2bin("0f 00 00 84 08 04 00 80 5a 74 7c + 5d 88 fa 9b d2 e5 5a b0 85 a6 10 15 b7 21 1f 82 4c d4 84 14 5a + b3 ff 52 f1 fd a8 47 7b 0b 7a bc 90 db 78 e2 d3 3a 5c 14 1a 07 + 86 53 fa 6b ef 78 0c 5e a2 48 ee aa a7 85 c4 f3 94 ca b6 d3 0b + be 8d 48 59 ee 51 1f 60 29 57 b1 54 11 ac 02 76 71 45 9e 46 44 + 5c 9e a5 8c 18 1e 81 8e 95 b8 c3 fb 0b f3 27 84 09 d3 be 15 2a + 3d a5 04 3e 06 3d da 65 cd f5 ae a2 0d 53 df ac d4 2f 74 f3"), + + %% {server} calculate finished "tls13 finished": + %% + %% PRK (32 octets): b6 7b 7d 69 0c c1 6c 4e 75 e5 42 13 cb 2d 37 b4 + %% e9 c9 12 bc de d9 10 5d 42 be fd 59 d3 91 ad 38 + %% + %% hash (0 octets): (empty) + %% + %% info (18 octets): 00 20 0e 74 6c 73 31 33 20 66 69 6e 69 73 68 65 + %% 64 00 + %% + %% expanded (32 octets): 00 8d 3b 66 f8 16 ea 55 9f 96 b5 37 e8 85 + %% c3 1f c0 68 bf 49 2c 65 2f 01 f2 88 a1 d8 cd c1 9f c8 + %% + %% finished (32 octets): 9b 9b 14 1d 90 63 37 fb d2 cb dc e7 1d f4 + %% de da 4a b4 2c 30 95 72 cb 7f ff ee 54 54 b7 8f 07 18 + + %% PRK = SHSTrafficSecret + FInfo = + hexstr2bin("00 20 0e 74 6c 73 31 33 20 66 69 6e 69 73 68 65 + 64 00"), + + FExpanded = + hexstr2bin("00 8d 3b 66 f8 16 ea 55 9f 96 b5 37 e8 85 + c3 1f c0 68 bf 49 2c 65 2f 01 f2 88 a1 d8 cd c1 9f c8"), + + FinishedVerifyData = + hexstr2bin("9b 9b 14 1d 90 63 37 fb d2 cb dc e7 1d f4 + de da 4a b4 2c 30 95 72 cb 7f ff ee 54 54 b7 8f 07 18"), + + FInfo = tls_v1:create_info(<<"finished">>, <<>>, ssl_cipher:hash_size(HKDFAlgo)), + + FExpanded = tls_v1:finished_key(SHSTrafficSecret, HKDFAlgo), + + MessageHistory0 = [CertificateVerify, + Certificate, + EncryptedExtensions, + ServerHello, + ClientHello], + + FinishedVerifyData = tls_v1:finished_verify_data(FExpanded, HKDFAlgo, MessageHistory0), + + %% {server} construct a Finished handshake message: + %% + %% Finished (36 octets): 14 00 00 20 9b 9b 14 1d 90 63 37 fb d2 cb + %% dc e7 1d f4 de da 4a b4 2c 30 95 72 cb 7f ff ee 54 54 b7 8f 07 + %% 18 + FinishedHSBin = + hexstr2bin("14 00 00 20 9b 9b 14 1d 90 63 37 fb d2 cb + dc e7 1d f4 de da 4a b4 2c 30 95 72 cb 7f ff ee 54 54 b7 8f 07 + 18"), + + FinishedHS = #finished{verify_data = FinishedVerifyData}, + + FinishedIOList = tls_handshake:encode_handshake(FinishedHS, {3,4}), + FinishedHSBin = iolist_to_binary(FinishedIOList), + + %% {server} derive secret "tls13 c ap traffic": + %% + %% PRK (32 octets): 18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a 47 + %% 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19 + %% + %% hash (32 octets): 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a + %% 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13 + %% + %% info (54 octets): 00 20 12 74 6c 73 31 33 20 63 20 61 70 20 74 72 + %% 61 66 66 69 63 20 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b + %% 1a 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13 + %% + %% expanded (32 octets): 9e 40 64 6c e7 9a 7f 9d c0 5a f8 88 9b ce + %% 65 52 87 5a fa 0b 06 df 00 87 f7 92 eb b7 c1 75 04 a5 + + %% PRK = MasterSecret + CAPTHash = + hexstr2bin("96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a + 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13"), + CAPTInfo = + hexstr2bin("00 20 12 74 6c 73 31 33 20 63 20 61 70 20 74 72 + 61 66 66 69 63 20 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b + 1a 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13"), + + CAPTrafficSecret = + hexstr2bin("9e 40 64 6c e7 9a 7f 9d c0 5a f8 88 9b ce + 65 52 87 5a fa 0b 06 df 00 87 f7 92 eb b7 c1 75 04 a5"), + + CHSF = <<ClientHello/binary, + ServerHello/binary, + EncryptedExtensions/binary, + Certificate/binary, + CertificateVerify/binary, + FinishedHSBin/binary>>, + + CAPTHash = crypto:hash(HKDFAlgo, CHSF), + + CAPTInfo = + tls_v1:create_info(<<"c ap traffic">>, CAPTHash, ssl_cipher:hash_size(HKDFAlgo)), + + CAPTrafficSecret = + tls_v1:client_application_traffic_secret_0(HKDFAlgo, {master_secret, MasterSecret}, CHSF), + + %% {server} derive secret "tls13 s ap traffic": + %% + %% PRK (32 octets): 18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a 47 + %% 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19 + %% + %% hash (32 octets): 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a + %% 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13 + %% + %% info (54 octets): 00 20 12 74 6c 73 31 33 20 73 20 61 70 20 74 72 + %% 61 66 66 69 63 20 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b + %% 1a 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13 + %% + %% expanded (32 octets): a1 1a f9 f0 55 31 f8 56 ad 47 11 6b 45 a9 + %% 50 32 82 04 b4 f4 4b fb 6b 3a 4b 4f 1f 3f cb 63 16 43 + + %% PRK = MasterSecret + %% hash = CAPTHash + SAPTInfo = + hexstr2bin(" 00 20 12 74 6c 73 31 33 20 73 20 61 70 20 74 72 + 61 66 66 69 63 20 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b + 1a 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13"), + + SAPTrafficSecret = + hexstr2bin("a1 1a f9 f0 55 31 f8 56 ad 47 11 6b 45 a9 + 50 32 82 04 b4 f4 4b fb 6b 3a 4b 4f 1f 3f cb 63 16 43"), + + SAPTInfo = + tls_v1:create_info(<<"s ap traffic">>, CAPTHash, ssl_cipher:hash_size(HKDFAlgo)), + + SAPTrafficSecret = + tls_v1:server_application_traffic_secret_0(HKDFAlgo, {master_secret, MasterSecret}, CHSF), + + %% {server} derive secret "tls13 exp master": + %% + %% PRK (32 octets): 18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a 47 + %% 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19 + %% + %% hash (32 octets): 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a + %% 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13 + %% + %% info (52 octets): 00 20 10 74 6c 73 31 33 20 65 78 70 20 6d 61 73 + %% 74 65 72 20 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a 00 + %% 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13 + %% + %% expanded (32 octets): fe 22 f8 81 17 6e da 18 eb 8f 44 52 9e 67 + %% 92 c5 0c 9a 3f 89 45 2f 68 d8 ae 31 1b 43 09 d3 cf 50 + + %% PRK = MasterSecret + %% hash = CAPTHash + ExporterInfo = + hexstr2bin("00 20 10 74 6c 73 31 33 20 65 78 70 20 6d 61 73 + 74 65 72 20 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a 00 + 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13"), + + ExporterMasterSecret = + hexstr2bin("fe 22 f8 81 17 6e da 18 eb 8f 44 52 9e 67 + 92 c5 0c 9a 3f 89 45 2f 68 d8 ae 31 1b 43 09 d3 cf 50"), + + ExporterInfo = + tls_v1:create_info(<<"exp master">>, CAPTHash, ssl_cipher:hash_size(HKDFAlgo)), + + ExporterMasterSecret = + tls_v1:exporter_master_secret(HKDFAlgo, {master_secret, MasterSecret}, CHSF), + + %% {server} derive write traffic keys for application data: + %% + %% PRK (32 octets): a1 1a f9 f0 55 31 f8 56 ad 47 11 6b 45 a9 50 32 + %% 82 04 b4 f4 4b fb 6b 3a 4b 4f 1f 3f cb 63 16 43 + %% + %% key info (13 octets): 00 10 09 74 6c 73 31 33 20 6b 65 79 00 + %% + %% key expanded (16 octets): 9f 02 28 3b 6c 9c 07 ef c2 6b b9 f2 ac + %% 92 e3 56 + %% + %% iv info (12 octets): 00 0c 08 74 6c 73 31 33 20 69 76 00 + %% + %% iv expanded (12 octets): cf 78 2b 88 dd 83 54 9a ad f1 e9 84 + + %% PRK = SAPTrafficsecret + %% key info = WriteKeyInfo + %% iv info = WrtieIVInfo + SWKey = + hexstr2bin("9f 02 28 3b 6c 9c 07 ef c2 6b b9 f2 ac 92 e3 56"), + + SWIV = + hexstr2bin("cf 78 2b 88 dd 83 54 9a ad f1 e9 84"), + + {SWKey, SWIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, Cipher, SAPTrafficSecret), + + %% {server} derive read traffic keys for handshake data: + %% + %% PRK (32 octets): b3 ed db 12 6e 06 7f 35 a7 80 b3 ab f4 5e 2d 8f + %% 3b 1a 95 07 38 f5 2e 96 00 74 6a 0e 27 a5 5a 21 + %% + %% key info (13 octets): 00 10 09 74 6c 73 31 33 20 6b 65 79 00 + %% + %% key expanded (16 octets): db fa a6 93 d1 76 2c 5b 66 6a f5 d9 50 + %% 25 8d 01 + %% + %% iv info (12 octets): 00 0c 08 74 6c 73 31 33 20 69 76 00 + %% + %% iv expanded (12 octets): 5b d3 c7 1b 83 6e 0b 76 bb 73 26 5f + + %% PRK = CHSTrafficsecret + %% key info = WriteKeyInfo + %% iv info = WrtieIVInfo + SRKey = + hexstr2bin("db fa a6 93 d1 76 2c 5b 66 6a f5 d9 50 25 8d 01"), + + SRIV = + hexstr2bin("5b d3 c7 1b 83 6e 0b 76 bb 73 26 5f"), + + {SRKey, SRIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, Cipher, CHSTrafficSecret). + + +tls13_finished_verify_data() -> + [{doc,"Test TLS 1.3 Finished message handling"}]. + +tls13_finished_verify_data(_Config) -> + ClientHello = + hexstr2bin("01 00 00 c6 03 03 00 01 02 03 04 05 06 07 08 09 + 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 + 1a 1b 1c 1d 1e 1f 20 e0 e1 e2 e3 e4 e5 e6 e7 e8 + e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 + f9 fa fb fc fd fe ff 00 06 13 01 13 02 13 03 01 + 00 00 77 00 00 00 18 00 16 00 00 13 65 78 61 6d + 70 6c 65 2e 75 6c 66 68 65 69 6d 2e 6e 65 74 00 + 0a 00 08 00 06 00 1d 00 17 00 18 00 0d 00 14 00 + 12 04 03 08 04 04 01 05 03 08 05 05 01 08 06 06 + 01 02 01 00 33 00 26 00 24 00 1d 00 20 35 80 72 + d6 36 58 80 d1 ae ea 32 9a df 91 21 38 38 51 ed + 21 a2 8e 3b 75 e9 65 d0 d2 cd 16 62 54 00 2d 00 + 02 01 01 00 2b 00 03 02 03 04"), + + ServerHello = + hexstr2bin("02 00 00 76 03 03 70 71 72 73 74 75 76 77 78 79 + 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 + 8a 8b 8c 8d 8e 8f 20 e0 e1 e2 e3 e4 e5 e6 e7 e8 + e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 + f9 fa fb fc fd fe ff 13 01 00 00 2e 00 33 00 24 + 00 1d 00 20 9f d7 ad 6d cf f4 29 8d d3 f9 6d 5b + 1b 2a f9 10 a0 53 5b 14 88 d7 f8 fa bb 34 9a 98 + 28 80 b6 15 00 2b 00 02 03 04"), + + EncryptedExtensions = + hexstr2bin("08 00 00 02 00 00"), + + Certificate = + hexstr2bin("0b 00 03 2e 00 00 03 2a 00 03 25 30 82 03 21 30 + 82 02 09 a0 03 02 01 02 02 08 15 5a 92 ad c2 04 + 8f 90 30 0d 06 09 2a 86 48 86 f7 0d 01 01 0b 05 + 00 30 22 31 0b 30 09 06 03 55 04 06 13 02 55 53 + 31 13 30 11 06 03 55 04 0a 13 0a 45 78 61 6d 70 + 6c 65 20 43 41 30 1e 17 0d 31 38 31 30 30 35 30 + 31 33 38 31 37 5a 17 0d 31 39 31 30 30 35 30 31 + 33 38 31 37 5a 30 2b 31 0b 30 09 06 03 55 04 06 + 13 02 55 53 31 1c 30 1a 06 03 55 04 03 13 13 65 + 78 61 6d 70 6c 65 2e 75 6c 66 68 65 69 6d 2e 6e + 65 74 30 82 01 22 30 0d 06 09 2a 86 48 86 f7 0d + 01 01 01 05 00 03 82 01 0f 00 30 82 01 0a 02 82 + 01 01 00 c4 80 36 06 ba e7 47 6b 08 94 04 ec a7 + b6 91 04 3f f7 92 bc 19 ee fb 7d 74 d7 a8 0d 00 + 1e 7b 4b 3a 4a e6 0f e8 c0 71 fc 73 e7 02 4c 0d + bc f4 bd d1 1d 39 6b ba 70 46 4a 13 e9 4a f8 3d + f3 e1 09 59 54 7b c9 55 fb 41 2d a3 76 52 11 e1 + f3 dc 77 6c aa 53 37 6e ca 3a ec be c3 aa b7 3b + 31 d5 6c b6 52 9c 80 98 bc c9 e0 28 18 e2 0b f7 + f8 a0 3a fd 17 04 50 9e ce 79 bd 9f 39 f1 ea 69 + ec 47 97 2e 83 0f b5 ca 95 de 95 a1 e6 04 22 d5 + ee be 52 79 54 a1 e7 bf 8a 86 f6 46 6d 0d 9f 16 + 95 1a 4c f7 a0 46 92 59 5c 13 52 f2 54 9e 5a fb + 4e bf d7 7a 37 95 01 44 e4 c0 26 87 4c 65 3e 40 + 7d 7d 23 07 44 01 f4 84 ff d0 8f 7a 1f a0 52 10 + d1 f4 f0 d5 ce 79 70 29 32 e2 ca be 70 1f df ad + 6b 4b b7 11 01 f4 4b ad 66 6a 11 13 0f e2 ee 82 + 9e 4d 02 9d c9 1c dd 67 16 db b9 06 18 86 ed c1 + ba 94 21 02 03 01 00 01 a3 52 30 50 30 0e 06 03 + 55 1d 0f 01 01 ff 04 04 03 02 05 a0 30 1d 06 03 + 55 1d 25 04 16 30 14 06 08 2b 06 01 05 05 07 03 + 02 06 08 2b 06 01 05 05 07 03 01 30 1f 06 03 55 + 1d 23 04 18 30 16 80 14 89 4f de 5b cc 69 e2 52 + cf 3e a3 00 df b1 97 b8 1d e1 c1 46 30 0d 06 09 + 2a 86 48 86 f7 0d 01 01 0b 05 00 03 82 01 01 00 + 59 16 45 a6 9a 2e 37 79 e4 f6 dd 27 1a ba 1c 0b + fd 6c d7 55 99 b5 e7 c3 6e 53 3e ff 36 59 08 43 + 24 c9 e7 a5 04 07 9d 39 e0 d4 29 87 ff e3 eb dd + 09 c1 cf 1d 91 44 55 87 0b 57 1d d1 9b df 1d 24 + f8 bb 9a 11 fe 80 fd 59 2b a0 39 8c de 11 e2 65 + 1e 61 8c e5 98 fa 96 e5 37 2e ef 3d 24 8a fd e1 + 74 63 eb bf ab b8 e4 d1 ab 50 2a 54 ec 00 64 e9 + 2f 78 19 66 0d 3f 27 cf 20 9e 66 7f ce 5a e2 e4 + ac 99 c7 c9 38 18 f8 b2 51 07 22 df ed 97 f3 2e + 3e 93 49 d4 c6 6c 9e a6 39 6d 74 44 62 a0 6b 42 + c6 d5 ba 68 8e ac 3a 01 7b dd fc 8e 2c fc ad 27 + cb 69 d3 cc dc a2 80 41 44 65 d3 ae 34 8c e0 f3 + 4a b2 fb 9c 61 83 71 31 2b 19 10 41 64 1c 23 7f + 11 a5 d6 5c 84 4f 04 04 84 99 38 71 2b 95 9e d6 + 85 bc 5c 5d d6 45 ed 19 90 94 73 40 29 26 dc b4 + 0e 34 69 a1 59 41 e8 e2 cc a8 4b b6 08 46 36 a0 + 00 00"), + + CertificateVerify = + hexstr2bin("0f 00 01 04 08 04 01 00 17 fe b5 33 ca 6d 00 7d + 00 58 25 79 68 42 4b bc 3a a6 90 9e 9d 49 55 75 + 76 a5 20 e0 4a 5e f0 5f 0e 86 d2 4f f4 3f 8e b8 + 61 ee f5 95 22 8d 70 32 aa 36 0f 71 4e 66 74 13 + 92 6e f4 f8 b5 80 3b 69 e3 55 19 e3 b2 3f 43 73 + df ac 67 87 06 6d cb 47 56 b5 45 60 e0 88 6e 9b + 96 2c 4a d2 8d ab 26 ba d1 ab c2 59 16 b0 9a f2 + 86 53 7f 68 4f 80 8a ef ee 73 04 6c b7 df 0a 84 + fb b5 96 7a ca 13 1f 4b 1c f3 89 79 94 03 a3 0c + 02 d2 9c bd ad b7 25 12 db 9c ec 2e 5e 1d 00 e5 + 0c af cf 6f 21 09 1e bc 4f 25 3c 5e ab 01 a6 79 + ba ea be ed b9 c9 61 8f 66 00 6b 82 44 d6 62 2a + aa 56 88 7c cf c6 6a 0f 38 51 df a1 3a 78 cf f7 + 99 1e 03 cb 2c 3a 0e d8 7d 73 67 36 2e b7 80 5b + 00 b2 52 4f f2 98 a4 da 48 7c ac de af 8a 23 36 + c5 63 1b 3e fa 93 5b b4 11 e7 53 ca 13 b0 15 fe + c7 e4 a7 30 f1 36 9f 9e"), + + BaseKey = + hexstr2bin("a2 06 72 65 e7 f0 65 2a 92 3d 5d 72 ab 04 67 c4 + 61 32 ee b9 68 b6 a3 2d 31 1c 80 58 68 54 88 14"), + + VerifyData = + hexstr2bin("ea 6e e1 76 dc cc 4a f1 85 9e 9e 4e 93 f7 97 ea + c9 a7 8c e4 39 30 1e 35 27 5a d4 3f 3c dd bd e3"), + + Messages = [CertificateVerify, + Certificate, + EncryptedExtensions, + ServerHello, + ClientHello], + + FinishedKey = tls_v1:finished_key(BaseKey, sha256), + VerifyData = tls_v1:finished_verify_data(FinishedKey, sha256, Messages). + +tls13_basic_ssl_server_openssl_client() -> + [{doc,"Test TLS 1.3 basic connection between ssl server and openssl s_client"}]. + +tls13_basic_ssl_server_openssl_client(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), + %% Set versions + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ServerOpts0], + {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts), + + ssl_test_lib:check_result(Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close_port(Client). + +tls13_custom_groups_ssl_server_openssl_client() -> + [{doc,"Test that ssl server can select a common group for key-exchange"}]. + +tls13_custom_groups_ssl_server_openssl_client(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), + %% Set versions + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {supported_groups, [x448, secp256r1, secp384r1]}|ServerOpts0], + {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + ClientOpts = [{groups,"P-384:P-256:X25519"}|ClientOpts0], + Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts), + + ssl_test_lib:check_result(Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close_port(Client). + +tls13_hello_retry_request_ssl_server_openssl_client() -> + [{doc,"Test that ssl server can request a new group when the client's first key share" + "is not supported"}]. + +tls13_hello_retry_request_ssl_server_openssl_client(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), + %% Set versions + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {supported_groups, [x448, x25519]}|ServerOpts0], + {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + ClientOpts = [{groups,"P-256:X25519"}|ClientOpts0], + Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts), + + ssl_test_lib:check_result(Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close_port(Client). + +tls13_client_auth_empty_cert_alert_ssl_server_openssl_client() -> + [{doc,"TLS 1.3: Test client authentication when client sends an empty certificate and fail_if_no_peer_cert is set to true."}]. + +tls13_client_auth_empty_cert_alert_ssl_server_openssl_client(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), + %% Delete Client Cert and Key + ClientOpts1 = proplists:delete(certfile, ClientOpts0), + ClientOpts = proplists:delete(keyfile, ClientOpts1), + + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), + %% Set versions + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {verify, verify_peer}, + {fail_if_no_peer_cert, true}|ServerOpts0], + {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts), + + ssl_test_lib:check_result(Server, + {error, + {tls_alert, + {certificate_required, + "received SERVER ALERT: Fatal - Certificate required - certificate_required"}}}), + ssl_test_lib:close(Server), + ssl_test_lib:close_port(Client). + +tls13_client_auth_empty_cert_ssl_server_openssl_client() -> + [{doc,"TLS 1.3: Test client authentication when client sends an empty certificate and fail_if_no_peer_cert is set to false."}]. + +tls13_client_auth_empty_cert_ssl_server_openssl_client(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), + %% Delete Client Cert and Key + ClientOpts1 = proplists:delete(certfile, ClientOpts0), + ClientOpts = proplists:delete(keyfile, ClientOpts1), + + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), + %% Set versions + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {verify, verify_peer}, + {fail_if_no_peer_cert, false}|ServerOpts0], + {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts), + + ssl_test_lib:check_result(Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close_port(Client). + + +tls13_client_auth_ssl_server_openssl_client() -> + [{doc,"TLS 1.3: Test client authentication."}]. + +tls13_client_auth_ssl_server_openssl_client(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), + %% Set versions + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {verify, verify_peer}, + {fail_if_no_peer_cert, true}|ServerOpts0], + {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts), + + ssl_test_lib:check_result(Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close_port(Client). + + +tls13_hrr_client_auth_empty_cert_alert_ssl_server_openssl_client() -> + [{doc,"TLS 1.3 (HelloRetryRequest): Test client authentication when client sends an empty certificate and fail_if_no_peer_cert is set to true."}]. + +tls13_hrr_client_auth_empty_cert_alert_ssl_server_openssl_client(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), + %% Delete Client Cert and Key + ClientOpts1 = proplists:delete(certfile, ClientOpts0), + ClientOpts2 = proplists:delete(keyfile, ClientOpts1), + ClientOpts = [{groups,"P-256:X25519"}|ClientOpts2], + + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), + %% Set versions + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {verify, verify_peer}, + {fail_if_no_peer_cert, true}, + {supported_groups, [x448, x25519]}|ServerOpts0], + {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts), + + ssl_test_lib:check_result(Server, + {error, + {tls_alert, + {certificate_required, + "received SERVER ALERT: Fatal - Certificate required - certificate_required"}}}), + ssl_test_lib:close(Server), + ssl_test_lib:close_port(Client). + + +tls13_hrr_client_auth_empty_cert_ssl_server_openssl_client() -> + [{doc,"TLS 1.3 (HelloRetryRequest): Test client authentication when client sends an empty certificate and fail_if_no_peer_cert is set to false."}]. + +tls13_hrr_client_auth_empty_cert_ssl_server_openssl_client(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), + %% Delete Client Cert and Key + ClientOpts1 = proplists:delete(certfile, ClientOpts0), + ClientOpts2 = proplists:delete(keyfile, ClientOpts1), + ClientOpts = [{groups,"P-256:X25519"}|ClientOpts2], + + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), + %% Set versions + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {verify, verify_peer}, + {fail_if_no_peer_cert, false}, + {supported_groups, [x448, x25519]}|ServerOpts0], + {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts), + + ssl_test_lib:check_result(Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close_port(Client). + + +tls13_hrr_client_auth_ssl_server_openssl_client() -> + [{doc,"TLS 1.3 (HelloRetryRequest): Test client authentication."}]. + +tls13_hrr_client_auth_ssl_server_openssl_client(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ClientOpts = [{groups,"P-256:X25519"}|ClientOpts0], + + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), + %% Set versions + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {verify, verify_peer}, + {fail_if_no_peer_cert, true}, + {supported_groups, [x448, x25519]}|ServerOpts0], + {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts), + + ssl_test_lib:check_result(Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close_port(Client). + + +tls13_unsupported_sign_algo_client_auth_ssl_server_openssl_client() -> + [{doc,"TLS 1.3: Test client authentication with unsupported signature_algorithm"}]. + +tls13_unsupported_sign_algo_client_auth_ssl_server_openssl_client(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), + %% Set versions + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {verify, verify_peer}, + %% Skip rsa_pkcs1_sha256! + {signature_algs, [rsa_pkcs1_sha384, rsa_pkcs1_sha512]}, + {fail_if_no_peer_cert, true}|ServerOpts0], + {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts), + + ssl_test_lib:check_result( + Server, + {error, + {tls_alert, + {insufficient_security, + "received SERVER ALERT: Fatal - Insufficient Security - " + "\"No suitable signature algorithm\""}}}), + ssl_test_lib:close(Server), + ssl_test_lib:close_port(Client). + + +%% Triggers Client Alert as openssl s_client does not have a certificate with a +%% signature algorithm supported by the server (signature_algorithms_cert extension +%% of CertificateRequest does not contain the algorithm of the client certificate). +tls13_unsupported_sign_algo_cert_client_auth_ssl_server_openssl_client() -> + [{doc,"TLS 1.3: Test client authentication with unsupported signature_algorithm_cert"}]. + +tls13_unsupported_sign_algo_cert_client_auth_ssl_server_openssl_client(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), + %% Set versions + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {log_level, debug}, + {verify, verify_peer}, + {signature_algs, [rsa_pkcs1_sha256, rsa_pkcs1_sha384, rsa_pss_rsae_sha256]}, + %% Skip rsa_pkcs1_sha256! + {signature_algs_cert, [rsa_pkcs1_sha384, rsa_pkcs1_sha512]}, + {fail_if_no_peer_cert, true}|ServerOpts0], + {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts), + + ssl_test_lib:check_result( + Server, + {error, + {tls_alert, + {illegal_parameter, + "received CLIENT ALERT: Fatal - Illegal Parameter"}}}), + ssl_test_lib:close(Server), + ssl_test_lib:close_port(Client). + + +tls13_connection_information() -> + [{doc,"Test the API function ssl:connection_information/1 in a TLS 1.3 connection"}]. + +tls13_connection_information(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), + %% Set versions + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ServerOpts0], + {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, connection_information_result, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts), + + ssl_test_lib:check_result(Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close_port(Client). + %%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ @@ -5046,147 +6135,6 @@ client_server_opts(#{key_exchange := KeyAlgo}, Config) when KeyAlgo == ecdh_rsa {ssl_test_lib:ssl_options(client_opts, Config), ssl_test_lib:ssl_options(server_ecdh_rsa_opts, Config)}. -run_suites(Ciphers, Config, Type) -> - Version = ssl_test_lib:protocol_version(Config), - ct:log("Running cipher suites ~p~n", [Ciphers]), - {ClientOpts, ServerOpts} = - case Type of - rsa -> - {ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), - [{ciphers, Ciphers} | - ssl_test_lib:ssl_options(server_rsa_opts, Config)]}; - dsa -> - {ssl_test_lib:ssl_options(client_dsa_verify_opts, Config), - [{ciphers, Ciphers} | - ssl_test_lib:ssl_options(server_dsa_opts, Config)]}; - anonymous -> - %% No certs in opts! - {ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), - [{ciphers, Ciphers} | - ssl_test_lib:ssl_options([], Config)]}; - psk -> - {ssl_test_lib:ssl_options(client_psk, Config), - [{ciphers, Ciphers} | - ssl_test_lib:ssl_options(server_psk, Config)]}; - psk_with_hint -> - {ssl_test_lib:ssl_options(client_psk, Config), - [{ciphers, Ciphers} | - ssl_test_lib:ssl_options(server_psk_hint, Config) - ]}; - psk_anon -> - {ssl_test_lib:ssl_options(client_psk, Config), - [{ciphers, Ciphers} | - ssl_test_lib:ssl_options(server_psk_anon, Config)]}; - psk_anon_with_hint -> - {ssl_test_lib:ssl_options(client_psk, Config), - [{ciphers, Ciphers} | - ssl_test_lib:ssl_options(server_psk_anon_hint, Config)]}; - srp -> - {ssl_test_lib:ssl_options(client_srp, Config), - [{ciphers, Ciphers} | - ssl_test_lib:ssl_options(server_srp, Config)]}; - srp_anon -> - {ssl_test_lib:ssl_options(client_srp, Config), - [{ciphers, Ciphers} | - ssl_test_lib:ssl_options(server_srp_anon, Config)]}; - srp_dsa -> - {ssl_test_lib:ssl_options(client_srp_dsa, Config), - [{ciphers, Ciphers} | - ssl_test_lib:ssl_options(server_srp_dsa, Config)]}; - ecdsa -> - {ssl_test_lib:ssl_options(client_ecdsa_opts, Config), - [{ciphers, Ciphers} | - ssl_test_lib:ssl_options(server_ecdsa_opts, Config)]}; - ecdh_rsa -> - {ssl_test_lib:ssl_options(client_ecdh_rsa_opts, Config), - [{ciphers, Ciphers} | - ssl_test_lib:ssl_options(server_ecdh_rsa_opts, Config)]}; - rc4_rsa -> - {ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), - [{ciphers, Ciphers} | - ssl_test_lib:ssl_options(server_rsa_verify_opts, Config)]}; - rc4_ecdh_rsa -> - {ssl_test_lib:ssl_options(client_ecdh_rsa_opts, Config), - [{ciphers, Ciphers} | - ssl_test_lib:ssl_options(server_ecdh_rsa_opts, Config)]}; - rc4_ecdsa -> - {ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), - [{ciphers, Ciphers} | - ssl_test_lib:ssl_options(server_ecdsa_opts, Config)]}; - des_dhe_rsa -> - {ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), - [{ciphers, Ciphers} | - ssl_test_lib:ssl_options(server_verification_opts, Config)]}; - des_rsa -> - {ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), - [{ciphers, Ciphers} | - ssl_test_lib:ssl_options(server_rsa_verify_opts, Config)]}; - chacha_rsa -> - {ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), - [{ciphers, Ciphers} | - ssl_test_lib:ssl_options(server_rsa_verify_opts, Config)]}; - chacha_ecdsa -> - {ssl_test_lib:ssl_options(client_ecdsa_opts, Config), - [{ciphers, Ciphers} | - ssl_test_lib:ssl_options(server_ecdsa_opts, Config)]} - end, - Suites = ssl_test_lib:filter_suites(Ciphers, Version), - ct:pal("ssl_test_lib:filter_suites(~p ~p) -> ~p ", [Ciphers, Version, Suites]), - Results0 = lists:map(fun(Cipher) -> - cipher(Cipher, Version, Config, ClientOpts, ServerOpts) end, - ssl_test_lib:filter_suites(Ciphers, Version)), - Results = lists:flatten(Results0), - true = length(Results) == length(Suites), - check_cipher_result(Results). - -check_cipher_result([]) -> - ok; -check_cipher_result([ok | Rest]) -> - check_cipher_result(Rest); -check_cipher_result([_ |_] = Error) -> - ct:fail(Error). - -erlang_cipher_suite(Suite) when is_list(Suite)-> - ssl_cipher_format:suite_definition(ssl_cipher_format:openssl_suite(Suite)); -erlang_cipher_suite(Suite) -> - Suite. - -cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) -> - %% process_flag(trap_exit, true), - ct:log("Testing CipherSuite ~p~n", [CipherSuite]), - ct:log("Server Opts ~p~n", [ServerOpts]), - ct:log("Client Opts ~p~n", [ClientOpts]), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - ErlangCipherSuite = erlang_cipher_suite(CipherSuite), - - ConnectionInfo = {ok, {Version, ErlangCipherSuite}}, - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, cipher_result, [ConnectionInfo]}}, - {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, cipher_result, [ConnectionInfo]}}, - {options, - [{ciphers,[CipherSuite]} | - ClientOpts]}]), - - Result = ssl_test_lib:wait_for_result(Server, ok, Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client), - - case Result of - ok -> - [ok]; - Error -> - [{ErlangCipherSuite, Error}] - end. - connection_information_result(Socket) -> {ok, Info = [_ | _]} = ssl:connection_information(Socket), case length(Info) > 3 of @@ -5350,3 +6298,31 @@ tls_or_dtls('dtlsv1.2') -> dtls; tls_or_dtls(_) -> tls. + +hexstr2int(S) -> + B = hexstr2bin(S), + Bits = size(B) * 8, + <<Integer:Bits/integer>> = B, + Integer. + +hexstr2bin(S) when is_binary(S) -> + hexstr2bin(S, <<>>); +hexstr2bin(S) -> + hexstr2bin(list_to_binary(S), <<>>). +%% +hexstr2bin(<<>>, Acc) -> + Acc; +hexstr2bin(<<C,T/binary>>, Acc) when C =:= 32; %% SPACE + C =:= 10; %% LF + C =:= 13 -> %% CR + hexstr2bin(T, Acc); +hexstr2bin(<<X,Y,T/binary>>, Acc) -> + I = hex2int(X) * 16 + hex2int(Y), + hexstr2bin(T, <<Acc/binary,I>>). + +hex2int(C) when $0 =< C, C =< $9 -> + C - $0; +hex2int(C) when $A =< C, C =< $F -> + C - $A + 10; +hex2int(C) when $a =< C, C =< $f -> + C - $a + 10. diff --git a/lib/ssl/test/ssl_bench.spec b/lib/ssl/test/ssl_bench.spec index 8b746c5ca9..217cc6fc83 100644 --- a/lib/ssl/test/ssl_bench.spec +++ b/lib/ssl/test/ssl_bench.spec @@ -1 +1,2 @@ {suites,"../ssl_test",[ssl_bench_SUITE, ssl_dist_bench_SUITE]}. +{skip_groups,"../ssl_test",ssl_bench_SUITE,basic,"Benchmarks run separately"}. diff --git a/lib/ssl/test/ssl_bench_SUITE.erl b/lib/ssl/test/ssl_bench_SUITE.erl index 13097b08b6..35efa2b8a3 100644 --- a/lib/ssl/test/ssl_bench_SUITE.erl +++ b/lib/ssl/test/ssl_bench_SUITE.erl @@ -25,10 +25,11 @@ suite() -> [{ct_hooks,[{ts_install_cth,[{nodenames,2}]}]}]. -all() -> [{group, setup}, {group, payload}, {group, pem_cache}]. +all() -> [{group, basic}, {group, setup}, {group, payload}, {group, pem_cache}]. groups() -> - [{setup, [{repeat, 3}], [setup_sequential, setup_concurrent]}, + [{basic, [], [basic_pem_cache]}, + {setup, [{repeat, 3}], [setup_sequential, setup_concurrent]}, {payload, [{repeat, 3}], [payload_simple]}, {pem_cache, [{repeat, 3}], [use_pem_cache, bypass_pem_cache]} ]. @@ -40,6 +41,7 @@ end_per_group(_GroupName, _Config) -> ok. init_per_suite(Config) -> + ct:timetrap({minutes, 1}), case node() of nonode@nohost -> {skipped, "Node not distributed"}; @@ -51,29 +53,21 @@ init_per_suite(Config) -> end_per_suite(_Config) -> ok. -init_per_testcase(use_pem_cache, Conf) -> +init_per_testcase(TC, Conf) when TC =:= use_pem_cache; + TC =:= bypass_pem_cache; + TC =:= basic_pem_cache -> case bypass_pem_cache_supported() of false -> {skipped, "PEM cache bypass support required"}; true -> application:set_env(ssl, bypass_pem_cache, false), Conf end; -init_per_testcase(bypass_pem_cache, Conf) -> - case bypass_pem_cache_supported() of - false -> {skipped, "PEM cache bypass support required"}; - true -> - application:set_env(ssl, bypass_pem_cache, true), - Conf - end; init_per_testcase(_Func, Conf) -> Conf. -end_per_testcase(use_pem_cache, _Config) -> - case bypass_pem_cache_supported() of - false -> ok; - true -> application:set_env(ssl, bypass_pem_cache, false) - end; -end_per_testcase(bypass_pem_cache, _Config) -> +end_per_testcase(TC, _Config) when TC =:= use_pem_cache; + TC =:= bypass_pem_cache; + TC =:= basic_pem_cache -> case bypass_pem_cache_supported() of false -> ok; true -> application:set_env(ssl, bypass_pem_cache, false) @@ -119,6 +113,9 @@ payload_simple(Config) -> {suite, "ssl"}, {name, "Payload simple"}]}), ok. +basic_pem_cache(_Config) -> + do_test(ssl, pem_cache, 10, 5, node()). + use_pem_cache(_Config) -> {ok, Result} = do_test(ssl, pem_cache, 100, 500, node()), ct_event:notify(#event{name = benchmark_data, @@ -167,7 +164,7 @@ do_test(Type, TC, Loop, ParallellConnections, Server) -> end end, Spawn = fun(Id) -> - Pid = spawn(fun() -> Test(Id) end), + Pid = spawn_link(fun() -> Test(Id) end), receive {Pid, init} -> Pid end end, Pids = [Spawn(Id) || Id <- lists:seq(ParallellConnections, 1, -1)], @@ -184,42 +181,42 @@ do_test(Type, TC, Loop, ParallellConnections, Server) -> {ok, TestPerSecond}. server_init(ssl, setup_connection, _, _, Server) -> - {ok, Socket} = ssl:listen(0, ssl_opts(listen)), - {ok, {_Host, Port}} = ssl:sockname(Socket), + {ok, LSocket} = ssl:listen(0, ssl_opts(listen)), + {ok, {_Host, Port}} = ssl:sockname(LSocket), {ok, Host} = inet:gethostname(), ?FPROF_SERVER andalso start_profile(fprof, [whereis(ssl_manager), new]), %%?EPROF_SERVER andalso start_profile(eprof, [ssl_connection_sup, ssl_manager]), ?EPROF_SERVER andalso start_profile(eprof, [ssl_manager]), Server ! {self(), {init, Host, Port}}, Test = fun(TSocket) -> - ok = ssl:ssl_accept(TSocket), - ssl:close(TSocket) + {ok, Socket} = ssl:handshake(TSocket), + ssl:close(Socket) end, - setup_server_connection(Socket, Test); + setup_server_connection(LSocket, Test); server_init(ssl, payload, Loop, _, Server) -> - {ok, Socket} = ssl:listen(0, ssl_opts(listen)), - {ok, {_Host, Port}} = ssl:sockname(Socket), + {ok, LSocket} = ssl:listen(0, ssl_opts(listen)), + {ok, {_Host, Port}} = ssl:sockname(LSocket), {ok, Host} = inet:gethostname(), Server ! {self(), {init, Host, Port}}, Test = fun(TSocket) -> - ok = ssl:ssl_accept(TSocket), + {ok, Socket} = ssl:handshake(TSocket), Size = byte_size(msg()), - server_echo(TSocket, Size, Loop), - ssl:close(TSocket) + server_echo(Socket, Size, Loop), + ssl:close(Socket) end, - setup_server_connection(Socket, Test); + setup_server_connection(LSocket, Test); server_init(ssl, pem_cache, Loop, _, Server) -> - {ok, Socket} = ssl:listen(0, ssl_opts(listen_der)), - {ok, {_Host, Port}} = ssl:sockname(Socket), + {ok, LSocket} = ssl:listen(0, ssl_opts(listen_der)), + {ok, {_Host, Port}} = ssl:sockname(LSocket), {ok, Host} = inet:gethostname(), Server ! {self(), {init, Host, Port}}, Test = fun(TSocket) -> - ok = ssl:ssl_accept(TSocket), + {ok, Socket} = ssl:handshake(TSocket), Size = byte_size(msg()), - server_echo(TSocket, Size, Loop), - ssl:close(TSocket) + server_echo(Socket, Size, Loop), + ssl:close(Socket) end, - setup_server_connection(Socket, Test); + setup_server_connection(LSocket, Test); server_init(Type, Tc, _, _, Server) -> io:format("No server init code for ~p ~p~n",[Type, Tc]), diff --git a/lib/ssl/test/ssl_certificate_verify_SUITE.erl b/lib/ssl/test/ssl_certificate_verify_SUITE.erl index c0a5367a57..55dee9a48f 100644 --- a/lib/ssl/test/ssl_certificate_verify_SUITE.erl +++ b/lib/ssl/test/ssl_certificate_verify_SUITE.erl @@ -147,6 +147,7 @@ init_per_testcase(_TestCase, Config) -> ssl:stop(), ssl:start(), ssl_test_lib:ct_log_supported_protocol_versions(Config), + ct:pal(" ~p", [ dtls_record:supported_protocol_versions()]), ct:timetrap({seconds, 10}), Config. @@ -447,7 +448,7 @@ server_require_peer_cert_partial_chain_fun_fail(Config) when is_list(Config) -> [{_,_,_}, {_, IntermidiateCA, _} | _] = public_key:pem_decode(ServerCAs), PartialChain = fun(_CertChain) -> - ture = false %% crash on purpose + true = false %% crash on purpose end, Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, @@ -481,7 +482,7 @@ verify_fun_always_run_client(Config) when is_list(Config) -> Port = ssl_test_lib:inet_port(Server), %% If user verify fun is called correctly we fail the connection. - %% otherwise we can not tell this case apart form where we miss + %% otherwise we cannot tell this case apart form where we miss %% to call users verify fun FunAndState = {fun(_,{extension, _}, UserState) -> {unknown, UserState}; @@ -514,7 +515,7 @@ verify_fun_always_run_server(Config) when is_list(Config) -> {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), %% If user verify fun is called correctly we fail the connection. - %% otherwise we can not tell this case apart form where we miss + %% otherwise we cannot tell this case apart form where we miss %% to call users verify fun FunAndState = {fun(_,{extension, _}, UserState) -> {unknown, UserState}; diff --git a/lib/ssl/test/ssl_cipher_suite_SUITE.erl b/lib/ssl/test/ssl_cipher_suite_SUITE.erl new file mode 100644 index 0000000000..8805df7b52 --- /dev/null +++ b/lib/ssl/test/ssl_cipher_suite_SUITE.erl @@ -0,0 +1,772 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2019-2019. 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_cipher_suite_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). + +%%-------------------------------------------------------------------- +%% Common Test interface functions ----------------------------------- +%%-------------------------------------------------------------------- +all() -> + [ + {group, 'tlsv1.2'}, + {group, 'tlsv1.1'}, + {group, 'tlsv1'}, + {group, 'sslv3'}, + {group, 'dtlsv1.2'}, + {group, 'dtlsv1'} + ]. + +groups() -> + [ + {'tlsv1.2', [], kex()}, + {'tlsv1.1', [], kex()}, + {'tlsv1', [], kex()}, + {'sslv3', [], kex()}, + {'dtlsv1.2', [], kex()}, + {'dtlsv1', [], kex()}, + {dhe_rsa, [],[dhe_rsa_3des_ede_cbc, + dhe_rsa_aes_128_cbc, + dhe_rsa_aes_256_cbc, + dhe_rsa_chacha20_poly1305 + ]}, + {ecdhe_rsa, [], [ecdhe_rsa_3des_ede_cbc, + ecdhe_rsa_aes_128_cbc, + ecdhe_rsa_aes_128_gcm, + ecdhe_rsa_aes_256_cbc, + ecdhe_rsa_aes_256_gcm, + ecdhe_rsa_chacha20_poly1305 + ]}, + {ecdhe_ecdsa, [],[ecdhe_ecdsa_rc4_128, + ecdhe_ecdsa_3des_ede_cbc, + ecdhe_ecdsa_aes_128_cbc, + ecdhe_ecdsa_aes_128_gcm, + ecdhe_ecdsa_aes_256_cbc, + ecdhe_ecdsa_aes_256_gcm, + ecdhe_ecdsa_chacha20_poly1305 + ]}, + {rsa, [], [rsa_3des_ede_cbc, + rsa_aes_128_cbc, + rsa_aes_256_cbc, + rsa_rc4_128 + ]}, + {dhe_dss, [], [dhe_dss_3des_ede_cbc, + dhe_dss_aes_128_cbc, + dhe_dss_aes_256_cbc]}, + {srp_rsa, [], [srp_rsa_3des_ede_cbc, + srp_rsa_aes_128_cbc, + srp_rsa_aes_256_cbc]}, + {srp_dss, [], [srp_dss_3des_ede_cbc, + srp_dss_aes_128_cbc, + srp_dss_aes_256_cbc]}, + {rsa_psk, [], [rsa_psk_3des_ede_cbc, + rsa_psk_rc4_128, + rsa_psk_aes_128_cbc, + rsa_psk_aes_256_cbc + ]}, + {dh_anon, [], [dh_anon_rc4_128, + dh_anon_3des_ede_cbc, + dh_anon_aes_128_cbc, + dh_anon_aes_128_gcm, + dh_anon_aes_256_cbc, + dh_anon_aes_256_gcm]}, + {ecdh_anon, [], [ecdh_anon_3des_ede_cbc, + ecdh_anon_aes_128_cbc, + ecdh_anon_aes_256_cbc + ]}, + {srp_anon, [], [srp_anon_3des_ede_cbc, + srp_anon_aes_128_cbc, + srp_anon_aes_256_cbc]}, + {psk, [], [psk_3des_ede_cbc, + psk_rc4_128, + psk_aes_128_cbc, + psk_aes_128_ccm, + psk_aes_128_ccm_8, + psk_aes_256_cbc, + psk_aes_256_ccm, + psk_aes_256_ccm_8 + ]}, + {dhe_psk, [], [dhe_psk_3des_ede_cbc, + dhe_psk_rc4_128, + dhe_psk_aes_128_cbc, + dhe_psk_aes_128_ccm, + dhe_psk_aes_128_ccm_8, + dhe_psk_aes_256_cbc, + dhe_psk_aes_256_ccm, + dhe_psk_aes_256_ccm_8 + ]}, + {ecdhe_psk, [], [ecdhe_psk_3des_ede_cbc, + ecdhe_psk_rc4_128, + ecdhe_psk_aes_128_cbc, + ecdhe_psk_aes_128_ccm, + ecdhe_psk_aes_128_ccm_8, + ecdhe_psk_aes_256_cbc + ]} + ]. + + +kex() -> + rsa() ++ ecdsa() ++ dss() ++ anonymous(). + +rsa() -> + [{group, dhe_rsa}, + {group, ecdhe_rsa}, + {group, rsa}, + {group, srp_rsa}, + {group, rsa_psk} + ]. + +ecdsa() -> + [{group, ecdhe_ecdsa}]. + +dss() -> + [{group, dhe_dss}, + {group, srp_dss}]. + +anonymous() -> + [{group, dh_anon}, + {group, ecdh_anon}, + {group, psk}, + {group, dhe_psk}, + {group, ecdhe_psk}, + {group, srp_anon} + ]. + + +init_per_suite(Config) -> + catch crypto:stop(), + try crypto:start() of + ok -> + ssl_test_lib:clean_start(), + Config + catch _:_ -> + {skip, "Crypto did not start"} + end. + +end_per_suite(_Config) -> + ssl:stop(), + application:stop(crypto). + +%%-------------------------------------------------------------------- +init_per_group(GroupName, Config) when GroupName == ecdh_anon; + GroupName == ecdhe_rsa; + GroupName == ecdhe_psk -> + case proplists:get_bool(ecdh, proplists:get_value(public_keys, crypto:supports())) of + true -> + init_certs(GroupName, Config); + false -> + {skip, "Missing EC crypto support"} + end; +init_per_group(ecdhe_ecdsa = GroupName, Config) -> + PKAlg = proplists:get_value(public_keys, crypto:supports()), + case lists:member(ecdh, PKAlg) andalso lists:member(ecdsa, PKAlg) of + true -> + init_certs(GroupName, Config); + false -> + {skip, "Missing EC crypto support"} + end; +init_per_group(dhe_dss = GroupName, Config) -> + PKAlg = proplists:get_value(public_keys, crypto:supports()), + case lists:member(dss, PKAlg) andalso lists:member(dh, PKAlg) of + true -> + init_certs(GroupName, Config); + false -> + {skip, "Missing DSS crypto support"} + end; +init_per_group(srp_dss = GroupName, Config) -> + PKAlg = proplists:get_value(public_keys, crypto:supports()), + case lists:member(dss, PKAlg) andalso lists:member(srp, PKAlg) of + true -> + init_certs(GroupName, Config); + false -> + {skip, "Missing DSS_SRP crypto support"} + end; +init_per_group(GroupName, Config) when GroupName == srp_anon; + GroupName == srp_rsa -> + PKAlg = proplists:get_value(public_keys, crypto:supports()), + case lists:member(srp, PKAlg) of + true -> + init_certs(GroupName, Config); + false -> + {skip, "Missing SRP crypto support"} + end; +init_per_group(dhe_psk = GroupName, Config) -> + PKAlg = proplists:get_value(public_keys, crypto:supports()), + case lists:member(dh, PKAlg) of + true -> + init_certs(GroupName, Config); + false -> + {skip, "Missing SRP crypto support"} + end; +init_per_group(GroupName, Config0) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + ssl_test_lib:init_tls_version(GroupName, end_per_group(GroupName, Config0)); + false -> + init_certs(GroupName, Config0) + end. + +end_per_group(GroupName, Config) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + ssl_test_lib:clean_tls_version(Config); + false -> + Config + end. +init_per_testcase(TestCase, Config) when TestCase == psk_3des_ede_cbc; + TestCase == srp_anon_3des_ede_cbc; + TestCase == dhe_psk_3des_ede_cbc; + TestCase == ecdhe_psk_3des_ede_cbc; + TestCase == srp_rsa_3des_ede_cbc; + TestCase == srp_dss_3des_ede_cbc; + TestCase == rsa_psk_3des_ede_cbc; + TestCase == rsa_3des_ede_cbc; + TestCase == dhe_rsa_3des_ede_cbc; + TestCase == dhe_dss_3des_ede_cbc; + TestCase == ecdhe_rsa_3des_ede_cbc; + TestCase == srp_anon_dss_3des_ede_cbc; + TestCase == dh_anon_3des_ede_cbc; + TestCase == ecdh_anon_3des_ede_cbc; + TestCase == ecdhe_ecdsa_3des_ede_cbc -> + SupCiphers = proplists:get_value(ciphers, crypto:supports()), + case lists:member(des_ede3, SupCiphers) of + true -> + ct:timetrap({seconds, 5}), + Config; + _ -> + {skip, "Missing 3DES crypto support"} + end; +init_per_testcase(TestCase, Config) when TestCase == psk_rc4_128; + TestCase == ecdhe_psk_rc4_128; + TestCase == dhe_psk_rc4_128; + TestCase == rsa_psk_rc4_128; + TestCase == rsa_rc4_128; + TestCase == ecdhe_rsa_rc4_128; + TestCase == ecdhe_ecdsa_rc4_128; + TestCase == dh_anon_rc4_128 -> + SupCiphers = proplists:get_value(ciphers, crypto:supports()), + case lists:member(rc4, SupCiphers) of + true -> + ct:timetrap({seconds, 5}), + Config; + _ -> + {skip, "Missing RC4 crypto support"} + end; +init_per_testcase(TestCase, Config) when TestCase == psk_aes_128_ccm_8; + TestCase == rsa_psk_aes_128_ccm_8; + TestCase == psk_aes_128_ccm_8; + TestCase == dhe_psk_aes_128_ccm_8; + TestCase == ecdhe_psk_aes_128_ccm_8 -> + SupCiphers = proplists:get_value(ciphers, crypto:supports()), + case lists:member(aes_128_ccm, SupCiphers) of + true -> + ct:timetrap({seconds, 5}), + Config; + _ -> + {skip, "Missing AES_128_CCM crypto support"} + end; +init_per_testcase(TestCase, Config) when TestCase == psk_aes_256_ccm_8; + TestCase == rsa_psk_aes_256_ccm_8; + TestCase == psk_aes_256_ccm_8; + TestCase == dhe_psk_aes_256_ccm_8; + TestCase == ecdhe_psk_aes_256_ccm_8 -> + SupCiphers = proplists:get_value(ciphers, crypto:supports()), + case lists:member(aes_256_ccm, SupCiphers) of + true -> + ct:timetrap({seconds, 5}), + Config; + _ -> + {skip, "Missing AES_256_CCM crypto support"} + end; +init_per_testcase(TestCase, Config) -> + Cipher = test_cipher(TestCase, Config), + %%Reason = io_lib:format("Missing ~p crypto support", [Cipher]), + SupCiphers = proplists:get_value(ciphers, crypto:supports()), + case lists:member(Cipher, SupCiphers) of + true -> + ct:timetrap({seconds, 5}), + Config; + _ -> + {skip, {Cipher, SupCiphers}} + end. + +end_per_testcase(_TestCase, Config) -> + Config. + +init_certs(srp_rsa, Config) -> + DefConf = ssl_test_lib:default_cert_chain_conf(), + CertChainConf = ssl_test_lib:gen_conf(rsa, rsa, DefConf, DefConf), + #{server_config := ServerOpts, + client_config := ClientOpts} + = public_key:pkix_test_data(CertChainConf), + [{tls_config, #{server_config => [{user_lookup_fun, {fun user_lookup/3, undefined}} | ServerOpts], + client_config => [{srp_identity, {"Test-User", "secret"}} | ClientOpts]}} | + proplists:delete(tls_config, Config)]; +init_certs(srp_anon, Config) -> + [{tls_config, #{server_config => [{user_lookup_fun, {fun user_lookup/3, undefined}}], + client_config => [{srp_identity, {"Test-User", "secret"}}]}} | + proplists:delete(tls_config, Config)]; +init_certs(rsa_psk, Config) -> + ClientExt = x509_test:extensions([{key_usage, [digitalSignature, keyEncipherment]}]), + {ClientOpts, ServerOpts} = ssl_test_lib:make_rsa_cert_chains([{server_chain, + [[],[],[{extensions, ClientExt}]]}], + Config, "_peer_keyEncipherment"), + PskSharedSecret = <<1,2,3,4,5,6,7,8,9,10,11,12,13,14,15>>, + [{tls_config, #{server_config => [{user_lookup_fun, {fun user_lookup/3, PskSharedSecret}} | ServerOpts], + client_config => [{psk_identity, "Test-User"}, + {user_lookup_fun, {fun user_lookup/3, PskSharedSecret}} | ClientOpts]}} | + proplists:delete(tls_config, Config)]; +init_certs(rsa, Config) -> + ClientExt = x509_test:extensions([{key_usage, [digitalSignature, keyEncipherment]}]), + {ClientOpts, ServerOpts} = ssl_test_lib:make_rsa_cert_chains([{server_chain, + [[],[],[{extensions, ClientExt}]]}], + Config, "_peer_keyEncipherment"), + [{tls_config, #{server_config => ServerOpts, + client_config => ClientOpts}} | + proplists:delete(tls_config, Config)]; +init_certs(dhe_dss, Config) -> + DefConf = ssl_test_lib:default_cert_chain_conf(), + CertChainConf = ssl_test_lib:gen_conf(dsa, dsa, DefConf, DefConf), + #{server_config := ServerOpts, + client_config := ClientOpts} + = public_key:pkix_test_data(CertChainConf), + [{tls_config, #{server_config => ServerOpts, + client_config => ClientOpts}} | + proplists:delete(tls_config, Config)]; +init_certs(srp_dss, Config) -> + DefConf = ssl_test_lib:default_cert_chain_conf(), + CertChainConf = ssl_test_lib:gen_conf(dsa, dsa, DefConf, DefConf), + #{server_config := ServerOpts, + client_config := ClientOpts} + = public_key:pkix_test_data(CertChainConf), + [{tls_config, #{server_config => [{user_lookup_fun, {fun user_lookup/3, undefined}} | ServerOpts], + client_config => [{srp_identity, {"Test-User", "secret"}} | ClientOpts]}} | + proplists:delete(tls_config, Config)]; +init_certs(GroupName, Config) when GroupName == dhe_rsa; + GroupName == ecdhe_rsa -> + DefConf = ssl_test_lib:default_cert_chain_conf(), + CertChainConf = ssl_test_lib:gen_conf(rsa, rsa, DefConf, DefConf), + #{server_config := ServerOpts, + client_config := ClientOpts} + = public_key:pkix_test_data(CertChainConf), + [{tls_config, #{server_config => ServerOpts, + client_config => ClientOpts}} | + proplists:delete(tls_config, Config)]; +init_certs(GroupName, Config) when GroupName == dhe_ecdsa; + GroupName == ecdhe_ecdsa -> + DefConf = ssl_test_lib:default_cert_chain_conf(), + CertChainConf = ssl_test_lib:gen_conf(ecdsa, ecdsa, DefConf, DefConf), + #{server_config := ServerOpts, + client_config := ClientOpts} + = public_key:pkix_test_data(CertChainConf), + [{tls_config, #{server_config => ServerOpts, + client_config => ClientOpts}} | + proplists:delete(tls_config, Config)]; +init_certs(GroupName, Config) when GroupName == psk; + GroupName == dhe_psk; + GroupName == ecdhe_psk -> + PskSharedSecret = <<1,2,3,4,5,6,7,8,9,10,11,12,13,14,15>>, + [{tls_config, #{server_config => [{user_lookup_fun, {fun user_lookup/3, PskSharedSecret}}], + client_config => [{psk_identity, "Test-User"}, + {user_lookup_fun, {fun user_lookup/3, PskSharedSecret}}]}} | + proplists:delete(tls_config, Config)]; +init_certs(srp, Config) -> + [{tls_config, #{server_config => [{user_lookup_fun, {fun user_lookup/3, undefined}}], + client_config => [{srp_identity, {"Test-User", "secret"}}]}} | + proplists:delete(tls_config, Config)]; +init_certs(_GroupName, Config) -> + %% Anonymous does not need certs + [{tls_config, #{server_config => [], + client_config => []}} | + proplists:delete(tls_config, Config)]. +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- + +%%-------------------------------------------------------------------- +%% SRP -------------------------------------------------------- +%%-------------------------------------------------------------------- +srp_rsa_3des_ede_cbc(Config) when is_list(Config) -> + run_ciphers_test(srp_rsa, '3des_ede_cbc', Config). + +srp_rsa_aes_128_cbc(Config) when is_list(Config) -> + run_ciphers_test(srp_rsa, 'aes_128_cbc', Config). + +srp_rsa_aes_256_cbc(Config) when is_list(Config) -> + run_ciphers_test(srp_rsa, 'aes_256_cbc', Config). + +srp_dss_3des_ede_cbc(Config) when is_list(Config) -> + run_ciphers_test(srp_dss, '3des_ede_cbc', Config). + +srp_dss_aes_128_cbc(Config) when is_list(Config) -> + run_ciphers_test(srp_dss, 'aes_128_cbc', Config). + +srp_dss_aes_256_cbc(Config) when is_list(Config) -> + run_ciphers_test(srp_dss, 'aes_256_cbc', Config). + +%%-------------------------------------------------------------------- +%% PSK -------------------------------------------------------- +%%-------------------------------------------------------------------- +rsa_psk_3des_ede_cbc(Config) when is_list(Config) -> + run_ciphers_test(rsa_psk, '3des_ede_cbc', Config). + +rsa_psk_aes_128_cbc(Config) when is_list(Config) -> + run_ciphers_test(rsa_psk, 'aes_128_cbc', Config). + +rsa_psk_aes_128_ccm(Config) when is_list(Config) -> + run_ciphers_test(rsa_psk, 'aes_128_ccm', Config). + +rsa_psk_aes_128_ccm_8(Config) when is_list(Config) -> + run_ciphers_test(rsa_psk, 'aes_128_ccm_8', Config). + +rsa_psk_aes_256_cbc(Config) when is_list(Config) -> + run_ciphers_test(rsa_psk, 'aes_256_cbc', Config). + +rsa_psk_aes_256_ccm(Config) when is_list(Config) -> + run_ciphers_test(rsa_psk, 'aes_256_ccm', Config). + +rsa_psk_aes_256_ccm_8(Config) when is_list(Config) -> + run_ciphers_test(rsa_psk, 'aes_256_ccm_8', Config). + +rsa_psk_rc4_128(Config) when is_list(Config) -> + run_ciphers_test(rsa_psk, 'rc4_128', Config). + +%%-------------------------------------------------------------------- +%% RSA -------------------------------------------------------- +%%-------------------------------------------------------------------- +rsa_des_cbc(Config) when is_list(Config) -> + run_ciphers_test(rsa, 'des_cbc', Config). + +rsa_3des_ede_cbc(Config) when is_list(Config) -> + run_ciphers_test(rsa, '3des_ede_cbc', Config). + +rsa_aes_128_cbc(Config) when is_list(Config) -> + run_ciphers_test(rsa, 'aes_128_cbc', Config). + +rsa_aes_256_cbc(Config) when is_list(Config) -> + run_ciphers_test(rsa, 'aes_256_cbc', Config). + +rsa_aes_128_gcm(Config) when is_list(Config) -> + run_ciphers_test(rsa, 'aes_128_gcm', Config). + +rsa_aes_256_gcm(Config) when is_list(Config) -> + run_ciphers_test(rsa, 'aes_256_gcm', Config). + +rsa_rc4_128(Config) when is_list(Config) -> + run_ciphers_test(rsa, 'rc4_128', Config). +%%-------------------------------------------------------------------- +%% DHE_RSA -------------------------------------------------------- +%%-------------------------------------------------------------------- +dhe_rsa_3des_ede_cbc(Config) when is_list(Config) -> + run_ciphers_test(dhe_rsa, '3des_ede_cbc', Config). + +dhe_rsa_aes_128_cbc(Config) when is_list(Config) -> + run_ciphers_test(dhe_rsa, 'aes_128_cbc', Config). + +dhe_rsa_aes_128_gcm(Config) when is_list(Config) -> + run_ciphers_test(dhe_rsa, 'aes_128_gcm', Config). + +dhe_rsa_aes_256_cbc(Config) when is_list(Config) -> + run_ciphers_test(dhe_rsa, 'aes_256_cbc', Config). + +dhe_rsa_aes_256_gcm(Config) when is_list(Config) -> + run_ciphers_test(dhe_rsa, 'aes_256_gcm', Config). + +dhe_rsa_chacha20_poly1305(Config) when is_list(Config) -> + run_ciphers_test(dhe_rsa, 'chacha20_poly1305', Config). +%%-------------------------------------------------------------------- +%% ECDHE_RSA -------------------------------------------------------- +%%-------------------------------------------------------------------- +ecdhe_rsa_3des_ede_cbc(Config) when is_list(Config) -> + run_ciphers_test(ecdhe_rsa, '3des_ede_cbc', Config). + +ecdhe_rsa_aes_128_cbc(Config) when is_list(Config) -> + run_ciphers_test(ecdhe_rsa, 'aes_128_cbc', Config). + +ecdhe_rsa_aes_128_gcm(Config) when is_list(Config) -> + run_ciphers_test(ecdhe_rsa, 'aes_128_gcm', Config). + +ecdhe_rsa_aes_256_cbc(Config) when is_list(Config) -> + run_ciphers_test(ecdhe_rsa, 'aes_256_cbc', Config). + +ecdhe_rsa_aes_256_gcm(Config) when is_list(Config) -> + run_ciphers_test(ecdhe_rsa, 'aes_256_gcm', Config). + +ecdhe_rsa_rc4_128(Config) when is_list(Config) -> + run_ciphers_test(ecdhe_rsa, 'rc4_128', Config). + +ecdhe_rsa_chacha20_poly1305(Config) when is_list(Config) -> + run_ciphers_test(ecdhe_rsa, 'chacha20_poly1305', Config). + +%%-------------------------------------------------------------------- +%% ECDHE_ECDSA -------------------------------------------------------- +%%-------------------------------------------------------------------- +ecdhe_ecdsa_rc4_128(Config) when is_list(Config) -> + run_ciphers_test(ecdhe_ecdsa, 'rc4_128', Config). + +ecdhe_ecdsa_3des_ede_cbc(Config) when is_list(Config) -> + run_ciphers_test(ecdhe_ecdsa, '3des_ede_cbc', Config). + +ecdhe_ecdsa_aes_128_cbc(Config) when is_list(Config) -> + run_ciphers_test(ecdhe_ecdsa, 'aes_128_cbc', Config). + +ecdhe_ecdsa_aes_128_gcm(Config) when is_list(Config) -> + run_ciphers_test(ecdhe_ecdsa, 'aes_128_gcm', Config). + +ecdhe_ecdsa_aes_256_cbc(Config) when is_list(Config) -> + run_ciphers_test(ecdhe_ecdsa, 'aes_256_cbc', Config). + +ecdhe_ecdsa_aes_256_gcm(Config) when is_list(Config) -> + run_ciphers_test(ecdhe_ecdsa, 'aes_256_gcm', Config). + +ecdhe_ecdsa_chacha20_poly1305(Config) when is_list(Config) -> + run_ciphers_test(ecdhe_ecdsa, 'chacha20_poly1305', Config). +%%-------------------------------------------------------------------- +%% DHE_DSS -------------------------------------------------------- +%%-------------------------------------------------------------------- +dhe_dss_des_cbc(Config) when is_list(Config) -> + run_ciphers_test(dhe_dss, 'des_cbc', Config). + +dhe_dss_3des_ede_cbc(Config) when is_list(Config) -> + run_ciphers_test(dhe_dss, '3des_ede_cbc', Config). + +dhe_dss_aes_128_cbc(Config) when is_list(Config) -> + run_ciphers_test(dhe_dss, 'aes_128_cbc', Config). + +dhe_dss_aes_256_cbc(Config) when is_list(Config) -> + run_ciphers_test(dhe_dss, 'aes_256_cbc', Config). + +dhe_dss_aes_128_gcm(Config) when is_list(Config) -> + run_ciphers_test(dhe_dss, 'aes_128_gcm', Config). + +dhe_dss_aes_256_gcm(Config) when is_list(Config) -> + run_ciphers_test(dhe_dss, 'aes_256_gcm', Config). + +%%-------------------------------------------------------------------- +%% Anonymous -------------------------------------------------------- +%%-------------------------------------------------------------------- +dh_anon_3des_ede_cbc(Config) when is_list(Config) -> + run_ciphers_test(dh_anon, '3des_ede_cbc', Config). + +dh_anon_aes_128_cbc(Config) when is_list(Config) -> + run_ciphers_test(dh_anon, 'aes_128_cbc', Config). + +dh_anon_aes_128_gcm(Config) when is_list(Config) -> + run_ciphers_test(dh_anon, 'aes_128_gcm', Config). + +dh_anon_aes_256_cbc(Config) when is_list(Config) -> + run_ciphers_test(dh_anon, 'aes_256_cbc', Config). + +dh_anon_aes_256_gcm(Config) when is_list(Config) -> + run_ciphers_test(dh_anon, 'aes_256_gcm', Config). + +dh_anon_rc4_128(Config) when is_list(Config) -> + run_ciphers_test(dh_anon, 'rc4_128', Config). + +ecdh_anon_3des_ede_cbc(Config) when is_list(Config) -> + run_ciphers_test(ecdh_anon, '3des_ede_cbc', Config). + +ecdh_anon_aes_128_cbc(Config) when is_list(Config) -> + run_ciphers_test(ecdh_anon, 'aes_128_cbc', Config). + +ecdh_anon_aes_256_cbc(Config) when is_list(Config) -> + run_ciphers_test(ecdh_anon, 'aes_256_cbc', Config). + +srp_anon_3des_ede_cbc(Config) when is_list(Config) -> + run_ciphers_test(srp_anon, '3des_ede_cbc', Config). + +srp_anon_aes_128_cbc(Config) when is_list(Config) -> + run_ciphers_test(srp_anon, 'aes_128_cbc', Config). + +srp_anon_aes_256_cbc(Config) when is_list(Config) -> + run_ciphers_test(srp_anon, 'aes_256_cbc', Config). + +dhe_psk_des_cbc(Config) when is_list(Config) -> + run_ciphers_test(dhe_psk, 'des_cbc', Config). + +dhe_psk_rc4_128(Config) when is_list(Config) -> + run_ciphers_test(dhe_psk, 'rc4_128', Config). + +dhe_psk_3des_ede_cbc(Config) when is_list(Config) -> + run_ciphers_test(dhe_psk, '3des_ede_cbc', Config). + +dhe_psk_aes_128_cbc(Config) when is_list(Config) -> + run_ciphers_test(dhe_psk, 'aes_128_cbc', Config). + +dhe_psk_aes_256_cbc(Config) when is_list(Config) -> + run_ciphers_test(dhe_psk, 'aes_256_cbc', Config). + +dhe_psk_aes_128_gcm(Config) when is_list(Config) -> + run_ciphers_test(dhe_psk, 'aes_128_gcm', Config). + +dhe_psk_aes_256_gcm(Config) when is_list(Config) -> + run_ciphers_test(dhe_psk, 'aes_256_gcm', Config). + +dhe_psk_aes_128_ccm(Config) when is_list(Config) -> + run_ciphers_test(dhe_psk, 'aes_128_ccm', Config). + +dhe_psk_aes_256_ccm(Config) when is_list(Config) -> + run_ciphers_test(dhe_psk, 'aes_256_ccm', Config). + +dhe_psk_aes_128_ccm_8(Config) when is_list(Config) -> + run_ciphers_test(dhe_psk, 'aes_128_ccm_8', Config). + +dhe_psk_aes_256_ccm_8(Config) when is_list(Config) -> + run_ciphers_test(dhe_psk, 'aes_256_ccm_8', Config). + +ecdhe_psk_des_cbc(Config) when is_list(Config) -> + run_ciphers_test(ecdhe_psk, 'des_cbc', Config). + +ecdhe_psk_rc4_128(Config) when is_list(Config) -> + run_ciphers_test(ecdhe_psk, 'rc4_128', Config). + +ecdhe_psk_3des_ede_cbc(Config) when is_list(Config) -> + run_ciphers_test(ecdhe_psk, '3des_ede_cbc', Config). + +ecdhe_psk_aes_128_cbc(Config) when is_list(Config) -> + run_ciphers_test(ecdhe_psk, 'aes_128_cbc', Config). + +ecdhe_psk_aes_256_cbc(Config) when is_list(Config) -> + run_ciphers_test(ecdhe_psk, 'aes_256_cbc', Config). + +ecdhe_psk_aes_128_gcm(Config) when is_list(Config) -> + run_ciphers_test(ecdhe_psk, 'aes_128_gcm', Config). + +ecdhe_psk_aes_256_gcm(Config) when is_list(Config) -> + run_ciphers_test(ecdhe_psk, 'aes_256_gcm', Config). + +ecdhe_psk_aes_128_ccm(Config) when is_list(Config) -> + run_ciphers_test(ecdhe_psk, 'aes_128_ccm', Config). + +ecdhe_psk_aes_128_ccm_8(Config) when is_list(Config) -> + run_ciphers_test(ecdhe_psk, 'aes_128_ccm_8', Config). + +psk_des_cbc(Config) when is_list(Config) -> + run_ciphers_test(psk, 'des_cbc', Config). + +psk_rc4_128(Config) when is_list(Config) -> + run_ciphers_test(psk, 'rc4_128', Config). + +psk_3des_ede_cbc(Config) when is_list(Config) -> + run_ciphers_test(psk, '3des_ede_cbc', Config). + +psk_aes_128_cbc(Config) when is_list(Config) -> + run_ciphers_test(psk, 'aes_128_cbc', Config). + +psk_aes_256_cbc(Config) when is_list(Config) -> + run_ciphers_test(psk, 'aes_256_cbc', Config). + +psk_aes_128_gcm(Config) when is_list(Config) -> + run_ciphers_test(psk, 'aes_128_gcm', Config). + +psk_aes_256_gcm(Config) when is_list(Config) -> + run_ciphers_test(psk, 'aes_256_gcm', Config). + +psk_aes_128_ccm(Config) when is_list(Config) -> + run_ciphers_test(psk, 'aes_128_ccm', Config). + +psk_aes_256_ccm(Config) when is_list(Config) -> + run_ciphers_test(psk, 'aes_256_ccm', Config). + +psk_aes_128_ccm_8(Config) when is_list(Config) -> + run_ciphers_test(psk, 'aes_128_ccm_8', Config). + +psk_aes_256_ccm_8(Config) when is_list(Config) -> + run_ciphers_test(psk, 'aes_256_ccm_8', Config). + +%%-------------------------------------------------------------------- +%% Internal functions ---------------------------------------------- +%%-------------------------------------------------------------------- +test_cipher(TestCase, Config) -> + [{name, Group} |_] = proplists:get_value(tc_group_properties, Config), + list_to_atom(re:replace(atom_to_list(TestCase), atom_to_list(Group) ++ "_", "", [{return, list}])). + +run_ciphers_test(Kex, Cipher, Config) -> + Version = ssl_test_lib:protocol_version(Config), + TestCiphers = test_ciphers(Kex, Cipher, Version), + + case TestCiphers of + [_|_] -> + lists:foreach(fun(TestCipher) -> + cipher_suite_test(TestCipher, Version, Config) + end, TestCiphers); + [] -> + {skip, {not_sup, Kex, Cipher, Version}} + end. + +cipher_suite_test(CipherSuite, Version, Config) -> + #{server_config := SOpts, + client_config := COpts} = proplists:get_value(tls_config, Config), + ServerOpts = ssl_test_lib:ssl_options(SOpts, Config), + ClientOpts = ssl_test_lib:ssl_options(COpts, Config), + ct:log("Testing CipherSuite ~p~n", [CipherSuite]), + ct:log("Server Opts ~p~n", [ServerOpts]), + ct:log("Client Opts ~p~n", [ClientOpts]), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + ErlangCipherSuite = erlang_cipher_suite(CipherSuite), + + ConnectionInfo = {ok, {Version, ErlangCipherSuite}}, + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, cipher_result, [ConnectionInfo]}}, + {options, [{versions, [Version]}, {ciphers, [CipherSuite]} | 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, cipher_result, [ConnectionInfo]}}, + {options, [{versions, [Version]}, {ciphers, [CipherSuite]} | + ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +erlang_cipher_suite(Suite) when is_list(Suite)-> + ssl_cipher_format:suite_definition(ssl_cipher_format:suite_openssl_str_to_map(Suite)); +erlang_cipher_suite(Suite) -> + Suite. + +user_lookup(psk, _Identity, UserState) -> + {ok, UserState}; +user_lookup(srp, Username, _UserState) -> + Salt = ssl_cipher:random_bytes(16), + UserPassHash = crypto:hash(sha, [Salt, crypto:hash(sha, [Username, <<$:>>, <<"secret">>])]), + {ok, {srp_1024, Salt, UserPassHash}}. + +test_ciphers(Kex, Cipher, Version) -> + ssl:filter_cipher_suites(ssl:cipher_suites(all, Version) ++ ssl:cipher_suites(anonymous, Version), + [{key_exchange, + fun(Kex0) when Kex0 == Kex -> true; + (_) -> false + end}, + {cipher, + fun(Cipher0) when Cipher0 == Cipher -> true; + (_) -> false + end}]). diff --git a/lib/ssl/test/ssl_dist_bench_SUITE.erl b/lib/ssl/test/ssl_dist_bench_SUITE.erl index 618ad0789e..1fea6f6f72 100644 --- a/lib/ssl/test/ssl_dist_bench_SUITE.erl +++ b/lib/ssl/test/ssl_dist_bench_SUITE.erl @@ -32,6 +32,7 @@ -export( [setup/1, roundtrip/1, + sched_utilization/1, throughput_0/1, throughput_64/1, throughput_1024/1, @@ -48,14 +49,19 @@ suite() -> [{ct_hooks, [{ts_install_cth, [{nodenames, 2}]}]}]. -all() -> [{group, ssl}, {group, plain}]. +all() -> + [{group, ssl}, + {group, crypto}, + {group, plain}]. groups() -> [{ssl, all_groups()}, + {crypto, all_groups()}, {plain, all_groups()}, %% {setup, [{repeat, 1}], [setup]}, {roundtrip, [{repeat, 1}], [roundtrip]}, + {sched_utilization,[{repeat, 1}], [sched_utilization]}, {throughput, [{repeat, 1}], [throughput_0, throughput_64, @@ -69,7 +75,9 @@ groups() -> all_groups() -> [{group, setup}, {group, roundtrip}, - {group, throughput}]. + {group, throughput}, + {group, sched_utilization} + ]. init_per_suite(Config) -> Digest = sha1, @@ -160,6 +168,17 @@ end_per_suite(Config) -> init_per_group(ssl, Config) -> [{ssl_dist, true}, {ssl_dist_prefix, "SSL"}|Config]; +init_per_group(crypto, Config) -> + case inet_crypto_dist:is_supported() of + true -> + [{ssl_dist, false}, {ssl_dist_prefix, "Crypto"}, + {ssl_dist_args, + "-proto_dist inet_crypto " + "-inet_crypto '#{secret => \"123456\"}'"} + |Config]; + false -> + {skip, "Not supported on this OTP version"} + end; init_per_group(plain, Config) -> [{ssl_dist, false}, {ssl_dist_prefix, "Plain"}|Config]; init_per_group(_GroupName, Config) -> @@ -351,6 +370,120 @@ roundtrip_client(Pid, Mon, StartTime, N) -> roundtrip_client(Pid, Mon, StartTime, N - 1) end. +%%--------------------------------------- +%% Scheduler utilization at constant load + + +sched_utilization(Config) -> + run_nodepair_test( + fun(A, B, Prefix, HA, HB) -> + sched_utilization(A, B, Prefix, HA, HB, proplists:get_value(ssl_dist, Config)) + end, Config). + +sched_utilization(A, B, Prefix, HA, HB, SSL) -> + [] = ssl_apply(HA, erlang, nodes, []), + [] = ssl_apply(HB, erlang, nodes, []), + {ClientMsacc, ServerMsacc, Msgs} = + ssl_apply(HA, fun () -> sched_util_runner(A, B, SSL) end), + [B] = ssl_apply(HA, erlang, nodes, []), + [A] = ssl_apply(HB, erlang, nodes, []), + msacc:print(ClientMsacc), + msacc:print(ServerMsacc), + ct:pal("Got ~p busy_dist_port msgs",[length(Msgs)]), + ct:log("Stats of B from A: ~p", + [ssl_apply(HA, net_kernel, node_info, [B])]), + ct:log("Stats of A from B: ~p", + [ssl_apply(HB, net_kernel, node_info, [A])]), + SchedUtilClient = + round(10000 * msacc:stats(system_runtime,ClientMsacc) / + msacc:stats(system_realtime,ClientMsacc)), + SchedUtilServer = + round(10000 * msacc:stats(system_runtime,ServerMsacc) / + msacc:stats(system_realtime,ServerMsacc)), + Verdict = + case Msgs of + [] -> + ""; + _ -> + " ???" + end, + {comment, ClientComment} = + report(Prefix ++ " Sched Utilization Client" ++ Verdict, + SchedUtilClient, "/100 %" ++ Verdict), + {comment, ServerComment} = + report(Prefix++" Sched Utilization Server" ++ Verdict, + SchedUtilServer, "/100 %" ++ Verdict), + {comment, "Client " ++ ClientComment ++ ", Server " ++ ServerComment}. + +%% Runs on node A and spawns a server on node B +%% We want to avoid getting busy_dist_port as it hides the true SU usage +%% of the receiver and sender. +sched_util_runner(A, B, true) -> + sched_util_runner(A, B, 250); +sched_util_runner(A, B, false) -> + sched_util_runner(A, B, 250); +sched_util_runner(A, B, Senders) -> + Payload = payload(5), + [A] = rpc:call(B, erlang, nodes, []), + ServerPids = + [erlang:spawn_link( + B, fun () -> throughput_server() end) + || _ <- lists:seq(1, Senders)], + ServerMsacc = + erlang:spawn( + B, + fun() -> + receive + {start,Pid} -> + msacc:start(10000), + receive + {done,Pid} -> + Pid ! {self(),msacc:stats()} + end + end + end), + erlang:system_monitor(self(),[busy_dist_port]), + %% We spawn 250 senders which should mean that we + %% have a load of 250 msgs/msec + [spawn_link( + fun() -> + throughput_client(Pid, Payload) + end) || Pid <- ServerPids], + %% + receive after 1000 -> ok end, + ServerMsacc ! {start,self()}, + msacc:start(10000), + ClientMsaccStats = msacc:stats(), + receive after 1000 -> ok end, + ServerMsacc ! {done,self()}, + ServerMsaccStats = receive {ServerMsacc,Stats} -> Stats end, + %% + {ClientMsaccStats,ServerMsaccStats, flush()}. + +flush() -> + receive + M -> + [M | flush()] + after 0 -> + [] + end. + +throughput_server() -> + receive _ -> ok end, + receive _ -> ok end, + receive _ -> ok end, + receive _ -> ok end, + receive _ -> ok end, + receive _ -> ok end, + receive _ -> ok end, + receive _ -> ok end, + receive _ -> ok end, + receive _ -> ok end, + throughput_server(). + +throughput_client(Pid, Payload) -> + Pid ! Payload, + receive after 1 -> throughput_client(Pid, Payload) end. %%----------------- %% Throughput speed @@ -425,15 +558,20 @@ throughput(A, B, Prefix, HA, HB, Packets, Size) -> + byte_size(erlang:term_to_binary([0|<<>>])), % Benchmark overhead Bytes = Packets * (Size + Overhead), io:format("~w bytes, ~.4g s~n", [Bytes,Time/1000000]), + SizeString = integer_to_list(Size), ClientMsaccStats =:= undefined orelse - io:format( - "Sender core usage ratio: ~.4g ns/byte~n", - [msacc:stats(system_runtime, ClientMsaccStats)*1000/Bytes]), + report( + Prefix ++ " Sender_RelativeCoreLoad_" ++ SizeString, + round(msacc:stats(system_runtime, ClientMsaccStats) + * 1000000 / Bytes), + "ps/byte"), ServerMsaccStats =:= undefined orelse begin - io:format( - "Receiver core usage ratio: ~.4g ns/byte~n", - [msacc:stats(system_runtime, ServerMsaccStats)*1000/Bytes]), + report( + Prefix ++ " Receiver_RelativeCoreLoad_" ++ SizeString, + round(msacc:stats(system_runtime, ServerMsaccStats) + * 1000000 / Bytes), + "ps/byte"), msacc:print(ServerMsaccStats) end, io:format("******* ClientProf:~n", []), prof_print(ClientProf), @@ -441,7 +579,7 @@ throughput(A, B, Prefix, HA, HB, Packets, Size) -> io:format("******* Server GC Before:~n~p~n", [Server_GC_Before]), io:format("******* Server GC After:~n~p~n", [Server_GC_After]), Speed = round((Bytes * 1000000) / (1024 * Time)), - report(Prefix++" Throughput_"++integer_to_list(Size), Speed, "kB/s"). + report(Prefix ++ " Throughput_" ++ SizeString, Speed, "kB/s"). %% Runs on node A and spawns a server on node B throughput_runner(A, B, Rounds, Size) -> @@ -449,11 +587,12 @@ throughput_runner(A, B, Rounds, Size) -> [A] = rpc:call(B, erlang, nodes, []), ClientPid = self(), ServerPid = - erlang:spawn( + erlang:spawn_opt( B, - fun () -> throughput_server(ClientPid, Rounds) end), + fun () -> throughput_server(ClientPid, Rounds) end, + [{message_queue_data, off_heap}]), ServerMon = erlang:monitor(process, ServerPid), - msacc:available() andalso + msacc_available() andalso begin msacc:stop(), msacc:reset(), @@ -465,7 +604,7 @@ throughput_runner(A, B, Rounds, Size) -> throughput_client(ServerPid, ServerMon, Payload, Rounds), prof_stop(), MsaccStats = - case msacc:available() of + case msacc_available() of true -> MStats = msacc:stats(), msacc:stop(), @@ -505,7 +644,7 @@ throughput_server(Pid, N) -> GC_Before = get_server_gc_info(), %% dbg:tracer(port, dbg:trace_port(file, "throughput_server_gc.log")), %% dbg:p(TLSDistReceiver, garbage_collection), - msacc:available() andalso + msacc_available() andalso begin msacc:stop(), msacc:reset(), @@ -518,7 +657,7 @@ throughput_server(Pid, N) -> throughput_server_loop(_Pid, GC_Before, 0) -> prof_stop(), MsaccStats = - case msacc:available() of + case msacc_available() of true -> msacc:stop(), MStats = msacc:stats(), @@ -535,8 +674,13 @@ throughput_server_loop(_Pid, GC_Before, 0) -> server_gc_after => get_server_gc_info()}); throughput_server_loop(Pid, GC_Before, N) -> receive - {Pid, N, _} -> - throughput_server_loop(Pid, GC_Before, N-1) + Msg -> + case Msg of + {Pid, N, _} -> + throughput_server_loop(Pid, GC_Before, N - 1); + Other -> + erlang:error({self(),?FUNCTION_NAME,Other}) + end end. get_server_gc_info() -> @@ -676,7 +820,7 @@ get_node_args(Tag, Config) -> true -> proplists:get_value(Tag, Config); false -> - "" + proplists:get_value(ssl_dist_args, Config, "") end. @@ -731,3 +875,6 @@ report(Name, Value, Unit) -> term_to_string(Term) -> unicode:characters_to_list( io_lib:write(Term, [{encoding, unicode}])). + +msacc_available() -> + msacc:available(). diff --git a/lib/ssl/test/ssl_dist_test_lib.erl b/lib/ssl/test/ssl_dist_test_lib.erl index 1b9c853fc4..b8b805b2c4 100644 --- a/lib/ssl/test/ssl_dist_test_lib.erl +++ b/lib/ssl/test/ssl_dist_test_lib.erl @@ -144,6 +144,8 @@ mk_node_cmdline(ListenPort, Name, Args) -> ++ integer_to_list(ListenPort) ++ " " ++ Args ++ " " ++ "-env ERL_CRASH_DUMP " ++ Pwd ++ "/erl_crash_dump." ++ Name ++ " " + ++ "-kernel inet_dist_connect_options \"[{recbuf,12582912},{sndbuf,12582912},{high_watermark,8388608},{low_watermark,4194304}]\" " + ++ "-kernel inet_dist_listen_options \"[{recbuf,12582912},{sndbuf,12582912},{high_watermark,8388608},{low_watermark,4194304}]\" " ++ "-kernel error_logger \"{file,\\\"" ++ Pwd ++ "/error_log." ++ Name ++ "\\\"}\" " ++ "-setcookie " ++ atom_to_list(erlang:get_cookie()). @@ -179,8 +181,9 @@ check_ssl_node_up(Socket, Name, Bin) -> Parent = self(), Go = make_ref(), %% Spawn connection handler on test server side - Pid = spawn_link( + Pid = spawn( fun () -> + link(group_leader()), receive Go -> ok end, process_flag(trap_exit, true), tstsrvr_con_loop(Name, Socket, Parent) diff --git a/lib/ssl/test/ssl_eqc_SUITE.erl b/lib/ssl/test/ssl_eqc_SUITE.erl new file mode 100644 index 0000000000..bd36d35c02 --- /dev/null +++ b/lib/ssl/test/ssl_eqc_SUITE.erl @@ -0,0 +1,58 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2015-2015. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% + +-module(ssl_eqc_SUITE). + +-compile(export_all). +%%-------------------------------------------------------------------- +%% Common Test interface functions ----------------------------------- +%%-------------------------------------------------------------------- + +all() -> + [ + tls_handshake_encoding + ]. + +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + ct_property_test:init_per_suite(Config). +end_per_suite(Config) -> + Config. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_,Config) -> + Config. + +init_per_testcase(_, Config0) -> + Config0. + +end_per_testcase(_TestCase, Config) -> + Config. + +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- + +tls_handshake_encoding(Config) when is_list(Config) -> + %% manual test: proper:quickcheck(ssl_eqc_handshake:prop_tls_hs_encode_decode()). + true = ct_property_test:quickcheck(ssl_eqc_handshake:prop_tls_hs_encode_decode(), + Config). diff --git a/lib/ssl/test/ssl_handshake_SUITE.erl b/lib/ssl/test/ssl_handshake_SUITE.erl index 1fa6029963..1b432970b6 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_handshake.hrl"). -include("ssl_internal.hrl"). -include("tls_handshake.hrl"). @@ -42,7 +43,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, encode_decode_srp]. %%-------------------------------------------------------------------- @@ -57,7 +58,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 -> @@ -106,15 +109,13 @@ decode_hello_handshake(_Config) -> #ssl_options{}), {Hello, _Data} = hd(Records), - #renegotiation_info{renegotiated_connection = <<0>>} - = (Hello#server_hello.extensions)#hello_extensions.renegotiation_info. - + Extensions = Hello#server_hello.extensions, + #{renegotiation_info := #renegotiation_info{renegotiated_connection = <<0>>}} = Extensions. decode_single_hello_extension_correctly(_Config) -> Renegotiation = <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(1), 0>>, - Extensions = ssl_handshake:decode_hello_extensions(Renegotiation), - #renegotiation_info{renegotiated_connection = <<0>>} - = Extensions#hello_extensions.renegotiation_info. + Extensions = ssl_handshake:decode_extensions(Renegotiation, {3,3}, undefined), + #{renegotiation_info := #renegotiation_info{renegotiated_connection = <<0>>}} = Extensions. decode_supported_elliptic_curves_hello_extension_correctly(_Config) -> % List of supported and unsupported curves (RFC4492:S5.1.1) @@ -125,37 +126,34 @@ decode_supported_elliptic_curves_hello_extension_correctly(_Config) -> Len = ListLen + 2, Extension = <<?UINT16(?ELLIPTIC_CURVES_EXT), ?UINT16(Len), ?UINT16(ListLen), EllipticCurveList/binary>>, % after decoding we should see only valid curves - #hello_extensions{elliptic_curves = DecodedCurves} = ssl_handshake:decode_hello_extensions(Extension), - #elliptic_curves{elliptic_curve_list = [?sect233k1, ?sect193r2]} = DecodedCurves. + Extensions = ssl_handshake:decode_hello_extensions(Extension, {3,2}, {3,2}, client), + #{elliptic_curves := #elliptic_curves{elliptic_curve_list = [?sect233k1, ?sect193r2]}} = Extensions. decode_unknown_hello_extension_correctly(_Config) -> FourByteUnknown = <<16#CA,16#FE, ?UINT16(4), 3, 0, 1, 2>>, Renegotiation = <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(1), 0>>, - Extensions = ssl_handshake:decode_hello_extensions(<<FourByteUnknown/binary, Renegotiation/binary>>), - #renegotiation_info{renegotiated_connection = <<0>>} - = Extensions#hello_extensions.renegotiation_info. + Extensions = ssl_handshake:decode_hello_extensions(<<FourByteUnknown/binary, Renegotiation/binary>>, {3,2}, {3,2}, client), + #{renegotiation_info := #renegotiation_info{renegotiated_connection = <<0>>}} = Extensions. + encode_single_hello_sni_extension_correctly(_Config) -> - Exts = #hello_extensions{sni = #sni{hostname = "test.com"}}, SNI = <<16#00, 16#00, 16#00, 16#0d, 16#00, 16#0b, 16#00, 16#00, 16#08, $t, $e, $s, $t, $., $c, $o, $m>>, ExtSize = byte_size(SNI), HelloExt = <<ExtSize:16/unsigned-big-integer, SNI/binary>>, - Encoded = ssl_handshake:encode_hello_extensions(Exts), + Encoded = ssl_handshake:encode_extensions([#sni{hostname = "test.com"}]), HelloExt = Encoded. decode_single_hello_sni_extension_correctly(_Config) -> - Exts = #hello_extensions{sni = #sni{hostname = "test.com"}}, SNI = <<16#00, 16#00, 16#00, 16#0d, 16#00, 16#0b, 16#00, 16#00, 16#08, $t, $e, $s, $t, $., $c, $o, $m>>, - Decoded = ssl_handshake:decode_hello_extensions(SNI), - Exts = Decoded. + Decoded = ssl_handshake:decode_hello_extensions(SNI, {3,3}, {3,3}, client), + #{sni := #sni{hostname = "test.com"}} = Decoded. decode_empty_server_sni_correctly(_Config) -> - Exts = #hello_extensions{sni = #sni{hostname = ""}}, SNI = <<?UINT16(?SNI_EXT),?UINT16(0)>>, - Decoded = ssl_handshake:decode_hello_extensions(SNI), - Exts = Decoded. + Decoded = ssl_handshake:decode_hello_extensions(SNI, {3,3}, {3,3}, server), + #{sni := #sni{hostname = ""}} = Decoded. select_proper_tls_1_2_rsa_default_hashsign(_Config) -> @@ -170,11 +168,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(), @@ -195,30 +193,78 @@ unorded_chain(Config) when is_list(Config) -> ssl_certificate:certificate_chain(PeerCert, ets:new(foo, []), ExtractedCerts, UnordedChain). encode_decode_srp(_Config) -> - Exts = #hello_extensions{ - srp = #srp{username = <<"foo">>}, - sni = #sni{hostname = "bar"}, - renegotiation_info = undefined, - signature_algs = undefined, - alpn = undefined, - next_protocol_negotiation = undefined, - ec_point_formats = undefined, - elliptic_curves = undefined - }, - EncodedExts = <<0,20, % Length + Exts = #{srp => #srp{username = <<"foo">>}, + sni => #sni{hostname = "bar"}, + renegotiation_info => undefined, + signature_algs => undefined, + alpn => undefined, + next_protocol_negotiation => undefined, + ec_point_formats => undefined, + elliptic_curves => undefined + }, + EncodedExts0 = <<0,20, % Length + 0,12, % SRP extension + 0,4, % Length + 3, % srp_I length + 102,111,111, % username = "foo" 0,0, % SNI extension 0,8, % Length 0,6, % ServerNameLength 0, % NameType (host_name) 0,3, % HostNameLength - 98,97,114, % hostname = "bar" - 0,12, % SRP extension - 0,4, % Length - 3, % srp_I length - 102,111,111>>, % username = "foo" - EncodedExts = ssl_handshake:encode_hello_extensions(Exts), - Exts = ssl_handshake:decode_hello_extensions({client, EncodedExts}). + 98,97,114>>, % hostname = "bar" + EncodedExts0 = <<?UINT16(_),EncodedExts/binary>> = + ssl_handshake:encode_hello_extensions(Exts), + Exts = ssl_handshake:decode_hello_extensions(EncodedExts, {3,3}, {3,3}, client). +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_npn_hello_SUITE.erl b/lib/ssl/test/ssl_npn_hello_SUITE.erl index 35af666e9e..46734ba180 100644 --- a/lib/ssl/test/ssl_npn_hello_SUITE.erl +++ b/lib/ssl/test/ssl_npn_hello_SUITE.erl @@ -71,44 +71,46 @@ encode_and_decode_client_hello_test(Config) -> Version = ssl_test_lib:protocol_version(Config), {[{DecodedHandshakeMessage, _Raw}], _} = tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>, #ssl_options{}), - NextProtocolNegotiation = (DecodedHandshakeMessage#client_hello.extensions)#hello_extensions.next_protocol_negotiation, - NextProtocolNegotiation = undefined. + Extensions = DecodedHandshakeMessage#client_hello.extensions, + #{next_protocol_negotiation := undefined} = Extensions. %%-------------------------------------------------------------------- encode_and_decode_npn_client_hello_test(Config) -> HandShakeData = create_client_handshake(#next_protocol_negotiation{extension_data = <<>>}), Version = ssl_test_lib:protocol_version(Config), {[{DecodedHandshakeMessage, _Raw}], _} = tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>, #ssl_options{}), - NextProtocolNegotiation = (DecodedHandshakeMessage#client_hello.extensions)#hello_extensions.next_protocol_negotiation, - NextProtocolNegotiation = #next_protocol_negotiation{extension_data = <<>>}. + Extensions = DecodedHandshakeMessage#client_hello.extensions, + #{next_protocol_negotiation := #next_protocol_negotiation{extension_data = <<>>}} = Extensions. %%-------------------------------------------------------------------- encode_and_decode_server_hello_test(Config) -> HandShakeData = create_server_handshake(undefined), Version = ssl_test_lib:protocol_version(Config), {[{DecodedHandshakeMessage, _Raw}], _} = tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>, #ssl_options{}), - NextProtocolNegotiation = (DecodedHandshakeMessage#server_hello.extensions)#hello_extensions.next_protocol_negotiation, - NextProtocolNegotiation = undefined. + Extensions = DecodedHandshakeMessage#server_hello.extensions, + #{next_protocol_negotiation := undefined} = Extensions. + %%-------------------------------------------------------------------- encode_and_decode_npn_server_hello_test(Config) -> HandShakeData = create_server_handshake(#next_protocol_negotiation{extension_data = <<6, "spdy/2">>}), Version = ssl_test_lib:protocol_version(Config), {[{DecodedHandshakeMessage, _Raw}], _} = tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>, #ssl_options{}), - NextProtocolNegotiation = (DecodedHandshakeMessage#server_hello.extensions)#hello_extensions.next_protocol_negotiation, - ct:log("~p ~n", [NextProtocolNegotiation]), - NextProtocolNegotiation = #next_protocol_negotiation{extension_data = <<6, "spdy/2">>}. + Extensions = DecodedHandshakeMessage#server_hello.extensions, + ct:log("~p ~n", [Extensions]), + #{next_protocol_negotiation := #next_protocol_negotiation{extension_data = <<6, "spdy/2">>}} = Extensions. %%-------------------------------------------------------------------- create_server_hello_with_no_advertised_protocols_test(_Config) -> - Hello = ssl_handshake:server_hello(<<>>, {3, 0}, create_connection_states(), #hello_extensions{}), - undefined = (Hello#server_hello.extensions)#hello_extensions.next_protocol_negotiation. + Hello = ssl_handshake:server_hello(<<>>, {3, 0}, create_connection_states(), #{}), + Extensions = Hello#server_hello.extensions, + #{} = Extensions. %%-------------------------------------------------------------------- create_server_hello_with_advertised_protocols_test(_Config) -> Hello = ssl_handshake:server_hello(<<>>, {3, 0}, create_connection_states(), - #hello_extensions{next_protocol_negotiation = [<<"spdy/1">>, <<"http/1.0">>, <<"http/1.1">>]}), - [<<"spdy/1">>, <<"http/1.0">>, <<"http/1.1">>] = - (Hello#server_hello.extensions)#hello_extensions.next_protocol_negotiation. + #{next_protocol_negotiation => [<<"spdy/1">>, <<"http/1.0">>, <<"http/1.1">>]}), + Extensions = Hello#server_hello.extensions, + #{next_protocol_negotiation := [<<"spdy/1">>, <<"http/1.0">>, <<"http/1.1">>]} = Extensions. %%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- @@ -120,9 +122,8 @@ create_client_handshake(Npn) -> session_id = <<>>, cipher_suites = [?TLS_DHE_DSS_WITH_DES_CBC_SHA], compression_methods = "", - extensions = #hello_extensions{ - next_protocol_negotiation = Npn, - renegotiation_info = #renegotiation_info{}} + extensions = #{next_protocol_negotiation => Npn, + renegotiation_info => #renegotiation_info{}} }, Vsn). create_server_handshake(Npn) -> @@ -133,9 +134,8 @@ create_server_handshake(Npn) -> session_id = <<>>, cipher_suite = ?TLS_DHE_DSS_WITH_DES_CBC_SHA, compression_method = 1, - extensions = #hello_extensions{ - next_protocol_negotiation = Npn, - renegotiation_info = #renegotiation_info{}} + extensions = #{next_protocol_negotiation => Npn, + renegotiation_info => #renegotiation_info{}} }, Vsn). create_connection_states() -> @@ -146,5 +146,5 @@ create_connection_states() -> } }, current_read => #{secure_renegotiation => false - } + } }. diff --git a/lib/ssl/test/ssl_rfc_5869_SUITE.erl b/lib/ssl/test/ssl_rfc_5869_SUITE.erl new file mode 100644 index 0000000000..8b2d1c2082 --- /dev/null +++ b/lib/ssl/test/ssl_rfc_5869_SUITE.erl @@ -0,0 +1,316 @@ +%% +%% %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% +%% + +%% +-module(ssl_rfc_5869_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). + +%%-------------------------------------------------------------------- +%% Common Test interface functions ----------------------------------- +%%-------------------------------------------------------------------- +all() -> + [sha_256_basic, + sha_256_long, + sha_256_no_salt, + sha_basic, + sha_long, + sha_no_salt, + sha_default_salt + ]. + +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + catch crypto:stop(), + try crypto:start() of + ok -> + Config + catch _:_ -> + {skip, "Crypto did not start"} + end. + +end_per_suite(_Config) -> + application:stop(crypto). + +%%-------------------------------------------------------------------- +init_per_testcase(_TestCase, Config) -> + ct:timetrap({seconds, 5}), + Config. + +end_per_testcase(_TestCase, Config) -> + Config. + +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- + +sha_256_basic() -> + [{doc, "Basic test case with SHA-256"}]. +sha_256_basic(Config) when is_list(Config) -> + %% Hash = SHA-256 + %% IKM = 0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b (22 octets) + %% salt = 0x000102030405060708090a0b0c (13 octets) + %% info = 0xf0f1f2f3f4f5f6f7f8f9 (10 octets) + %% L = 42 + %% PRK = 0x077709362c2e32df0ddc3f0dc47bba63 + %% 90b6c73bb50f9c3122ec844ad7c2b3e5 (32 octets) + %% OKM = 0x3cb25f25faacd57a90434f64d0362f2a + %% 2d2d0a90cf1a5a4c5db02d56ecc4c5bf + %% 34007208d5b887185865 (42 octets) + IKM = hexstr2bin("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"), + Salt = hexstr2bin("000102030405060708090a0b0c"), + Info = hexstr2bin("f0f1f2f3f4f5f6f7f8f9"), + PRK = hexstr2bin("077709362c2e32df0ddc3f0dc47bba63" + "90b6c73bb50f9c3122ec844ad7c2b3e5"), + OKM = hexstr2bin("3cb25f25faacd57a90434f64d0362f2a" + "2d2d0a90cf1a5a4c5db02d56ecc4c5bf" + "34007208d5b887185865"), + hkdf_test(sha256, Salt, IKM, PRK, Info, 42, OKM). + +sha_256_long() -> + [{doc, "Test with SHA-256 and longer inputs/outputs"}]. +sha_256_long(Config) when is_list(Config) -> + %% Hash = SHA-256 + %% IKM = 0x000102030405060708090a0b0c0d0e0f + %% 101112131415161718191a1b1c1d1e1f + %% 202122232425262728292a2b2c2d2e2f + %% 303132333435363738393a3b3c3d3e3f + %% 404142434445464748494a4b4c4d4e4f (80 octets) + %% salt = 0x606162636465666768696a6b6c6d6e6f + %% 707172737475767778797a7b7c7d7e7f + %% 808182838485868788898a8b8c8d8e8f + %% 909192939495969798999a9b9c9d9e9f + %% a0a1a2a3a4a5a6a7a8a9aaabacadaeaf (80 octets) + %% info = 0xb0b1b2b3b4b5b6b7b8b9babbbcbdbebf + %% c0c1c2c3c4c5c6c7c8c9cacbcccdcecf + %% d0d1d2d3d4d5d6d7d8d9dadbdcdddedf + %% e0e1e2e3e4e5e6e7e8e9eaebecedeeef + %% f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff (80 octets) + %% L = 82 + + %% PRK = 0x06a6b88c5853361a06104c9ceb35b45c + %% ef760014904671014a193f40c15fc244 (32 octets) + %% OKM = 0xb11e398dc80327a1c8e7f78c596a4934 + %% 4f012eda2d4efad8a050cc4c19afa97c + %% 59045a99cac7827271cb41c65e590e09 + %% da3275600c2f09b8367793a9aca3db71 + %% cc30c58179ec3e87c14c01d5c1f3434f + %% 1d87 (82 octets) + IKM = hexstr2bin("000102030405060708090a0b0c0d0e0f" + "101112131415161718191a1b1c1d1e1f" + "202122232425262728292a2b2c2d2e2f" + "303132333435363738393a3b3c3d3e3f" + "404142434445464748494a4b4c4d4e4f" + ), + Salt = hexstr2bin("606162636465666768696a6b6c6d6e6f" + "707172737475767778797a7b7c7d7e7f" + "808182838485868788898a8b8c8d8e8f" + "909192939495969798999a9b9c9d9e9f" + "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf" + ), + Info = hexstr2bin("b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" + "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" + "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" + "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" + "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff" + ), + PRK = hexstr2bin("06a6b88c5853361a06104c9ceb35b45c" + "ef760014904671014a193f40c15fc244"), + OKM = hexstr2bin("b11e398dc80327a1c8e7f78c596a4934" + "4f012eda2d4efad8a050cc4c19afa97c" + "59045a99cac7827271cb41c65e590e09" + "da3275600c2f09b8367793a9aca3db71" + "cc30c58179ec3e87c14c01d5c1f3434f" + "1d87" + ), + hkdf_test(sha256, Salt, IKM, PRK, Info, 82, OKM). +sha_256_no_salt() -> + [{doc, "Test with SHA-256 and zero-length salt/info"}]. +sha_256_no_salt(Config) when is_list(Config) -> + %% Hash = SHA-256 + %% IKM = 0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b (22 octets) + %% salt = (0 octets) + %% info = (0 octets) + %% L = 42 + + %% PRK = 0x19ef24a32c717b167f33a91d6f648bdf + %% 96596776afdb6377ac434c1c293ccb04 (32 octets) + %% OKM = 0x8da4e775a563c18f715f802a063c5a31 + %% b8a11f5c5ee1879ec3454e5f3c738d2d + %% 9d201395faa4b61a96c8 (42 octets) + IKM = hexstr2bin("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"), + Salt = <<>>, + Info = <<>>, + PRK = hexstr2bin("19ef24a32c717b167f33a91d6f648bdf" + "96596776afdb6377ac434c1c293ccb04"), + OKM = hexstr2bin("8da4e775a563c18f715f802a063c5a31" + "b8a11f5c5ee1879ec3454e5f3c738d2d" + "9d201395faa4b61a96c8"), + hkdf_test(sha256, Salt, IKM, PRK, Info, 42, OKM). + +sha_basic() -> + [{doc, " Basic test case with SHA-1"}]. +sha_basic(Config) when is_list(Config) -> + %% Hash = SHA-1 + %% IKM = 0x0b0b0b0b0b0b0b0b0b0b0b (11 octets) + %% salt = 0x000102030405060708090a0b0c (13 octets) + %% info = 0xf0f1f2f3f4f5f6f7f8f9 (10 octets) + %% L = 42 + + %% PRK = 0x9b6c18c432a7bf8f0e71c8eb88f4b30baa2ba243 (20 octets) + %% OKM = 0x085a01ea1b10f36933068b56efa5ad81 + %% a4f14b822f5b091568a9cdd4f155fda2 + %% c22e422478d305f3f896 (42 octets) + IKM = hexstr2bin("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"), + Salt = hexstr2bin("000102030405060708090a0b0c"), + Info = hexstr2bin("f0f1f2f3f4f5f6f7f8f9"), + PRK = hexstr2bin("077709362c2e32df0ddc3f0dc47bba63" + "90b6c73bb50f9c3122ec844ad7c2b3e5"), + OKM = hexstr2bin("3cb25f25faacd57a90434f64d0362f2a" + "2d2d0a90cf1a5a4c5db02d56ecc4c5bf" + "34007208d5b887185865"), + hkdf_test(sha256, Salt, IKM, PRK, Info, 42, OKM). + +sha_long() -> + [{doc, "Test with SHA-1 and longer inputs/outputs"}]. +sha_long(Config) when is_list(Config) -> + %% Hash = SHA-1 + %% IKM = 0x000102030405060708090a0b0c0d0e0f + %% 101112131415161718191a1b1c1d1e1f + %% 202122232425262728292a2b2c2d2e2f + %% 303132333435363738393a3b3c3d3e3f + %% 404142434445464748494a4b4c4d4e4f (80 octets) + %% salt = 0x606162636465666768696a6b6c6d6e6f + %% 707172737475767778797a7b7c7d7e7f + %% 808182838485868788898a8b8c8d8e8f + %% 909192939495969798999a9b9c9d9e9f + %% a0a1a2a3a4a5a6a7a8a9aaabacadaeaf (80 octets) + %% info = 0xb0b1b2b3b4b5b6b7b8b9babbbcbdbebf + %% c0c1c2c3c4c5c6c7c8c9cacbcccdcecf + %% d0d1d2d3d4d5d6d7d8d9dadbdcdddedf + %% e0e1e2e3e4e5e6e7e8e9eaebecedeeef + %% f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff (80 octets) + %% L = 82 + + %% PRK = 0x8adae09a2a307059478d309b26c4115a224cfaf6 (20 octets) + %% OKM = 0x0bd770a74d1160f7c9f12cd5912a06eb + %% ff6adcae899d92191fe4305673ba2ffe + %% 8fa3f1a4e5ad79f3f334b3b202b2173c + %% 486ea37ce3d397ed034c7f9dfeb15c5e + %% 927336d0441f4c4300e2cff0d0900b52 + %% d3b4 (82 octets) + IKM = hexstr2bin("000102030405060708090a0b0c0d0e0f" + "101112131415161718191a1b1c1d1e1f" + "202122232425262728292a2b2c2d2e2f" + "303132333435363738393a3b3c3d3e3f" + "404142434445464748494a4b4c4d4e4f" + ), + Salt = hexstr2bin("606162636465666768696a6b6c6d6e6f" + "707172737475767778797a7b7c7d7e7f" + "808182838485868788898a8b8c8d8e8f" + "909192939495969798999a9b9c9d9e9f" + "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf" + ), + Info = hexstr2bin("b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" + "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" + "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" + "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" + "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff" + ), + PRK = hexstr2bin("8adae09a2a307059478d309b26c4115a224cfaf6"), + OKM = hexstr2bin("0bd770a74d1160f7c9f12cd5912a06eb" + "ff6adcae899d92191fe4305673ba2ffe" + "8fa3f1a4e5ad79f3f334b3b202b2173c" + "486ea37ce3d397ed034c7f9dfeb15c5e" + "927336d0441f4c4300e2cff0d0900b52" + "d3b4" + ), + hkdf_test(sha, Salt, IKM, PRK, Info, 82, OKM). + +sha_no_salt() -> + [{doc, "Test with SHA-1 and zero-length salt/info"}]. +sha_no_salt(Config) when is_list(Config) -> + %% Hash = SHA-1 + %% IKM = 0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b (22 octets) + %% salt = (0 octets) + %% info = (0 octets) + %% L = 42 + + %% PRK = 0xda8c8a73c7fa77288ec6f5e7c297786aa0d32d01 (20 octets) + %% OKM = 0x0ac1af7002b3d761d1e55298da9d0506 + %% b9ae52057220a306e07b6b87e8df21d0 + %% ea00033de03984d34918 (42 octets) + IKM = hexstr2bin("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"), + Salt = <<>>, + Info = <<>>, + PRK = hexstr2bin("da8c8a73c7fa77288ec6f5e7c297786aa0d32d01"), + OKM = hexstr2bin("0ac1af7002b3d761d1e55298da9d0506" + "b9ae52057220a306e07b6b87e8df21d0" + "ea00033de03984d34918"), + hkdf_test(sha, Salt, IKM, PRK, Info, 42, OKM). + + +sha_default_salt() -> + [{doc, "Test with SHA-1, salt not provided (defaults to HashLen zero octets), + zero-length info"}]. +sha_default_salt(Config) when is_list(Config) -> + %% Hash = SHA-1 + %% IKM = 0x0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c (22 octets) + %% salt = not provided (defaults to HashLen zero octets) + %% info = (0 octets) + %% L = 42 + + %% PRK = 0x2adccada18779e7c2077ad2eb19d3f3e731385dd (20 octets) + %% OKM = 0x2c91117204d745f3500d636a62f64f0a + %% b3bae548aa53d423b0d1f27ebba6f5e5 + %% 673a081d70cce7acfc48 (42 octets) + IKM = hexstr2bin("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c"), + Salt = binary:copy(<<0>>, 20), + Info = <<>>, + PRK = hexstr2bin("2adccada18779e7c2077ad2eb19d3f3e731385dd"), + OKM = hexstr2bin("2c91117204d745f3500d636a62f64f0a" + "b3bae548aa53d423b0d1f27ebba6f5e5" + "673a081d70cce7acfc48"), + hkdf_test(sha, Salt, IKM, PRK, Info, 42, OKM). + +hkdf_test(HashAlg, Salt, KeyingMaterial, PsedoRandKey, ContextInfo, Length, Key) -> + PsedoRandKey = tls_v1:hkdf_extract(HashAlg, Salt, KeyingMaterial), + Key = tls_v1:hkdf_expand(PsedoRandKey, ContextInfo, Length, HashAlg). + +hexstr2bin(S) when is_binary(S) -> + list_to_binary(hexstr2list(binary_to_list(S))); +hexstr2bin(S) -> + list_to_binary(hexstr2list(S)). + +hexstr2list([$ |T]) -> + hexstr2list(T); +hexstr2list([X,Y|T]) -> + [mkint(X)*16 + mkint(Y) | hexstr2list(T)]; +hexstr2list([]) -> + []. +mkint(C) when $0 =< C, C =< $9 -> + C - $0; +mkint(C) when $A =< C, C =< $F -> + C - $A + 10; +mkint(C) when $a =< C, C =< $f -> + C - $a + 10. diff --git a/lib/ssl/test/ssl_session_cache_SUITE.erl b/lib/ssl/test/ssl_session_cache_SUITE.erl index 7f33fe3204..b71b15b028 100644 --- a/lib/ssl/test/ssl_session_cache_SUITE.erl +++ b/lib/ssl/test/ssl_session_cache_SUITE.erl @@ -186,7 +186,7 @@ session_cleanup() -> session_cleanup(Config) when is_list(Config) -> process_flag(trap_exit, true), ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl index c6a4a45dce..65b8998cc3 100644 --- a/lib/ssl/test/ssl_test_lib.erl +++ b/lib/ssl/test/ssl_test_lib.erl @@ -51,20 +51,20 @@ node_to_hostip(Node) -> Address. start_server(Args) -> - Result = spawn_link(?MODULE, run_server, [Args]), + Node = proplists:get_value(node, Args), + Result = spawn_link(Node, ?MODULE, run_server, [Args]), receive {listen, up} -> Result end. run_server(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]), + {ok, ListenSocket} = Transport:listen(Port, Options), Pid ! {listen, up}, send_selected_port(Pid, Port, ListenSocket), run_server(ListenSocket, Opts). @@ -90,13 +90,12 @@ do_run_server(_, ok = Result, Opts) -> Pid = proplists:get_value(from, Opts), Pid ! {self(), Result}; do_run_server(ListenSocket, AcceptSocket, Opts) -> - Node = proplists:get_value(node, Opts), Pid = proplists:get_value(from, Opts), Transport = proplists:get_value(transport, Opts, ssl), {Module, Function, Args} = proplists:get_value(mfa, Opts), ct:log("~p:~p~nServer: apply(~p,~p,~p)~n", [?MODULE,?LINE, Module, Function, [AcceptSocket | Args]]), - case rpc:call(Node, Module, Function, [AcceptSocket | Args]) of + case apply(Module, Function, [AcceptSocket | Args]) of no_result_msg -> ok; Msg -> @@ -110,8 +109,8 @@ do_run_server(ListenSocket, AcceptSocket, Opts) -> run_server(ListenSocket, [MFA | proplists:delete(mfa, Opts)]); close -> ct:log("~p:~p~nServer closing ~p ~n", [?MODULE,?LINE, self()]), - Result = rpc:call(Node, Transport, close, [AcceptSocket], 500), - Result1 = rpc:call(Node, Transport, close, [ListenSocket], 500), + Result = Transport:close(AcceptSocket), + Result1 = Transport:close(ListenSocket), ct:log("~p:~p~nResult ~p : ~p ~n", [?MODULE,?LINE, Result, Result1]); {ssl_closed, _} -> ok @@ -132,41 +131,37 @@ connect(#sslsocket{} = ListenSocket, Opts) -> remove_close_msg(ReconnectTimes), AcceptSocket end; -connect(ListenSocket, Opts) -> - Node = proplists:get_value(node, Opts), +connect(ListenSocket, _Opts) -> ct:log("~p:~p~ngen_tcp:accept(~p)~n", [?MODULE,?LINE, ListenSocket]), - {ok, AcceptSocket} = rpc:call(Node, gen_tcp, accept, - [ListenSocket]), + {ok, AcceptSocket} = gen_tcp:accept(ListenSocket), AcceptSocket. connect(_, _, 0, AcceptSocket, _, _, _) -> AcceptSocket; connect(ListenSocket, Node, _N, _, Timeout, SslOpts, cancel) -> ct:log("ssl:transport_accept(~p)~n", [ListenSocket]), - {ok, AcceptSocket} = rpc:call(Node, ssl, transport_accept, - [ListenSocket]), + {ok, AcceptSocket} = ssl:transport_accept(ListenSocket), ct:log("~p:~p~nssl:handshake(~p,~p,~p)~n", [?MODULE,?LINE, AcceptSocket, SslOpts,Timeout]), - case rpc:call(Node, ssl, handshake, [AcceptSocket, SslOpts, Timeout]) of + case ssl:handshake(AcceptSocket, SslOpts, Timeout) of {ok, Socket0, Ext} -> ct:log("Ext ~p:~n", [Ext]), ct:log("~p:~p~nssl:handshake_cancel(~p)~n", [?MODULE,?LINE, Socket0]), - rpc:call(Node, ssl, handshake_cancel, [Socket0]); + ssl:handshake_cancel(Socket0); Result -> ct:log("~p:~p~nssl:handshake@~p ret ~p",[?MODULE,?LINE, Node,Result]), Result end; connect(ListenSocket, Node, N, _, Timeout, SslOpts, [_|_] =ContOpts) -> ct:log("ssl:transport_accept(~p)~n", [ListenSocket]), - {ok, AcceptSocket} = rpc:call(Node, ssl, transport_accept, - [ListenSocket]), + {ok, AcceptSocket} = ssl:transport_accept(ListenSocket), ct:log("~p:~p~nssl:handshake(~p,~p,~p)~n", [?MODULE,?LINE, AcceptSocket, SslOpts,Timeout]), - case rpc:call(Node, ssl, handshake, [AcceptSocket, SslOpts, Timeout]) of + case ssl:handshake(AcceptSocket, SslOpts, Timeout) of {ok, Socket0, Ext} -> ct:log("Ext ~p:~n", [Ext]), ct:log("~p:~p~nssl:handshake_continue(~p,~p,~p)~n", [?MODULE,?LINE, Socket0, ContOpts,Timeout]), - case rpc:call(Node, ssl, handshake_continue, [Socket0, ContOpts, Timeout]) of + case ssl:handshake_continue(Socket0, ContOpts, Timeout) of {ok, Socket} -> connect(ListenSocket, Node, N-1, Socket, Timeout, SslOpts, ContOpts); Error -> @@ -179,35 +174,35 @@ connect(ListenSocket, Node, N, _, Timeout, SslOpts, [_|_] =ContOpts) -> end; connect(ListenSocket, Node, N, _, Timeout, [], ContOpts) -> ct:log("ssl:transport_accept(~p)~n", [ListenSocket]), - {ok, AcceptSocket} = rpc:call(Node, ssl, transport_accept, - [ListenSocket]), + {ok, AcceptSocket} = ssl:transport_accept(ListenSocket), ct:log("~p:~p~nssl:ssl_accept(~p, ~p)~n", [?MODULE,?LINE, AcceptSocket, Timeout]), - case rpc:call(Node, ssl, ssl_accept, [AcceptSocket, Timeout]) of - ok -> - connect(ListenSocket, Node, N-1, AcceptSocket, Timeout, [], ContOpts); + case ssl:handshake(AcceptSocket, Timeout) of + {ok, Socket} -> + connect(ListenSocket, Node, N-1, Socket, Timeout, [], ContOpts); Result -> - ct:log("~p:~p~nssl:ssl_accept@~p ret ~p",[?MODULE,?LINE, Node,Result]), + ct:log("~p:~p~nssl:handshake@~p ret ~p",[?MODULE,?LINE, Node,Result]), Result end; -connect(ListenSocket, Node, _, _, Timeout, Opts, _) -> +connect(ListenSocket, _Node, _, _, Timeout, Opts, _) -> ct:log("ssl:transport_accept(~p)~n", [ListenSocket]), - {ok, AcceptSocket} = rpc:call(Node, ssl, transport_accept, - [ListenSocket]), - ct:log("ssl:ssl_accept(~p,~p, ~p)~n", [AcceptSocket, Opts, Timeout]), - rpc:call(Node, ssl, ssl_accept, [AcceptSocket, Opts, Timeout]), + {ok, AcceptSocket} = ssl:transport_accept(ListenSocket), + ct:log("ssl:handshake(~p,~p, ~p)~n", [AcceptSocket, Opts, Timeout]), + ssl:handshake(AcceptSocket, Opts, Timeout), AcceptSocket. start_server_transport_abuse_socket(Args) -> - Result = spawn_link(?MODULE, transport_accept_abuse, [Args]), + Node = proplists:get_value(node, Args), + Result = spawn_link(Node, ?MODULE, transport_accept_abuse, [Args]), receive {listen, up} -> Result end. start_server_transport_control(Args) -> - Result = spawn_link(?MODULE, transport_switch_control, [Args]), + Node = proplists:get_value(node, Args), + Result = spawn_link(Node, ?MODULE, transport_switch_control, [Args]), receive {listen, up} -> Result @@ -215,35 +210,31 @@ start_server_transport_control(Args) -> 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]), + {ok, ListenSocket} = 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]), + {ok, AcceptSocket} = ssl:transport_accept(ListenSocket), + {error, _} = ssl:connection_information(AcceptSocket), + _ = 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]), + {ok, ListenSocket} = 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()]), + {ok, AcceptSocket} = ssl:transport_accept(ListenSocket), + ok = ssl:controlling_process(AcceptSocket, self()), Pid ! {self(), ok}. @@ -256,7 +247,8 @@ remove_close_msg(ReconnectTimes) -> end. start_client(Args) -> - Result = spawn_link(?MODULE, run_client_init, [lists:delete(return_socket, Args)]), + Node = proplists:get_value(node, Args), + Result = spawn_link(Node, ?MODULE, run_client_init, [lists:delete(return_socket, Args)]), receive {connected, Socket} -> case lists:member(return_socket, Args) of @@ -288,8 +280,8 @@ run_client(Opts) -> client_cont_loop(Node, Host, Port, Pid, Transport, Options, ContOpts, Opts) end. -client_loop(Node, Host, Port, Pid, Transport, Options, Opts) -> - case rpc:call(Node, Transport, connect, [Host, Port, Options]) of +client_loop(_Node, Host, Port, Pid, Transport, Options, Opts) -> + case Transport:connect(Host, Port, Options) of {ok, Socket} -> Pid ! {connected, Socket}, ct:log("~p:~p~nClient: connected~n", [?MODULE,?LINE]), @@ -299,7 +291,7 @@ client_loop(Node, Host, Port, Pid, Transport, Options, Opts) -> {Module, Function, Args} = proplists:get_value(mfa, Opts), ct:log("~p:~p~nClient: apply(~p,~p,~p)~n", [?MODULE,?LINE, Module, Function, [Socket | Args]]), - case rpc:call(Node, Module, Function, [Socket | Args]) of + case apply(Module, Function, [Socket | Args]) of no_result_msg -> ok; Msg -> @@ -309,7 +301,7 @@ client_loop(Node, Host, Port, Pid, Transport, Options, Opts) -> receive close -> ct:log("~p:~p~nClient closing~n", [?MODULE,?LINE]), - rpc:call(Node, Transport, close, [Socket]); + Transport:close(Socket); {ssl_closed, Socket} -> ok; {gen_tcp, closed} -> @@ -339,16 +331,13 @@ client_loop(Node, Host, Port, Pid, Transport, Options, Opts) -> end; {error, Reason} -> ct:log("~p:~p~nClient: connection failed: ~p ~n", [?MODULE,?LINE, Reason]), - Pid ! {connect_failed, Reason}; - {badrpc,BadRPC} -> - ct:log("~p:~p~nBad rpc: ~p",[?MODULE,?LINE, BadRPC]), - Pid ! {connect_failed, {badrpc,BadRPC}} + Pid ! {connect_failed, Reason} end. -client_cont_loop(Node, Host, Port, Pid, Transport, Options, cancel, _Opts) -> - case rpc:call(Node, Transport, connect, [Host, Port, Options]) of +client_cont_loop(_Node, Host, Port, Pid, Transport, Options, cancel, _Opts) -> + case Transport:connect(Host, Port, Options) of {ok, Socket, _} -> - Result = rpc:call(Node, Transport, handshake_cancel, [Socket]), + Result = Transport:handshake_cancel(Socket), ct:log("~p:~p~nClient: Cancel: ~p ~n", [?MODULE,?LINE, Result]), Pid ! {connect_failed, Result}; {error, Reason} -> @@ -356,17 +345,17 @@ client_cont_loop(Node, Host, Port, Pid, Transport, Options, cancel, _Opts) -> Pid ! {connect_failed, Reason} end; -client_cont_loop(Node, Host, Port, Pid, Transport, Options, ContOpts, Opts) -> - case rpc:call(Node, Transport, connect, [Host, Port, Options]) of +client_cont_loop(_Node, Host, Port, Pid, Transport, Options, ContOpts, Opts) -> + case Transport:connect(Host, Port, Options) of {ok, Socket0, _} -> ct:log("~p:~p~nClient: handshake_continue(~p, ~p, infinity) ~n", [?MODULE, ?LINE, Socket0, ContOpts]), - case rpc:call(Node, Transport, handshake_continue, [Socket0, ContOpts]) of + case Transport:handshake_continue(Socket0, ContOpts) of {ok, Socket} -> Pid ! {connected, Socket}, {Module, Function, Args} = proplists:get_value(mfa, Opts), ct:log("~p:~p~nClient: apply(~p,~p,~p)~n", [?MODULE,?LINE, Module, Function, [Socket | Args]]), - case rpc:call(Node, Module, Function, [Socket | Args]) of + case apply(Module, Function, [Socket | Args]) of no_result_msg -> ok; Msg -> @@ -514,13 +503,6 @@ wait_for_result(Pid, Msg) -> %% Unexpected end. -user_lookup(psk, _Identity, UserState) -> - {ok, UserState}; -user_lookup(srp, Username, _UserState) -> - Salt = ssl_cipher:random_bytes(16), - UserPassHash = crypto:hash(sha, [Salt, crypto:hash(sha, [Username, <<$:>>, <<"secret">>])]), - {ok, {srp_1024, Salt, UserPassHash}}. - cert_options(Config) -> ClientCaCertFile = filename:join([proplists:get_value(priv_dir, Config), "client", "cacerts.pem"]), @@ -903,14 +885,14 @@ make_ecdh_rsa_cert(Config) -> end. start_upgrade_server(Args) -> - Result = spawn_link(?MODULE, run_upgrade_server, [Args]), + Node = proplists:get_value(node, Args), + Result = spawn_link(Node, ?MODULE, run_upgrade_server, [Args]), receive {listen, up} -> Result end. run_upgrade_server(Opts) -> - Node = proplists:get_value(node, Opts), Port = proplists:get_value(port, Opts), TimeOut = proplists:get_value(timeout, Opts, infinity), TcpOptions = proplists:get_value(tcp_options, Opts), @@ -918,43 +900,41 @@ run_upgrade_server(Opts) -> Pid = proplists:get_value(from, Opts), ct:log("~p:~p~ngen_tcp:listen(~p, ~p)~n", [?MODULE,?LINE, Port, TcpOptions]), - {ok, ListenSocket} = rpc:call(Node, gen_tcp, listen, [Port, TcpOptions]), + {ok, ListenSocket} = gen_tcp:listen(Port, TcpOptions), Pid ! {listen, up}, send_selected_port(Pid, Port, ListenSocket), ct:log("~p:~p~ngen_tcp:accept(~p)~n", [?MODULE,?LINE, ListenSocket]), - {ok, AcceptSocket} = rpc:call(Node, gen_tcp, accept, [ListenSocket]), + {ok, AcceptSocket} = gen_tcp:accept(ListenSocket), try {ok, SslAcceptSocket} = case TimeOut of infinity -> - ct:log("~p:~p~nssl:ssl_accept(~p, ~p)~n", + ct:log("~p:~p~nssl:handshake(~p, ~p)~n", [?MODULE,?LINE, AcceptSocket, SslOptions]), - rpc:call(Node, ssl, ssl_accept, - [AcceptSocket, SslOptions]); + ssl:handshake(AcceptSocket, SslOptions); _ -> - ct:log("~p:~p~nssl:ssl_accept(~p, ~p, ~p)~n", + ct:log("~p:~p~nssl:handshake(~p, ~p, ~p)~n", [?MODULE,?LINE, AcceptSocket, SslOptions, TimeOut]), - rpc:call(Node, ssl, ssl_accept, - [AcceptSocket, SslOptions, TimeOut]) + ssl:handshake(AcceptSocket, SslOptions, TimeOut) end, {Module, Function, Args} = proplists:get_value(mfa, Opts), - Msg = rpc:call(Node, Module, Function, [SslAcceptSocket | Args]), + Msg = apply(Module, Function, [SslAcceptSocket | Args]), ct:log("~p:~p~nUpgrade Server Msg: ~p ~n", [?MODULE,?LINE, Msg]), Pid ! {self(), Msg}, receive close -> ct:log("~p:~p~nUpgrade Server closing~n", [?MODULE,?LINE]), - rpc:call(Node, ssl, close, [SslAcceptSocket]) + ssl:close(SslAcceptSocket) end catch error:{badmatch, Error} -> Pid ! {self(), Error} end. start_upgrade_client(Args) -> - spawn_link(?MODULE, run_upgrade_client, [Args]). + Node = proplists:get_value(node, Args), + spawn_link(Node, ?MODULE, run_upgrade_client, [Args]). run_upgrade_client(Opts) -> - Node = proplists:get_value(node, Opts), Host = proplists:get_value(host, Opts), Port = proplists:get_value(port, Opts), Pid = proplists:get_value(from, Opts), @@ -963,34 +943,34 @@ run_upgrade_client(Opts) -> ct:log("~p:~p~ngen_tcp:connect(~p, ~p, ~p)~n", [?MODULE,?LINE, Host, Port, TcpOptions]), - {ok, Socket} = rpc:call(Node, gen_tcp, connect, [Host, Port, TcpOptions]), + {ok, Socket} = gen_tcp:connect(Host, Port, TcpOptions), send_selected_port(Pid, Port, Socket), ct:log("~p:~p~nssl:connect(~p, ~p)~n", [?MODULE,?LINE, Socket, SslOptions]), - {ok, SslSocket} = rpc:call(Node, ssl, connect, [Socket, SslOptions]), + {ok, SslSocket} = ssl:connect(Socket, SslOptions), {Module, Function, Args} = proplists:get_value(mfa, Opts), ct:log("~p:~p~napply(~p, ~p, ~p)~n", [?MODULE,?LINE, Module, Function, [SslSocket | Args]]), - Msg = rpc:call(Node, Module, Function, [SslSocket | Args]), + Msg = apply(Module, Function, [SslSocket | Args]), ct:log("~p:~p~nUpgrade Client Msg: ~p ~n", [?MODULE,?LINE, Msg]), Pid ! {self(), Msg}, receive close -> ct:log("~p:~p~nUpgrade Client closing~n", [?MODULE,?LINE]), - rpc:call(Node, ssl, close, [SslSocket]) + ssl:close(SslSocket) end. start_upgrade_server_error(Args) -> - Result = spawn_link(?MODULE, run_upgrade_server_error, [Args]), + Node = proplists:get_value(node, Args), + Result = spawn_link(Node,?MODULE, run_upgrade_server_error, [Args]), receive {listen, up} -> Result end. run_upgrade_server_error(Opts) -> - Node = proplists:get_value(node, Opts), Port = proplists:get_value(port, Opts), TimeOut = proplists:get_value(timeout, Opts, infinity), TcpOptions = proplists:get_value(tcp_options, Opts), @@ -998,22 +978,20 @@ run_upgrade_server_error(Opts) -> Pid = proplists:get_value(from, Opts), ct:log("~p:~p~ngen_tcp:listen(~p, ~p)~n", [?MODULE,?LINE, Port, TcpOptions]), - {ok, ListenSocket} = rpc:call(Node, gen_tcp, listen, [Port, TcpOptions]), + {ok, ListenSocket} = gen_tcp:listen(Port, TcpOptions), Pid ! {listen, up}, send_selected_port(Pid, Port, ListenSocket), ct:log("~p:~p~ngen_tcp:accept(~p)~n", [?MODULE,?LINE, ListenSocket]), - {ok, AcceptSocket} = rpc:call(Node, gen_tcp, accept, [ListenSocket]), + {ok, AcceptSocket} = gen_tcp:accept(ListenSocket), Error = case TimeOut of infinity -> - ct:log("~p:~p~nssl:ssl_accept(~p, ~p)~n", + ct:log("~p:~p~nssl:handshake(~p, ~p)~n", [?MODULE,?LINE, AcceptSocket, SslOptions]), - rpc:call(Node, ssl, ssl_accept, - [AcceptSocket, SslOptions]); + ssl:handshake(AcceptSocket, SslOptions); _ -> - ct:log("~p:~p~nssl:ssl_accept(~p, ~p, ~p)~n", + ct:log("~p:~p~nssl:ssl_handshake(~p, ~p, ~p)~n", [?MODULE,?LINE, AcceptSocket, SslOptions, TimeOut]), - rpc:call(Node, ssl, ssl_accept, - [AcceptSocket, SslOptions, TimeOut]) + ssl:handshake(AcceptSocket, SslOptions, TimeOut) end, Pid ! {self(), Error}. @@ -1025,32 +1003,31 @@ start_server_error(Args) -> end. run_server_error(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]), - case rpc:call(Node, Transport, listen, [Port, Options]) of + case Transport:listen(Port, Options) of {ok, #sslsocket{} = ListenSocket} -> %% To make sure error_client will %% get {error, closed} and not {error, connection_refused} Pid ! {listen, up}, send_selected_port(Pid, Port, ListenSocket), ct:log("~p:~p~nssl:transport_accept(~p)~n", [?MODULE,?LINE, ListenSocket]), - case rpc:call(Node, Transport, transport_accept, [ListenSocket]) of + case Transport:transport_accept(ListenSocket) of {error, _} = Error -> Pid ! {self(), Error}; {ok, AcceptSocket} -> ct:log("~p:~p~nssl:ssl_accept(~p)~n", [?MODULE,?LINE, AcceptSocket]), - Error = rpc:call(Node, ssl, ssl_accept, [AcceptSocket]), + Error = ssl:handshake(AcceptSocket), Pid ! {self(), Error} end; {ok, ListenSocket} -> Pid ! {listen, up}, send_selected_port(Pid, Port, ListenSocket), ct:log("~p:~p~n~p:accept(~p)~n", [?MODULE,?LINE, Transport, ListenSocket]), - case rpc:call(Node, Transport, accept, [ListenSocket]) of + case Transport:accept(ListenSocket) of {error, _} = Error -> Pid ! {self(), Error} end; @@ -1062,17 +1039,17 @@ run_server_error(Opts) -> end. start_client_error(Args) -> - spawn_link(?MODULE, run_client_error, [Args]). + Node = proplists:get_value(node, Args), + spawn_link(Node, ?MODULE, run_client_error, [Args]). run_client_error(Opts) -> - Node = proplists:get_value(node, Opts), Host = proplists:get_value(host, Opts), Port = proplists:get_value(port, Opts), Pid = proplists:get_value(from, Opts), Transport = proplists:get_value(transport, Opts, ssl), Options = proplists:get_value(options, Opts), ct:log("~p:~p~nssl:connect(~p, ~p, ~p)~n", [?MODULE,?LINE, Host, Port, Options]), - Error = rpc:call(Node, Transport, connect, [Host, Port, Options]), + Error = Transport:connect(Host, Port, Options), Pid ! {self(), Error}. accepters(N) -> @@ -1106,6 +1083,35 @@ ecc_test_error(COpts, SOpts, CECCOpts, SECCOpts, Config) -> Client = start_client_ecc_error(erlang, Port, COpts, CECCOpts, Config), check_server_alert(Server, Client, insufficient_security). +start_basic_client(openssl, Version, Port, ClientOpts) -> + Cert = proplists:get_value(certfile, ClientOpts), + Key = proplists:get_value(keyfile, ClientOpts), + CA = proplists:get_value(cacertfile, ClientOpts), + Groups0 = proplists:get_value(groups, ClientOpts), + Exe = "openssl", + Args0 = ["s_client", "-verify", "2", "-port", integer_to_list(Port), + ssl_test_lib:version_flag(Version), + "-CAfile", CA, "-host", "localhost", "-msg", "-debug"], + Args1 = + case Groups0 of + undefined -> + Args0; + G -> + Args0 ++ ["-groups", G] + end, + Args = + case {Cert, Key} of + {C, K} when C =:= undefined orelse + K =:= undefined -> + Args1; + {C, K} -> + Args1 ++ ["-cert", C, "-key", K] + end, + + OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args), + true = port_command(OpenSslPort, "Hello world"), + OpenSslPort. + start_client(openssl, Port, ClientOpts, Config) -> Cert = proplists:get_value(certfile, ClientOpts), Key = proplists:get_value(keyfile, ClientOpts), @@ -1335,13 +1341,13 @@ common_ciphers(crypto) -> common_ciphers(openssl) -> OpenSslSuites = string:tokens(string:strip(os:cmd("openssl ciphers"), right, $\n), ":"), - [ssl_cipher_format:suite_definition(S) + [ssl_cipher_format:suite_bin_to_map(S) || S <- ssl_cipher:suites(tls_record:highest_protocol_version([])), - lists:member(ssl_cipher_format:openssl_suite_name(S), OpenSslSuites) + lists:member(ssl_cipher_format:suite_map_to_openssl_str(ssl_cipher_format:suite_bin_to_map(S)), OpenSslSuites) ]. available_suites(Version) -> - [ssl_cipher_format:suite_definition(Suite) || + [ssl_cipher_format:suite_bin_to_map(Suite) || Suite <- ssl_cipher:filter_suites(ssl_cipher:suites(Version))]. @@ -1414,7 +1420,7 @@ string_regex_filter(_Str, _Search) -> false. ecdh_dh_anonymous_suites(Version) -> - ssl:filter_cipher_suites([ssl_cipher_format:suite_definition(S) || S <- ssl_cipher:anonymous_suites(Version)], + ssl:filter_cipher_suites([ssl_cipher_format:suite_bin_to_map(S) || S <- ssl_cipher:anonymous_suites(Version)], [{key_exchange, fun(dh_anon) -> true; @@ -1424,7 +1430,7 @@ ecdh_dh_anonymous_suites(Version) -> false end}]). psk_suites({3,_} = Version) -> - ssl:filter_cipher_suites([ssl_cipher_format:suite_definition(S) || S <- ssl_cipher:psk_suites(Version)], []); + ssl:filter_cipher_suites([ssl_cipher_format:suite_bin_to_map(S) || S <- ssl_cipher:psk_suites(Version)], []); psk_suites(Version) -> ssl:filter_cipher_suites(psk_suites(dtls_v1:corresponding_tls_version(Version)), [{cipher, @@ -1435,7 +1441,7 @@ psk_suites(Version) -> end}]). psk_anon_suites({3,_} = Version) -> - ssl:filter_cipher_suites([ssl_cipher_format:suite_definition(S) || S <- ssl_cipher:psk_suites_anon(Version)], + ssl:filter_cipher_suites([ssl_cipher_format:suite_bin_to_map(S) || S <- ssl_cipher:psk_suites_anon(Version)], [{key_exchange, fun(psk) -> true; @@ -1458,7 +1464,7 @@ psk_anon_suites(Version) -> srp_suites() -> - ssl:filter_cipher_suites([ssl_cipher_format:suite_definition(S) || S <- ssl_cipher:srp_suites()], + ssl:filter_cipher_suites([ssl_cipher_format:suite_bin_to_map(S) || S <- ssl_cipher:srp_suites()], [{key_exchange, fun(srp_rsa) -> true; @@ -1466,10 +1472,10 @@ srp_suites() -> false end}]). srp_anon_suites() -> - ssl:filter_cipher_suites([ssl_cipher_format:suite_definition(S) || S <- ssl_cipher:srp_suites_anon()], + ssl:filter_cipher_suites([ssl_cipher_format:suite_bin_to_map(S) || S <- ssl_cipher:srp_suites_anon()], []). srp_dss_suites() -> - ssl:filter_cipher_suites([ssl_cipher_format:suite_definition(S) || S <- ssl_cipher:srp_suites()], + ssl:filter_cipher_suites([ssl_cipher_format:suite_bin_to_map(S) || S <- ssl_cipher:srp_suites()], [{key_exchange, fun(srp_dss) -> true; @@ -1477,14 +1483,14 @@ srp_dss_suites() -> false end}]). chacha_suites(Version) -> - [ssl_cipher_format:suite_definition(S) || S <- ssl_cipher:filter_suites(ssl_cipher:chacha_suites(Version))]. + [ssl_cipher_format:suite_bin_to_map(S) || S <- ssl_cipher:filter_suites(ssl_cipher:chacha_suites(Version))]. rc4_suites(Version) -> - ssl:filter_cipher_suites([ssl_cipher_format:suite_definition(S) || S <-ssl_cipher:rc4_suites(Version)], []). + ssl:filter_cipher_suites([ssl_cipher_format:suite_bin_to_map(S) || S <-ssl_cipher:rc4_suites(Version)], []). des_suites(Version) -> - ssl:filter_cipher_suites([ssl_cipher_format:suite_definition(S) || S <-ssl_cipher:des_suites(Version)], []). + ssl:filter_cipher_suites([ssl_cipher_format:suite_bin_to_map(S) || S <-ssl_cipher:des_suites(Version)], []). tuple_to_map({Kex, Cipher, Mac}) -> #{key_exchange => Kex, @@ -1511,10 +1517,13 @@ cipher_result(Socket, Result) -> ct:log("~p:~p~nSuccessfull connect: ~p~n", [?MODULE,?LINE, Result]), %% Importante to send two packets here %% to properly test "cipher state" handling - ssl:send(Socket, "Hello\n"), - "Hello\n" = active_recv(Socket, length( "Hello\n")), - ssl:send(Socket, " world\n"), - " world\n" = active_recv(Socket, length(" world\n")), + Hello = "Hello\n", + World = " world\n", + ssl:send(Socket, Hello), + ct:sleep(500), + ssl:send(Socket, World), + Expected = Hello ++ World, + Expected = active_recv(Socket, length(Expected)), ok. session_info_result(Socket) -> @@ -1588,7 +1597,12 @@ init_tls_version(Version, Config) -> clean_tls_version(Config) -> proplists:delete(protocol_opts, proplists:delete(protocol, Config)). - + +sufficient_crypto_support(Version) + when Version == 'tlsv1.3' -> + CryptoSupport = crypto:supports(), + lists:member(rsa_pkcs1_pss_padding, proplists:get_value(rsa_opts, CryptoSupport)) andalso + lists:member(x448, proplists:get_value(curves, CryptoSupport)); sufficient_crypto_support(Version) when Version == 'tlsv1.2'; Version == 'dtlsv1.2' -> CryptoSupport = crypto:supports(), @@ -1742,6 +1756,15 @@ is_sane_ecc(crypto) -> is_sane_ecc(_) -> sufficient_crypto_support(cipher_ec). +is_sane_oppenssl_client() -> + [{_,_, Bin}] = crypto:info_lib(), + case binary_to_list(Bin) of + "OpenSSL 0.9" ++ _ -> + false; + _ -> + true + end. + is_fips(openssl) -> VersionStr = os:cmd("openssl version"), case re:split(VersionStr, "fips") of @@ -1850,6 +1873,14 @@ check_sane_openssl_version(Version) -> case {Version, os:cmd("openssl version")} of {'sslv3', "OpenSSL 1.0.2" ++ _} -> false; + {'dtlsv1', "OpenSSL 0" ++ _} -> + false; + {'dtlsv1.2', "OpenSSL 0" ++ _} -> + false; + {'dtlsv1.2', "OpenSSL 1.0.2" ++ _} -> + false; + {'dtlsv1', "OpenSSL 1.0.0" ++ _} -> + false; {'dtlsv1', _} -> not is_fips(openssl); {'dtlsv1.2', _} -> @@ -1862,18 +1893,10 @@ check_sane_openssl_version(Version) -> false; {'tlsv1.1', "OpenSSL 1.0.0" ++ _} -> false; - {'dtlsv1.2', "OpenSSL 1.0.2" ++ _} -> - false; - {'dtlsv1', "OpenSSL 1.0.0" ++ _} -> - false; {'tlsv1.2', "OpenSSL 0" ++ _} -> false; {'tlsv1.1', "OpenSSL 0" ++ _} -> false; - {'dtlsv1', "OpenSSL 0" ++ _} -> - false; - {'dtlsv1.2', "OpenSSL 0" ++ _} -> - false; {_, _} -> true end; @@ -1906,6 +1929,8 @@ version_flag('tlsv1.1') -> "-tls1_1"; version_flag('tlsv1.2') -> "-tls1_2"; +version_flag('tlsv1.3') -> + "-tls1_3"; version_flag(sslv3) -> "-ssl3"; version_flag(sslv2) -> @@ -1916,10 +1941,10 @@ version_flag('dtlsv1') -> "-dtls1". filter_suites([Cipher | _] = Ciphers, AtomVersion) when is_list(Cipher)-> - filter_suites([ssl_cipher_format:openssl_suite(S) || S <- Ciphers], + filter_suites([ssl_cipher_format:suite_openssl_str_to_map(S) || S <- Ciphers], AtomVersion); filter_suites([Cipher | _] = Ciphers, AtomVersion) when is_binary(Cipher)-> - filter_suites([ssl_cipher_format:suite_definition(S) || S <- Ciphers], + filter_suites([ssl_cipher_format:suite_bin_to_map(S) || S <- Ciphers], AtomVersion); filter_suites(Ciphers0, AtomVersion) -> Version = tls_version(AtomVersion), @@ -1931,7 +1956,7 @@ filter_suites(Ciphers0, AtomVersion) -> ++ ssl_cipher:srp_suites_anon() ++ ssl_cipher:rc4_suites(Version), Supported1 = ssl_cipher:filter_suites(Supported0), - Supported2 = [ssl_cipher_format:suite_definition(S) || S <- Supported1], + Supported2 = [ssl_cipher_format:suite_bin_to_map(S) || S <- Supported1], [Cipher || Cipher <- Ciphers0, lists:member(Cipher, Supported2)]. -define(OPENSSL_QUIT, "Q\n"). @@ -2342,4 +2367,10 @@ reuse_session(ClientOpts, ServerOpts, Config) -> ssl_test_lib:close(Server1), ssl_test_lib:close(Client3), ssl_test_lib:close(Client4). - + +user_lookup(psk, _Identity, UserState) -> + {ok, UserState}; +user_lookup(srp, Username, _UserState) -> + Salt = ssl_cipher:random_bytes(16), + UserPassHash = crypto:hash(sha, [Salt, crypto:hash(sha, [Username, <<$:>>, <<"secret">>])]), + {ok, {srp_1024, Salt, UserPassHash}}. diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl index df84411b6d..07abddbcf7 100644 --- a/lib/ssl/test/ssl_to_openssl_SUITE.erl +++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl @@ -39,16 +39,14 @@ all() -> case ssl_test_lib:openssl_sane_dtls() of true -> - [{group, basic}, - {group, 'tlsv1.2'}, + [{group, 'tlsv1.2'}, {group, 'tlsv1.1'}, {group, 'tlsv1'}, {group, 'sslv3'}, {group, 'dtlsv1.2'}, {group, 'dtlsv1'}]; false -> - [{group, basic}, - {group, 'tlsv1.2'}, + [{group, 'tlsv1.2'}, {group, 'tlsv1.1'}, {group, 'tlsv1'}, {group, 'sslv3'}] @@ -57,8 +55,7 @@ all() -> groups() -> case ssl_test_lib:openssl_sane_dtls() of true -> - [{basic, [], basic_tests()}, - {'tlsv1.2', [], all_versions_tests() ++ alpn_tests() ++ npn_tests() ++ sni_server_tests()}, + [{'tlsv1.2', [], all_versions_tests() ++ alpn_tests() ++ npn_tests() ++ sni_server_tests()}, {'tlsv1.1', [], all_versions_tests() ++ alpn_tests() ++ npn_tests() ++ sni_server_tests()}, {'tlsv1', [], all_versions_tests()++ alpn_tests() ++ npn_tests() ++ sni_server_tests()}, {'sslv3', [], all_versions_tests()}, @@ -66,20 +63,13 @@ groups() -> {'dtlsv1', [], dtls_all_versions_tests()} ]; false -> - [{basic, [], basic_tests()}, - {'tlsv1.2', [], all_versions_tests() ++ alpn_tests() ++ npn_tests() ++ sni_server_tests()}, + [{'tlsv1.2', [], all_versions_tests() ++ alpn_tests() ++ npn_tests() ++ sni_server_tests()}, {'tlsv1.1', [], all_versions_tests() ++ alpn_tests() ++ npn_tests() ++ sni_server_tests()}, {'tlsv1', [], all_versions_tests()++ alpn_tests() ++ npn_tests() ++ sni_server_tests()}, {'sslv3', [], all_versions_tests()} ] end. - -basic_tests() -> - [basic_erlang_client_openssl_server, - basic_erlang_server_openssl_client, - expired_session - ]. - + all_versions_tests() -> [ erlang_client_openssl_server, @@ -191,16 +181,6 @@ end_per_suite(_Config) -> ssl:stop(), application:stop(crypto). -init_per_group(basic, Config0) -> - case ssl_test_lib:supports_ssl_tls_version('tlsv1.2') - orelse ssl_test_lib:supports_ssl_tls_version('tlsv1.1') - orelse ssl_test_lib:supports_ssl_tls_version('tlsv1') - of - true -> - ssl_test_lib:clean_tls_version(Config0); - false -> - {skip, "only sslv3 supported by OpenSSL"} - end; init_per_group(GroupName, Config) -> case ssl_test_lib:is_tls_version(GroupName) of @@ -243,7 +223,7 @@ init_per_testcase(TestCase, Config) when TestCase == erlang_server_openssl_client_dsa_cert; TestCase == erlang_client_openssl_server_dsa_cert; TestCase == erlang_server_openssl_client_dsa_cert -> - case ssl_test_lib:openssl_dsa_support() of + case ssl_test_lib:openssl_dsa_support() andalso ssl_test_lib:is_sane_oppenssl_client() of true -> special_init(TestCase, Config); false -> @@ -344,7 +324,16 @@ special_init(TestCase, Config0) ]} ]}]} | Config0], check_openssl_sni_support(Config); - +special_init(TestCase, Config) + when TestCase == erlang_server_openssl_client; + TestCase == erlang_server_openssl_client_client_cert; + TestCase == erlang_server_openssl_client_reuse_session -> + case ssl_test_lib:is_sane_oppenssl_client() of + true -> + Config; + false -> + {skip, "Broken OpenSSL client"} + end; special_init(_, Config) -> Config. @@ -357,85 +346,7 @@ end_per_testcase(_, Config) -> %%-------------------------------------------------------------------- %% Test Cases -------------------------------------------------------- %%-------------------------------------------------------------------- -basic_erlang_client_openssl_server() -> - [{doc,"Test erlang client with openssl server"}]. -basic_erlang_client_openssl_server(Config) when is_list(Config) -> - process_flag(trap_exit, true), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), - ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), - - {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), - - Data = "From openssl to erlang", - - Port = ssl_test_lib:inet_port(node()), - CertFile = proplists:get_value(certfile, ServerOpts), - KeyFile = proplists:get_value(keyfile, ServerOpts), - - Exe = "openssl", - Args = ["s_server", "-accept", integer_to_list(Port), - "-cert", CertFile, "-key", KeyFile], - - OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), - - - ssl_test_lib:wait_for_openssl_server(Port, tls), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, - erlang_ssl_receive, [Data]}}, - {options, ClientOpts}]), - true = port_command(OpensslPort, Data), - - ssl_test_lib:check_result(Client, ok), - - %% Clean close down! Server needs to be closed first !! - ssl_test_lib:close_port(OpensslPort), - ssl_test_lib:close(Client), - process_flag(trap_exit, false). - -%%-------------------------------------------------------------------- -basic_erlang_server_openssl_client() -> - [{doc,"Test erlang server with openssl client"}]. -basic_erlang_server_openssl_client(Config) when is_list(Config) -> - process_flag(trap_exit, true), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), - - {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Data = "From openssl to erlang", - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, - {options,ServerOpts}]), - - Port = ssl_test_lib:inet_port(Server), - - Exe = "openssl", - Args = case no_low_flag("-no_ssl2") of - [] -> - ["s_client", "-connect", hostname_format(Hostname) ++ - ":" ++ integer_to_list(Port), no_low_flag("-no_ssl3") - | workaround_openssl_s_clinent()]; - Flag -> - ["s_client", "-connect", hostname_format(Hostname) ++ - ":" ++ integer_to_list(Port), no_low_flag("-no_ssl3"), Flag - | workaround_openssl_s_clinent()] - end, - - OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args), - true = port_command(OpenSslPort, Data), - - ssl_test_lib:check_result(Server, ok), - - %% Clean close down! Server needs to be closed first !! - ssl_test_lib:close(Server), - ssl_test_lib:close_port(OpenSslPort), - process_flag(trap_exit, false). -%%-------------------------------------------------------------------- erlang_client_openssl_server() -> [{doc,"Test erlang client with openssl server"}]. erlang_client_openssl_server(Config) when is_list(Config) -> @@ -1161,7 +1072,7 @@ erlang_client_bad_openssl_server(Config) when is_list(Config) -> Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {ssl_test_lib, no_result_msg, []}}, + {mfa, {ssl_test_lib, no_result, []}}, {options, [{versions, [Version]} | ClientOpts]}]), @@ -1249,7 +1160,7 @@ ssl2_erlang_server_openssl_client(Config) when is_list(Config) -> ct:log("Ports ~p~n", [[erlang:port_info(P) || P <- erlang:ports()]]), ssl_test_lib:consume_port_exit(OpenSslPort), - ssl_test_lib:check_server_alert(Server, bad_record_mac), + ssl_test_lib:check_server_alert(Server, unexpected_message), process_flag(trap_exit, false). %%-------------------------------------------------------------------- @@ -1550,6 +1461,7 @@ send_and_hostname(SSLSocket) -> end. erlang_server_openssl_client_sni_test(Config, SNIHostname, ExpectedSNIHostname, ExpectedCN) -> + Version = ssl_test_lib:protocol_version(Config), ct:log("Start running handshake, Config: ~p, SNIHostname: ~p, ExpectedSNIHostname: ~p, ExpectedCN: ~p", [Config, SNIHostname, ExpectedSNIHostname, ExpectedCN]), ServerOptions = proplists:get_value(sni_server_opts, Config) ++ proplists:get_value(server_rsa_opts, Config), {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -1560,9 +1472,9 @@ erlang_server_openssl_client_sni_test(Config, SNIHostname, ExpectedSNIHostname, Exe = "openssl", ClientArgs = case SNIHostname of undefined -> - openssl_client_args(ssl_test_lib:supports_ssl_tls_version(sslv2), Hostname,Port); + openssl_client_args(Version, Hostname,Port); _ -> - openssl_client_args(ssl_test_lib:supports_ssl_tls_version(sslv2), Hostname, Port, SNIHostname) + openssl_client_args(Version, Hostname, Port, SNIHostname) end, ClientPort = ssl_test_lib:portable_open_port(Exe, ClientArgs), @@ -1573,6 +1485,7 @@ erlang_server_openssl_client_sni_test(Config, SNIHostname, ExpectedSNIHostname, erlang_server_openssl_client_sni_test_sni_fun(Config, SNIHostname, ExpectedSNIHostname, ExpectedCN) -> + Version = ssl_test_lib:protocol_version(Config), ct:log("Start running handshake for sni_fun, Config: ~p, SNIHostname: ~p, ExpectedSNIHostname: ~p, ExpectedCN: ~p", [Config, SNIHostname, ExpectedSNIHostname, ExpectedCN]), [{sni_hosts, ServerSNIConf}] = proplists:get_value(sni_server_opts, Config), SNIFun = fun(Domain) -> proplists:get_value(Domain, ServerSNIConf, undefined) end, @@ -1585,9 +1498,9 @@ erlang_server_openssl_client_sni_test_sni_fun(Config, SNIHostname, ExpectedSNIHo Exe = "openssl", ClientArgs = case SNIHostname of undefined -> - openssl_client_args(ssl_test_lib:supports_ssl_tls_version(sslv2), Hostname,Port); + openssl_client_args(Version, Hostname,Port); _ -> - openssl_client_args(ssl_test_lib:supports_ssl_tls_version(sslv2), Hostname, Port, SNIHostname) + openssl_client_args(Version, Hostname, Port, SNIHostname) end, ClientPort = ssl_test_lib:portable_open_port(Exe, ClientArgs), @@ -1998,13 +1911,19 @@ send_wait_send(Socket, [ErlData, OpenSslData]) -> check_openssl_sni_support(Config) -> HelpText = os:cmd("openssl s_client --help"), - case string:str(HelpText, "-servername") of - 0 -> - {skip, "Current openssl doesn't support SNI"}; - _ -> - Config + case ssl_test_lib:is_sane_oppenssl_client() of + true -> + case string:str(HelpText, "-servername") of + 0 -> + {skip, "Current openssl doesn't support SNI"}; + _ -> + Config + end; + false -> + {skip, "Current openssl doesn't support SNI or extension handling is flawed"} end. + check_openssl_npn_support(Config) -> HelpText = os:cmd("openssl s_client --help"), case string:str(HelpText, "nextprotoneg") of @@ -2070,17 +1989,13 @@ workaround_openssl_s_clinent() -> [] end. -openssl_client_args(false, Hostname, Port) -> - ["s_client", "-connect", Hostname ++ ":" ++ integer_to_list(Port)]; -openssl_client_args(true, Hostname, Port) -> - ["s_client", "-no_ssl2", "-connect", Hostname ++ ":" ++ integer_to_list(Port)]. +openssl_client_args(Version, Hostname, Port) -> + ["s_client", "-connect", Hostname ++ ":" ++ integer_to_list(Port), ssl_test_lib:version_flag(Version)]. -openssl_client_args(false, Hostname, Port, ServerName) -> +openssl_client_args(Version, Hostname, Port, ServerName) -> ["s_client", "-connect", Hostname ++ ":" ++ - integer_to_list(Port), "-servername", ServerName]; -openssl_client_args(true, Hostname, Port, ServerName) -> - ["s_client", "-no_ssl2", "-connect", Hostname ++ ":" ++ - integer_to_list(Port), "-servername", ServerName]. + integer_to_list(Port), ssl_test_lib:version_flag(Version), "-servername", ServerName]. + hostname_format(Hostname) -> case lists:member($., Hostname) of @@ -2090,22 +2005,12 @@ hostname_format(Hostname) -> "localhost" end. -no_low_flag("-no_ssl2" = Flag) -> - case ssl_test_lib:supports_ssl_tls_version(sslv2) of - true -> - Flag; - false -> - "" - end; -no_low_flag(Flag) -> - Flag. - openssl_has_common_ciphers(Ciphers) -> OCiphers = ssl_test_lib:common_ciphers(openssl), has_common_ciphers(Ciphers, OCiphers). -has_common_ciphers([], OCiphers) -> +has_common_ciphers([], _) -> false; has_common_ciphers([Cipher | Rest], OCiphers) -> case lists:member(Cipher, OCiphers) of diff --git a/lib/ssl/test/ssl_upgrade_SUITE.erl b/lib/ssl/test/ssl_upgrade_SUITE.erl index 875399db76..ead18aeb73 100644 --- a/lib/ssl/test/ssl_upgrade_SUITE.erl +++ b/lib/ssl/test/ssl_upgrade_SUITE.erl @@ -47,10 +47,7 @@ init_per_suite(Config0) -> {skip, Reason} -> {skip, Reason}; Config -> - Result = - {ok, _} = make_certs:all(proplists:get_value(data_dir, Config), - proplists:get_value(priv_dir, Config)), - ssl_test_lib:cert_options(Config) + ssl_test_lib:make_rsa_cert(Config) end catch _:_ -> {skip, "Crypto did not start"} @@ -149,8 +146,8 @@ use_connection(Socket) -> end. soft_start_connection(Config, ResulProxy) -> - ClientOpts = proplists:get_value(client_verification_opts, Config), - ServerOpts = proplists:get_value(server_verification_opts, Config), + ClientOpts = proplists:get_value(client_rsa_verify_opts, Config), + ServerOpts = proplists:get_value(server_rsa_verify_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = start_server([{node, ServerNode}, {port, 0}, {from, ResulProxy}, @@ -166,8 +163,8 @@ soft_start_connection(Config, ResulProxy) -> {Server, Client}. restart_start_connection(Config, ResulProxy) -> - ClientOpts = proplists:get_value(client_verification_opts, Config), - ServerOpts = proplists:get_value(server_verification_opts, Config), + ClientOpts = proplists:get_value(client_rsa_verify_opts, Config), + ServerOpts = proplists:get_value(server_rsa_verify_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = start_server([{node, ServerNode}, {port, 0}, {from, ResulProxy}, diff --git a/lib/ssl/test/x509_test.erl b/lib/ssl/test/x509_test.erl index fea01efdaf..faf223ae35 100644 --- a/lib/ssl/test/x509_test.erl +++ b/lib/ssl/test/x509_test.erl @@ -22,7 +22,7 @@ -module(x509_test). - -include_lib("public_key/include/public_key.hrl"). +-include_lib("public_key/include/public_key.hrl"). -export([extensions/1, gen_pem_config_files/3]). |