Features
This is a framework for supporting TLS Hello Extensions (RFC 4366) and Supplemental Data (RFC 4680). It is intended to help developers who want to add support for new TLS extensions into OpenSSL.
Our framework treats every extension as an object, with data to be exchanged during the handshake and methods that implement the extension logic. Supplemental Data are part of such objects, to adhere with RFC 4680:
Information provided in a supplemental data object MUST be intended
to be used exclusively by applications and protocols above the TLS
protocol layer. Any such data MUST NOT need to be processed by the
TLS protocol.
By having this unified view for extensions, it is easy to support once for all some of the requirements imposed by RFCs, for instance (RFC 4366):
- There MUST NOT be more than one extension of the same type.
- In the event that a client requests additional functionality using the extended client hello, and this functionality is not supplied by the server, the client MAY abort the handshake.
Download/try
Patch to OpenSSL (SNAP-20081214):
- get & unpack OpenSSL in /path/to
- download the patch in /path/to
- cd /path/to/openssl-SNAP-20081214
- patch -p1 < ../tlsext_general-SNAP-20081211.patch
Patched OpenSSL distribution:
- ./config
- make
- cd apps/
- ./openssl s_server and ./openssl s_client -tls1
TODO
A short list of the things still to do:- Error management
- Porting of extensions already hard-coded in OpenSSL
- Improvement of the core code
- Functions for find within the stacks of extensions and supplem ental data entries
- Optimize search of sd <-> ext within ssl3_get_[client|server]_supp_data()
- Functions for find within the stacks of extensions and supplem ental data entries
- Best practice for extension implementation
- Interface for the applications
- Interface for the applications
Design Principles
Overview
The code is organized in four main modules, as shown in the following picture (blue square). We refer to the extensive Doxygen documentation within the code for more details.Patch:
The lower layer is a patch to libssl, to handle TLS Extensions and support the new SupplementalData handshake message.Core:
For simplicity, the patch to the existent code is limited to single function calls, which carry out all the necessary computation.
The macro OPENSSL_NO_TLSEXT_GENERAL disables the framework at compilation time.
Moreover, the already available macro OPENSSL_NO_TLSEXT automatically forces OPENSSL_NO_TLSEXT_GENERAL.
The middle layer contains the data structures and the core functionalities
Interface:
The upper layer consists in the interface available to programmers to write new TLS Extensions (see the Example below).App. Interface:
This module contains the portion of the Interface that could be useful to be accessed by the application
(e.g. the possibility for the client to require the use of an extension, or abort the handshake if the server ignores it).
However, this SHOULD NOT directly accessed by applications, but it SHOULD be wrapped by any extension which want (or not) to provide such functionalities to the application layer.
Data Structures
The OpenSSL SSL object is extended with a STACK_OF TLSEXT_GENERAL.A TLSEXT_GENERAL is an object that contains data related to a TLS Extension (in the meaning of our general framework) as well as callback functions to implement the logic of the extension.
The next picture gives an overall idea.
For what concern data, the two parts will contains data to be sent or received depending if the object is instantiated on the client or server side.
In more detail:
client_datais the type of the extension (it must be the same on client and server).
is the payload the client will send (and the server received) through the Extension (ClientHello handshake message).
client_data_length
is the length of the payload.
client_supp_data
is a stack of SUPP_DATA_ENTRY objects, each describing in TLV (type-length-value) format a supplemental data entry related to this extension.
The client sends (and the server receives) this data through the SupplementalData handshake message.
The same (but symmetric) is for server data.
Callbacks, on the other hand, are only handled on the side the object is instantiated and so they SHOULD be defined only in the right side.
Similarly, the context data (client_ctx_data, resp. server_ctx_data) to exchange information from/to the callbacks, SHOULD be used only in the correct side.
Finally, there are three flags:
client_required
tell if the client has to abort in case the server ignores the extension.
server_send
receivedtell if the server should send this extension (see Workflow for more details).
tell if the extension has been received. It is for internal use, both to check for duplicates and to have a list of the negotiated extensions.
Workflow
The workflow of the framework with respect to the TLS Handshake is depicted in the following picture.
Colours refer to the previous picture showing the TLSEXT_GENERAL object.
- If the Client registers a TLSEXT_GENERAL object, then it is sent as part of the Extension within the ClientHello handshake message.
The Server, on the contrary, is supposed to register all the TLSEXT_GENERAL objects it want to handle. - When the Server receives the ClientHello, it parses the Extensions and -if any is handled by our framework- it invokes the related server_ext_callback.
This callback is in charge of taking the decision to reply or not to the extension, by setting the flag server_send. - The Server replies with the ServerHello message containing the accepted extensions, for which the Client invokes the client_ext_callback.
- The Client verifies that every extension with client_required flag set to true has been received. If not, it aborts the handshake.
- If any extension requires supplemental data on server side (expressed by a non-NULL server_supp_data stack), the server SupplementalData handshake message is sent.
In this case, the Client receives it and invokes the client_supp_data_callback for each extension that registered server supplemental data. - The handshake continues until the ServerDone message.
- If any extension requires supplemental data on client side (expressed by a non-NULL client_supp_data stack), the client SupplementalData handshake message is sent.
In this case, the Server receives it and invokes the server_supp_data_callback for each extension that registered client supplemental data. - The handshake concludes.
- After the handshake is terminated the Client, resp. the Server, invokes the client_finish_callback, resp. the server_finish_callback, for each negotiated extension.
Example: a Test Extension
Before starting to implement the "Hello world!" extension, it is worth of noting that extension implemented exploiting the General TLS Extension framework still are part of the libssl and still MUST be standardized by IANA.Hello World!
We begin with a simple TLS Extension in the sense of RFC 4366 (i.e. without supplemental data).
The Client defines an initialization function:
void SSL_TLSEXT_TEST_client_init(SSL * s) {
TLSEXT_GENERAL * e;
e = SSL_TLSEXT_GENERAL_new(s, 65000);
SSL_TLSEXT_GENERAL_client_data(e, 6, "Hello\0");
SSL_TLSEXT_GENERAL_client_ext_cb(e, ssl_tlsext_test_client_ext_cb);
}
It creates an extension of type 65000, with payload "Hello", and registers a callback function ssl_tlsext_test_client_ext_cb().
As callback function we write a simple debug function, which prints out the data received from the server:
void ssl_tlsext_test_client_ext_cb(SSL * s, TLSEXT_GENERAL * e) {
fprintf(stderr, "TEST: %d\n", e->type);
fprintf(stderr, "TEST: %d\n", e->server_data_length);
fprintf(stderr, "TEST: %s\n", e->server_data);
}
On the Server, we again define an initialization function that defines the extension.
void SSL_TLSEXT_TEST_server_init(SSL * s) {
TLSEXT_GENERAL * e;
e = SSL_TLSEXT_GENERAL_new(s, 65000);
SSL_TLSEXT_GENERAL_server_ext_cb(e, ssl_tlsext_test_server_ext_cb);
}
Note that this time we did not input any payload: the callback is in charge of setting the payload, maybe depending on what the client sent:
void ssl_tlsext_test_server_ext_cb(SSL * s, TLSEXT_GENERAL * e) {
fprintf(stderr, "TEST: %d\n", e->type);
fprintf(stderr, "TEST: %d\n", e->client_data_length);
fprintf(stderr, "TEST: %s\n", e->client_data);
/* generate the response, maybe accessing data from client */
SSL_TLSEXT_GENERAL_server_data(e, 7, "world!\0");
SSL_TLSEXT_GENERAL_server_send(e);
}
Supplemental Data
We now add some supplemental data. As an example, we assume only the Server has to send supplemental data (in particular two supplemental data entries).
First remark: both the Client and the Server MUST define the extension, including in the definition the number and type of related supplemental data entries (which is assumed to be fixed and standardized).
On the Client we modify the initialization function by describing the supplemental data entries (still if it is the Server who will send them) and registering a finish callback to handle the data received:
void SSL_TLSEXT_TEST_client_init(SSL * s) {
TLSEXT_GENERAL * e;
e = SSL_TLSEXT_GENERAL_new(s, 65000);
SSL_TLSEXT_GENERAL_client_data(e, 8, "test-c1\0");
SSL_TLSEXT_GENERAL_client_ext_cb(e, ssl_tlsext_test_client_ext_cb);
/* the server will send supplemental data */
SSL_TLSEXT_GENERAL_server_supp_data_new(e, 65100);
SSL_TLSEXT_GENERAL_server_supp_data_new(e, 65101);
SSL_TLSEXT_GENERAL_client_finish_cb(e, ssl_tlsext_test_client_finish_cb);
}
Again, we simply define a debug callback:
void ssl_tlsext_test_client_finish_cb(SSL * s, TLSEXT_GENERAL * e) {
int i;
SUPP_DATA_ENTRY * sd;
if (e->server_supp_data) {
for (i = 0; i < sk_SUPP_DATA_ENTRY_num(e->server_supp_data); i++) {
sd = sk_SUPP_DATA_ENTRY_value(e->server_supp_data, i);
fprintf(stderr, "TEST: %s -> SD %d (%d) %s\n", e->server_data, sd->type, sd->length, sd->data);
}
}
}
On the Server we add the supplemental data entries and register a callback to fill their payload:
void SSL_TLSEXT_TEST_server_init(SSL * s) {
TLSEXT_GENERAL * e;
e = SSL_TLSEXT_GENERAL_new(s, 65000);
SSL_TLSEXT_GENERAL_server_ext_cb(e, ssl_tlsext_test_server_ext_cb);
/* the server will send supplemental data */
SSL_TLSEXT_GENERAL_server_supp_data_new(e, 65100);
SSL_TLSEXT_GENERAL_server_supp_data_new(e, 65101);
SSL_TLSEXT_GENERAL_server_supp_data_cb(e, ssl_tlsext_test_server_supp_data_cb);
}
And finally the Server's callback fills the content of the supplemental data entries (it might access to all the data exchanged before):
void ssl_tlsext_test_server_supp_data_cb(SSL * s, TLSEXT_GENERAL * e) {
SSL_TLSEXT_GENERAL_server_supp_data(e, 65100, 10, "test-Ssd0\0");
SSL_TLSEXT_GENERAL_server_supp_data(e, 65101, 10, "test-Ssd1\0");
}
The Test extension included within the distribution contains an enhanced example.