summaryrefslogtreecommitdiff
path: root/lib/ssl/doc/src/using_ssl.xml
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssl/doc/src/using_ssl.xml')
-rw-r--r--lib/ssl/doc/src/using_ssl.xml328
1 files changed, 309 insertions, 19 deletions
diff --git a/lib/ssl/doc/src/using_ssl.xml b/lib/ssl/doc/src/using_ssl.xml
index 8c1e15a616..7f45b72db9 100644
--- a/lib/ssl/doc/src/using_ssl.xml
+++ b/lib/ssl/doc/src/using_ssl.xml
@@ -33,10 +33,10 @@
<file>using_ssl.xml</file>
</header>
<p>To see relevant version information for ssl, call
- <seealso marker="ssl:ssl#versions-0"><c>ssl:versions/0</c></seealso>
+ <seemfa marker="ssl:ssl#versions/0"><c>ssl:versions/0</c></seemfa>
.</p>
- <p>To see all supported cipher suites, call <seealso marker="ssl:ssl#cipher_suites-1"><c>ssl:cipher_suites(all)</c> </seealso>.
+ <p>To see all supported cipher suites, call <seemfa marker="ssl:ssl#cipher_suites/1"><c>ssl:cipher_suites(all)</c> </seemfa>.
The available cipher suites for a connection depend on your certificate.
Specific cipher suites that you want your connection to use can also be
specified. Default is to use the strongest available.</p>
@@ -51,7 +51,7 @@
<section>
<title>Minimal Example</title>
- <note><p> The minimal setup is not the most secure setup of SSL/TLS/DTLS.</p>
+ <note><p> The minimal setup is not the most secure setup of TLS/DTLS.</p>
</note>
<p>To set up client/server connections:</p>
@@ -60,7 +60,7 @@
<code type="erl">1 server> ssl:start().
ok</code>
- <p><em>Step 2:</em> Create an TLS listen socket: (To run DTLS add the option {protocol, dtls})</p>
+ <p><em>Step 2:</em> Create a TLS listen socket: (To run DTLS add the option {protocol, dtls})</p>
<code type="erl">2 server> {ok, ListenSocket} =
ssl:listen(9999, [{certfile, "cert.pem"}, {keyfile, "key.pem"},{reuseaddr, true}]).
{ok,{sslsocket, [...]}}</code>
@@ -78,7 +78,7 @@ ok</code>
<p><em>Step 5:</em> Do the TLS handshake:</p>
<code type="erl">4 server> {ok, Socket} = ssl:handshake(TLSTransportSocket).
-ok</code>
+{ok,{sslsocket, [...]}}</code>
<p><em>Step 6:</em> Send a message over TLS:</p>
<code type="erl">5 server> ssl:send(Socket, "foo").
@@ -94,12 +94,12 @@ ok</code>
<section>
<title>Upgrade Example - TLS only </title>
- <note><p>To upgrade a TCP/IP connection to an SSL connection, the
+ <note><p>To upgrade a TCP/IP connection to a TLS connection, the
client and server must agree to do so. The agreement
can be accomplished by using a protocol, for example, the one used by HTTP
specified in RFC 2817.</p></note>
- <p>To upgrade to an SSL connection:</p>
+ <p>To upgrade to a TLS connection:</p>
<p><em>Step 1:</em> Start the server side:</p>
<code type="erl">1 server> ssl:start().
@@ -120,8 +120,8 @@ ok</code>
<code type="erl">2 client> {ok, Socket} = gen_tcp:connect("localhost", 9999, [], infinity).</code>
<p><em>Step 5:</em> Ensure <c>active</c> is set to <c>false</c> before trying
- to upgrade a connection to an SSL connection, otherwise
- SSL handshake messages can be delivered to the wrong process:</p>
+ to upgrade a connection to a TLS connection, otherwise
+ TLS handshake messages can be delivered to the wrong process:</p>
<code type="erl">4 server> inet:setopts(Socket, [{active, false}]).
ok</code>
@@ -130,7 +130,7 @@ ok</code>
{certfile, "cert.pem"}, {keyfile, "key.pem"}]).
{ok,{sslsocket,[...]}}</code>
- <p><em>Step 7:</em> Upgrade to an TLS connection. The client and server
+ <p><em>Step 7:</em> Upgrade to a TLS connection. The client and server
must agree upon the upgrade. The server must call
<c>ssl:handshake/2</c> before the client calls <c>ssl:connect/3.</c></p>
<code type="erl">3 client>{ok, TLSSocket} = ssl:connect(Socket, [{cacertfile, "cacerts.pem"},
@@ -154,9 +154,9 @@ ok</code>
</section>
<section>
- <title>Customizing cipher suits</title>
+ <title>Customizing cipher suites</title>
- <p>Fetch default cipher suite list for an TLS/DTLS version. Change default
+ <p>Fetch default cipher suite list for a TLS/DTLS version. Change default
to all to get all possible cipher suites.</p>
<code type="erl">1> Default = ssl:cipher_suites(default, 'tlsv1.2').
[#{cipher => aes_256_gcm,key_exchange => ecdhe_ecdsa,
@@ -229,10 +229,186 @@ ssl:connect("localhost", 9999,
{key, PrivKey}], infinity).
</code>
- <p>See also <seealso marker="crypto:engine_load#engine_load"> crypto documentation</seealso> </p>
+ <p>See also <seeguide marker="crypto:engine_load#engine_load"> crypto documentation</seeguide> </p>
</section>
+
+ <section>
+ <title>Session Reuse pre TLS 1.3</title>
+ <p>Clients can request to reuse a session established
+ by a previous full handshake between that client and server by
+ sending the id of the session in the initial handshake
+ message. The server may or may not agree to reuse it. If agreed
+ the server will send back the id and if not it will send a new
+ id. The ssl application has several options for handling session
+ reuse.</p>
+
+ <p>On the client side the ssl application will save session data
+ to try to automate session reuse on behalf of the client processes
+ on the Erlang node. Note that only verified sessions will be
+ saved for security reasons, that is session resumption relies on
+ the certificate validation to have been run in the original
+ handshake. To minimize memory consumption only unique sessions
+ will be saved unless the special <c>save</c> value is specified
+ for the following option <c> {reuse_sessions, boolean() |
+ save}</c> in which case a full handhake will be performed and that
+ specific session will have been saved before the handshake
+ returns. The session id and even an opaque binary containing the
+ session data can be retrieved using
+ <c>ssl:connection_information/1</c> function. A saved session
+ (guaranteed by the save option) can be explicitly reused using
+ <c>{reuse_session, SessionId}</c>. Also it is possible for the
+ client to reuse a session that is not saved by the ssl application
+ using <c>{reuse_session, {SessionId, SessionData}}</c>.</p>
+
+ <note><p>When using explicit session reuse, it is up to the client
+ to make sure that the session being reused is for the correct
+ server and has been verified.</p></note>
+
+ <p>Here follows a client side example,
+ divide into several steps for readability.
+ </p>
+
+ <p>Step 1 - Automated Session Reuse</p>
+
+ <code type="erl">
+1> ssl:start().
+ok
+
+2&gt; {ok, C1} = ssl:connect("localhost", 9999, [{verify, verify_peer},
+ {versions, ['tlsv1.2']},
+ {cacertfile, "cacerts.pem"}]).
+{ok,{sslsocket,{gen_tcp,#Port&lt;0.7&gt;,tls_connection,undefined}, ...}}
+
+3&gt; ssl:connection_information(C1, [session_id]).
+{ok,[{session_id,&lt;&lt;95,32,43,22,35,63,249,22,26,36,106,
+ 152,49,52,124,56,130,192,137,161,
+ 146,145,164,232,...&gt;&gt;}]}
+
+%% Reuse session if possible, note that if C2 is really fast the session
+%% data might not be available for reuse.
+4&gt; {ok, C2} = ssl:connect("localhost", 9999, [{verify, verify_peer},
+ {versions, ['tlsv1.2']},
+ {cacertfile, "cacerts.pem"},
+ {reuse_sessions, true}]).
+{ok,{sslsocket,{gen_tcp,#Port&lt;0.8&gt;,tls_connection,undefined}, ...]}}
+
+%% C2 got same session ID as client one, session was automatically reused.
+5&gt; ssl:connection_information(C2, [session_id]).
+{ok,[{session_id,&lt;&lt;95,32,43,22,35,63,249,22,26,36,106,
+ 152,49,52,124,56,130,192,137,161,
+ 146,145,164,232,...&gt;&gt;}]}
+
+</code>
+
+<p>Step 2- Using <c>save</c> Option </p>
+
+<code type="erl">
+%% We want save this particular session for reuse although it has the same basis as C1
+6&gt; {ok, C3} = ssl:connect("localhost", 9999, [{verify, verify_peer},
+ {versions, ['tlsv1.2']},
+ {cacertfile, "cacerts.pem"},
+ {reuse_sessions, save}]).
+{ok,{sslsocket,{gen_tcp,#Port&lt;0.9&gt;,tls_connection,undefined}, ...]}}
+
+%% A full handshake is performed and we get a new session ID
+7&gt; {ok, [{session_id, ID}]} = ssl:connection_information(C3, [session_id]).
+{ok,[{session_id,&lt;&lt;91,84,27,151,183,39,84,90,143,141,
+ 121,190,66,192,10,1,27,192,33,95,78,
+ 8,34,180,...&gt;&gt;}]}
+
+%% Use automatic session reuse
+8&gt; {ok, C4} = ssl:connect("localhost", 9999, [{verify, verify_peer},
+ {versions, ['tlsv1.2']},
+ {cacertfile, "cacerts.pem"},
+ {reuse_sessions, true}]).
+{ok,{sslsocket,{gen_tcp,#Port&lt;0.10&gt;,tls_connection,
+ undefined}, ...]}}
+
+%% The "saved" one happened to be selected, but this is not a guarantee
+9&gt; ssl:connection_information(C4, [session_id]).
+{ok,[{session_id,&lt;&lt;91,84,27,151,183,39,84,90,143,141,
+ 121,190,66,192,10,1,27,192,33,95,78,
+ 8,34,180,...&gt;&gt;}]}
+
+%% Make sure to reuse the "saved" session
+10&gt; {ok, C5} = ssl:connect("localhost", 9999, [{verify, verify_peer},
+ {versions, ['tlsv1.2']},
+ {cacertfile, "cacerts.pem"},
+ {reuse_session, ID}]).
+{ok,{sslsocket,{gen_tcp,#Port&lt;0.11&gt;,tls_connection,
+ undefined}, ...]}}
+
+11&gt; ssl:connection_information(C5, [session_id]).
+{ok,[{session_id,&lt;&lt;91,84,27,151,183,39,84,90,143,141,
+ 121,190,66,192,10,1,27,192,33,95,78,
+ 8,34,180,...&gt;&gt;}]}
+</code>
+
+<p>Step 3 - Explicit Session Reuse </p>
+
+<code type="erl">
+%% Preform a full handshake and the session will not be saved for reuse
+12&gt; {ok, C9} = ssl:connect("localhost", 9999, [{verify, verify_peer},
+ {versions, ['tlsv1.2']},
+ {cacertfile, "cacerts.pem"},
+ {reuse_sessions, false},
+ {server_name_indication, disable}]).
+{ok,{sslsocket,{gen_tcp,#Port&lt;0.14&gt;,tls_connection, ...}}
+
+%% Fetch session ID and data for C9 connection
+12&gt; {ok, [{session_id, ID1}, {session_data, SessData}]} =
+ ssl:connection_information(C9, [session_id, session_data]).
+{ok,[{session_id,&lt;&lt;9,233,4,54,170,88,170,180,17,96,202,
+ 85,85,99,119,47,9,68,195,50,120,52,
+ 130,239,...&gt;&gt;},
+ {session_data,&lt;&lt;131,104,13,100,0,7,115,101,115,115,105,
+ 111,110,109,0,0,0,32,9,233,4,54,170,...&gt;&gt;}]}
+
+%% Explicitly reuse the session from C9
+13&gt; {ok, C10} = ssl:connect("localhost", 9999, [{verify, verify_peer},
+ {versions, ['tlsv1.2']},
+ {cacertfile, "cacerts.pem"},
+ {reuse_session, {ID1, SessData}}]).
+{ok,{sslsocket,{gen_tcp,#Port&lt;0.15&gt;,tls_connection,
+ undefined}, ...}}
+
+14&gt; ssl:connection_information(C10, [session_id]).
+{ok,[{session_id,&lt;&lt;9,233,4,54,170,88,170,180,17,96,202,
+ 85,85,99,119,47,9,68,195,50,120,52,
+ 130,239,...&gt;&gt;}]}
+
+</code>
+
+<p>Step 4 - Not Possible to Reuse Explicit Session by ID Only</p>
+
+<code type="erl">
+%% Try to reuse the session from C9 using only the id
+15&gt; {ok, E} = ssl:connect("localhost", 9999, [{verify, verify_peer},
+ {versions, ['tlsv1.2']},
+ {cacertfile, "cacerts.pem"},
+ {reuse_session, ID1}]).
+{ok,{sslsocket,{gen_tcp,#Port&lt;0.18&gt;,tls_connection,
+ undefined}, ...}}
+
+%% This will fail (as it is not saved for reuse)
+%% and a full handshake will be performed, we get a new id.
+16&gt; ssl:connection_information(E, [session_id]).
+{ok,[{session_id,&lt;&lt;87,46,43,126,175,68,160,153,37,29,
+ 196,240,65,160,254,88,65,224,18,63,
+ 18,17,174,39,...&gt;&gt;}]}
+</code>
+
+ <p>On the server side the the <c>{reuse_sessions, boolean()}</c> option
+ determines if the server will save session data and allow session
+ reuse or not. This can be further customized by the option
+ <c>{reuse_session, fun()}</c> that may introduce a local policy for
+ session reuse.
+ </p>
+
+ </section>
+
<section>
<title>Session Tickets and Session Resumption in TLS 1.3</title>
@@ -249,12 +425,12 @@ ssl:connect("localhost", 9999,
are opaque for the clients. Generally, stateful tickets are smaller and the server can guarantee
that tickets are only used once. Stateless tickets contain additional data, require less storage
on the server side, but they offer different guarantees against anti-replay. See also
- <seealso marker="ssl:using_ssl#anti-replay-protection-in-tls-1.3">
- Anti-Replay Protection in TLS 1.3</seealso>
+ <seeguide marker="ssl:using_ssl#anti-replay-protection-in-tls-1.3">
+ Anti-Replay Protection in TLS 1.3</seeguide>
</p>
<p>Session tickets are sent by servers on newly estalished TLS connections.
The number of tickets sent and their lifetime are configurable by application variables. See also
- <seealso marker="ssl:ssl_app#configuration"> SSL's configuration</seealso>.</p>
+ <seeapp marker="ssl:ssl_app#configuration"> SSL's configuration</seeapp>.</p>
<p>Session tickets are protected by application traffic keys, and in stateless
tickets, the opaque data structure itself is self-encrypted.</p>
@@ -383,6 +559,120 @@ ssl:connect("localhost", 9999,
</section>
<section>
+ <title>Early Data in TLS 1.3</title>
+ <p>TLS 1.3 allows clients to send data on the first flight if the endpoints have
+ a shared crypographic secret (pre-shared key). This means that clients can send
+ early data if they have a valid session ticket received in a previous
+ successful handshake. For more information about session resumption see
+ <seeguide marker="ssl:using_ssl#session-tickets-and-session-resumption-in-tls-1.3">
+ Session Tickets and Session Resumption in TLS 1.3</seeguide>.
+ </p>
+ <p>The security properties of Early Data are weaker than other kinds of TLS data.
+ This data is not forward secret, and it is vulnerable to replay attacks. For available
+ mitigation strategies see
+ <seeguide marker="ssl:using_ssl#anti-replay-protection-in-tls-1.3">
+ Anti-Replay Protection in TLS 1.3</seeguide>.</p>
+ <p>In normal operation, clients will not know which, if any, of the available mitigation
+ strategies servers actually implement, and hence must only send early data which
+ they deem safe to be replayed. For example, idempotent HTTP operations, such as HEAD and
+ GET, can usually be regarded as safe but even they can be exploited by a large number of
+ replays causing resource limit exhaustion and other similar problems.</p>
+ <p>An example of sending early data with automatic and manual session ticket handling:</p>
+ <warning>
+ <p>The Early Data feature is experimental in this version of OTP.
+ </p>
+ </warning>
+
+ <p><em>Server (with NSS key logging)</em></p>
+ <code type="none">
+ early_data_server() ->
+ application:load(ssl),
+ {ok, _} = application:ensure_all_started(ssl),
+ Port = 11029,
+ LOpts = [{certfile, ?SERVER_CERT},
+ {keyfile, ?SERVER_KEY},
+ {reuseaddr, true},
+ {versions, ['tlsv1.2','tlsv1.3']},
+ {session_tickets, stateless},
+ {early_data, enabled},
+ {keep_secrets, true} %% Enable NSS key log (debug option)
+ ],
+ {ok, LSock} = ssl:listen(Port, LOpts),
+ %% Accept first connection
+ {ok, CSock0} = ssl:transport_accept(LSock),
+ {ok, _} = ssl:handshake(CSock0),
+ %% Accept second connection
+ {ok, CSock1} = ssl:transport_accept(LSock),
+ {ok, Sock} = ssl:handshake(CSock1),
+ Sock.
+ </code>
+ <p><em>Exporting the secrets (optional)</em></p>
+ <code type="none">
+ {ok, [{keylog, KeylogItems}]} = ssl:connection_information(Sock, [keylog]).
+ file:write_file("key.log", [[KeylogItem,$\n] || KeylogItem &lt;- KeylogItems]).
+ </code>
+ <p><em>Client (automatic ticket handling):</em></p>
+ <code type="erl">
+ early_data_auto() -&gt;
+ %% First handshake 1-RTT - get session tickets
+ application:load(ssl),
+ {ok, _} = application:ensure_all_started(ssl),
+ Port = 11029,
+ Data = &lt;&lt;"HEAD / HTTP/1.1\r\nHost: \r\nConnection: close\r\n"&gt;&gt;,
+ COpts0 = [{cacertfile, ?CA_CERT},
+ {versions, ['tlsv1.2', 'tlsv1.3']},
+ {session_tickets, auto}],
+ {ok, Sock0} = ssl:connect("localhost", Port, COpts0),
+
+ %% Wait for session tickets
+ timer:sleep(500),
+ %% Close socket if server cannot handle multiple connections e.g. openssl s_server
+ ssl:close(Sock0),
+
+ %% Second handshake 0-RTT
+ COpts1 = [{cacertfile, ?CA_CERT},
+ {versions, ['tlsv1.2', 'tlsv1.3']},
+ {session_tickets, auto},
+ {early_data, Data}],
+ {ok, Sock} = ssl:connect("localhost", Port, COpts1),
+ Sock.
+ </code>
+ <p><em>Client (manual ticket handling):</em></p>
+ <code type="erl">
+ early_data_manual() -&gt;
+ %% First handshake 1-RTT - get session tickets
+ application:load(ssl),
+ {ok, _} = application:ensure_all_started(ssl),
+ Port = 11029,
+ Data = &lt;&lt;"HEAD / HTTP/1.1\r\nHost: \r\nConnection: close\r\n"&gt;&gt;,
+ COpts0 = [{cacertfile, ?CA_CERT},
+ {versions, ['tlsv1.2', 'tlsv1.3']},
+ {session_tickets, manual}],
+ {ok, Sock0} = ssl:connect("localhost", Port, COpts0),
+
+ %% Wait for session tickets
+ Ticket =
+ receive
+ {ssl, session_ticket, Ticket0} ->
+ Ticket0
+ end,
+
+ %% Close socket if server cannot handle multiple connections
+ %% e.g. openssl s_server
+ ssl:close(Sock0),
+
+ %% Second handshake 0-RTT
+ COpts1 = [{cacertfile, ?CA_CERT},
+ {versions, ['tlsv1.2', 'tlsv1.3']},
+ {session_tickets, manual},
+ {use_ticket, [Ticket]},
+ {early_data, Data}],
+ {ok, Sock} = ssl:connect("localhost", Port, COpts1),
+ Sock.
+ </code>
+ </section>
+
+ <section>
<title>Anti-Replay Protection in TLS 1.3</title>
<p>The TLS 1.3 protocol does not provide inherent protection for replay of 0-RTT data but
@@ -414,7 +704,7 @@ ssl:connect("localhost", 9999,
Bloom filters are fast, memory-efficient, probabilistic data structures that can tell
if an element may be in a set or if it is definitely not in the set.</p>
- <p>If the option <seealso marker="ssl:ssl#type-anti_replay">anti_replay</seealso>
+ <p>If the option <seetype marker="ssl:ssl#anti_replay">anti_replay</seetype>
is defined in the server, a pair of Bloom filters (<em>current</em> and
<em>old</em>) are used to record incoming ClientHello messages (it is the unique
binder value that is actually stored).
@@ -424,12 +714,12 @@ ssl:connect("localhost", 9999,
is set as <em>current</em>.
</p>
- <p>The Anti-Replay protection feature in statless servers executes in the following steps
+ <p>The Anti-Replay protection feature in stateless servers executes in the following steps
when a new ClientHello is received:</p>
<list type="bulleted">
<item><p>Reported ticket age (obfuscated ticket age) shall be
less than ticket lifetime.</p></item>
- <item><p>Actual ticket age shall be less than the ticket lifetime (statless session
+ <item><p>Actual ticket age shall be less than the ticket lifetime (stateless session
tickets contain the servers timestamp when the ticket was issued).</p></item>
<item><p>Ticket shall be used within specified time window (freshness checks).</p></item>
<item><p>If all above checks passed both <em>current</em> and <em>old</em> Bloom filters