Skip to content

Commit

Permalink
Add max_plain_text_length option to ssl:listen.
Browse files Browse the repository at this point in the history
This corresponds to OpenSSL's SSL_CTRL_SET_MAX_SEND_FRAGMENT option, and
allows the size of plain text fragments to be specified.

This is useful, for example, for talking to embedded devices with
limited resources.

This is different from the max_fragment_length negotiation specified by
RFC 6066:

- RFC 6066 is an extension to TLS 1.2; this supports TLS 1.1.
- RFC 6066 only allows 512-, 1024-, 2048- and 4096-byte fragment sizes;
  this allows arbitrary values, 512 <= N <= 16384.

Unfortunately, this requires the server to _know_ what the client's
expecting; this is an acceptable trade-off in this scenario.
  • Loading branch information
rlipscombe committed Jan 13, 2014
1 parent a407ab0 commit 71c53d2
Show file tree
Hide file tree
Showing 4 changed files with 17 additions and 10 deletions.
7 changes: 6 additions & 1 deletion lib/ssl/src/ssl.erl
Expand Up @@ -627,6 +627,7 @@ handle_options(Opts0, _Role) ->
psk_identity = handle_option(psk_identity, Opts, undefined),
srp_identity = handle_option(srp_identity, Opts, undefined),
ciphers = handle_option(ciphers, Opts, []),
max_plain_text_length = handle_option(max_plain_text_length, Opts, ?MAX_PLAIN_TEXT_LENGTH),
%% Server side option
reuse_session = handle_option(reuse_session, Opts, ReuseSessionFun),
reuse_sessions = handle_option(reuse_sessions, Opts, true),
Expand All @@ -647,7 +648,7 @@ handle_options(Opts0, _Role) ->
fail_if_no_peer_cert, verify_client_once,
depth, cert, certfile, key, keyfile,
password, cacerts, cacertfile, dh, dhfile,
user_lookup_fun, psk_identity, srp_identity, ciphers,
user_lookup_fun, psk_identity, srp_identity, ciphers, max_plain_text_length,
reuse_session, reuse_sessions, ssl_imp,
cb_info, renegotiate_at, secure_renegotiate, hibernate_after,
erl_dist, next_protocols_advertised,
Expand Down Expand Up @@ -767,6 +768,10 @@ validate_option(srp_identity, {Username, Password})
when is_list(Username), is_list(Password), Username =/= "", length(Username) =< 255 ->
{list_to_binary(Username), list_to_binary(Password)};

validate_option(max_plain_text_length, Value)
when Value >= 512, Value =< ?MAX_PLAIN_TEXT_LENGTH ->
Value;

validate_option(ciphers, Value) when is_list(Value) ->
Version = tls_record:highest_protocol_version([]),
try cipher_suites(Version, Value)
Expand Down
2 changes: 2 additions & 0 deletions lib/ssl/src/ssl_internal.hrl
Expand Up @@ -39,6 +39,7 @@
-type issuer() :: tuple().
-type serialnumber() :: integer().
-type cert_key() :: {reference(), integer(), issuer()}.
-type max_plain_text_length() :: 512..16384.

%% basic binary constructors
-define(BOOLEAN(X), X:8/unsigned-big-integer).
Expand Down Expand Up @@ -104,6 +105,7 @@
reuse_sessions :: boolean(),
renegotiate_at,
secure_renegotiate,
max_plain_text_length = 16384 :: max_plain_text_length(),
debug,
%% undefined if not hibernating, or number of ms of
%% inactivity after which ssl_connection will go into
Expand Down
8 changes: 4 additions & 4 deletions lib/ssl/src/ssl_record.erl
Expand Up @@ -42,7 +42,7 @@

%% Encoding records
-export([encode_handshake/3, encode_alert_record/3,
encode_change_cipher_spec/2, encode_data/3]).
encode_change_cipher_spec/2, encode_data/4]).

%% Compression
-export([compress/3, uncompress/3, compressions/0]).
Expand Down Expand Up @@ -328,17 +328,17 @@ encode_change_cipher_spec(Version, ConnectionStates) ->
encode_plain_text(?CHANGE_CIPHER_SPEC, Version, <<1:8>>, ConnectionStates).

%%--------------------------------------------------------------------
-spec encode_data(binary(), tls_version(), #connection_states{}) ->
-spec encode_data(binary(), tls_version(), max_plain_text_length(), #connection_states{}) ->
{iolist(), #connection_states{}}.
%%
%% Description: Encodes data to send on the ssl-socket.
%%--------------------------------------------------------------------
encode_data(Frag, Version,
encode_data(Frag, Version, MaxPlainTextLength,
#connection_states{current_write = #connection_state{
security_parameters =
#security_parameters{bulk_cipher_algorithm = BCA}}} =
ConnectionStates) ->
Data = split_bin(Frag, ?MAX_PLAIN_TEXT_LENGTH, Version, BCA),
Data = split_bin(Frag, MaxPlainTextLength, Version, BCA),
encode_iolist(?APPLICATION_DATA, Data, Version, ConnectionStates).

uncompress(?NULL, Data, CS) ->
Expand Down
10 changes: 5 additions & 5 deletions lib/ssl/src/tls_connection.erl
Expand Up @@ -523,13 +523,13 @@ next_state_connection(StateName, #state{send_queue = Queue0,
negotiated_version = Version,
socket = Socket,
transport_cb = Transport,
connection_states = ConnectionStates0
} = State) ->
connection_states = ConnectionStates0,
ssl_options = #ssl_options{max_plain_text_length = MaxPlainTextLength}} = State) ->
%% Send queued up data that was queued while renegotiating
case queue:out(Queue0) of
{{value, {From, Data}}, Queue} ->
{Msgs, ConnectionStates} =
ssl_record:encode_data(Data, Version, ConnectionStates0),
ssl_record:encode_data(Data, Version, MaxPlainTextLength, ConnectionStates0),
Result = Transport:send(Socket, Msgs),
gen_fsm:reply(From, Result),
next_state_connection(StateName,
Expand Down Expand Up @@ -759,15 +759,15 @@ write_application_data(Data0, From,
connection_states = ConnectionStates0,
send_queue = SendQueue,
socket_options = SockOpts,
ssl_options = #ssl_options{renegotiate_at = RenegotiateAt}} = State) ->
ssl_options = #ssl_options{renegotiate_at = RenegotiateAt, max_plain_text_length = MaxPlainTextLength}} = State) ->
Data = encode_packet(Data0, SockOpts),

case time_to_renegotiate(Data, ConnectionStates0, RenegotiateAt) of
true ->
renegotiate(State#state{send_queue = queue:in_r({From, Data}, SendQueue),
renegotiation = {true, internal}});
false ->
{Msgs, ConnectionStates} = ssl_record:encode_data(Data, Version, ConnectionStates0),
{Msgs, ConnectionStates} = ssl_record:encode_data(Data, Version, MaxPlainTextLength, ConnectionStates0),
Result = Transport:send(Socket, Msgs),
{reply, Result,
connection, State#state{connection_states = ConnectionStates}, get_timeout(State)}
Expand Down

0 comments on commit 71c53d2

Please sign in to comment.