Skip to content

Commit cc339f5

Browse files
kshreejabaldwinmatt
authored andcommitted
Adding resumption using session tickets (#785)
* Adding resumption using session tickets * Addressed feedback comments * Addressed feedback: Update to Session ID cache lookup logic, cleaned integration tests and few other changes * Addressed feedback and updated s2n_ticket_key to store intro timestamp instead of expiry time * Addressed Feedback: Removed S2N_RECEIVED_VALID_TICKET status * Added intro time to STK addition API * Addressed feedback * Renamed valid and semi-valid key to encrypt-decrypt and decrpt key respectively, and addressed other feedback * Updated s2n_conn_set_handshake_type_proof in SAWScript tests * Addressed feedback * Added missing break statement and addressed feedback * Addressed feedback * Updated max key hashes
1 parent 08d14c7 commit cc339f5

30 files changed

+1934
-68
lines changed

api/s2n.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ extern int s2n_config_free(struct s2n_config *config);
5656
extern int s2n_config_free_dhparams(struct s2n_config *config);
5757
extern int s2n_config_free_cert_chain_and_key(struct s2n_config *config);
5858

59-
6059
typedef int (*s2n_clock_time_nanoseconds) (void *, uint64_t *);
6160
extern int s2n_config_set_wall_clock(struct s2n_config *config, s2n_clock_time_nanoseconds clock_fn, void *ctx);
6261
extern int s2n_config_set_monotonic_clock(struct s2n_config *config, s2n_clock_time_nanoseconds clock_fn, void *ctx);
@@ -110,6 +109,16 @@ extern int s2n_config_set_extension_data(struct s2n_config *config, s2n_tls_exte
110109
extern int s2n_config_send_max_fragment_length(struct s2n_config *config, s2n_max_frag_len mfl_code);
111110
extern int s2n_config_accept_max_fragment_length(struct s2n_config *config);
112111

112+
extern int s2n_config_set_session_state_lifetime(struct s2n_config *config, uint64_t lifetime_in_secs);
113+
114+
extern int s2n_config_set_session_tickets_onoff(struct s2n_config *config, uint8_t enabled);
115+
extern int s2n_config_set_ticket_encrypt_decrypt_key_lifetime(struct s2n_config *config, uint64_t lifetime_in_secs);
116+
extern int s2n_config_set_ticket_decrypt_key_lifetime(struct s2n_config *config, uint64_t lifetime_in_secs);
117+
extern int s2n_config_add_ticket_crypto_key(struct s2n_config *config,
118+
const uint8_t *name, uint32_t name_len,
119+
uint8_t *key, uint32_t key_len,
120+
uint64_t intro_time_in_seconds_from_epoch);
121+
113122
struct s2n_connection;
114123
typedef enum { S2N_SERVER, S2N_CLIENT } s2n_mode;
115124
extern struct s2n_connection *s2n_connection_new(s2n_mode mode);
@@ -183,6 +192,7 @@ extern int s2n_connection_get_client_cert_chain(struct s2n_connection *conn, uin
183192

184193
extern int s2n_connection_set_session(struct s2n_connection *conn, const uint8_t *session, size_t length);
185194
extern int s2n_connection_get_session(struct s2n_connection *conn, uint8_t *session, size_t max_length);
195+
extern int s2n_connection_get_session_ticket_lifetime_hint(struct s2n_connection *conn);
186196
extern ssize_t s2n_connection_get_session_length(struct s2n_connection *conn);
187197
extern ssize_t s2n_connection_get_session_id_length(struct s2n_connection *conn);
188198
extern int s2n_connection_is_session_resumed(struct s2n_connection *conn);

bin/s2nc.c

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,9 @@ void usage()
6363
fprintf(stderr, " -i,--insecure\n");
6464
fprintf(stderr, " Turns off certification validation altogether.\n");
6565
fprintf(stderr, " -r,--reconnect\n");
66-
fprintf(stderr, " Drop and re-make the connection with the same Session-ID\n");
66+
fprintf(stderr, " Drop and re-make the connection using Session ticket. If session ticket is disabled, then re-make the connection using Session-ID \n");
67+
fprintf(stderr, " -T,--no-session-ticket \n");
68+
fprintf(stderr, " Disable session ticket for resumption.\n");
6769
fprintf(stderr, " -D,--dynamic\n");
6870
fprintf(stderr, " Set dynamic record resize threshold\n");
6971
fprintf(stderr, " -t,--timeout\n");
@@ -223,6 +225,7 @@ int main(int argc, char *const *argv)
223225
uint16_t mfl_value = 0;
224226
uint8_t insecure = 0;
225227
int reconnect = 0;
228+
uint8_t session_ticket = 1;
226229
s2n_status_request_type type = S2N_STATUS_REQUEST_NONE;
227230
uint32_t dyn_rec_threshold = 0;
228231
uint8_t dyn_rec_timeout = 0;
@@ -245,12 +248,13 @@ int main(int argc, char *const *argv)
245248
{"ca-dir", required_argument, 0, 'd'},
246249
{"insecure", no_argument, 0, 'i'},
247250
{"reconnect", no_argument, 0, 'r'},
251+
{"no-session-ticket", no_argument, 0, 'T'},
248252
{"Dynamic", required_argument, 0, 'D'},
249253
{"timeout", required_argument, 0, 't'},
250254
};
251255
while (1) {
252256
int option_index = 0;
253-
int c = getopt_long(argc, argv, "a:c:ehn:sf:d:D:t:ir", long_options, &option_index);
257+
int c = getopt_long(argc, argv, "a:c:ehn:sf:d:D:t:irT", long_options, &option_index);
254258
if (c == -1) {
255259
break;
256260
}
@@ -288,6 +292,9 @@ int main(int argc, char *const *argv)
288292
case 'r':
289293
reconnect = 5;
290294
break;
295+
case 'T':
296+
session_ticket = 0;
297+
break;
291298
case 't':
292299
dyn_rec_timeout = (uint8_t) MIN(255, atoi(optarg));
293300
break;
@@ -373,6 +380,10 @@ int main(int argc, char *const *argv)
373380
s2n_config_disable_x509_verification(config);
374381
}
375382

383+
if (session_ticket) {
384+
s2n_config_set_session_tickets_onoff(config, 1);
385+
}
386+
376387
struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT);
377388

378389
if (conn == NULL) {
@@ -413,7 +424,7 @@ int main(int argc, char *const *argv)
413424

414425
/* Save session state from connection if reconnect is enabled */
415426
if (reconnect > 0) {
416-
if (s2n_connection_get_session_id_length(conn) <= 0) {
427+
if (!session_ticket && s2n_connection_get_session_id_length(conn) <= 0) {
417428
printf("Endpoint sent empty session id so cannot resume session\n");
418429
exit(1);
419430
}

bin/s2nd.c

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@
3232
#include <getopt.h>
3333

3434
#include <errno.h>
35+
#include <error.h>
36+
37+
#include <error/s2n_errno.h>
3538

3639
#include <openssl/crypto.h>
3740
#include <openssl/err.h>
@@ -135,6 +138,13 @@ static char dhparams[] =
135138
"HI5CnYmkAwJ6+FSWGaZQDi8bgerFk9RWwwIBAg==\n"
136139
"-----END DH PARAMETERS-----\n";
137140

141+
uint8_t ticket_key_name[16] = "2016.07.26.15\0";
142+
143+
uint8_t default_ticket_key[32] = {0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf, 0x0d, 0xdc,
144+
0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63, 0x90, 0xb6, 0xc7, 0x3b,
145+
0xb5, 0x0f, 0x9c, 0x31, 0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2,
146+
0xb3, 0xe5 };
147+
138148
#define MAX_KEY_LEN 32
139149
#define MAX_VAL_LEN 255
140150

@@ -328,6 +338,10 @@ void usage()
328338
fprintf(stderr, " This option is only used if mutual auth is enabled.\n");
329339
fprintf(stderr, " -i,--insecure\n");
330340
fprintf(stderr, " Turns off certification validation altogether.\n");
341+
fprintf(stderr, " --stk-file\n");
342+
fprintf(stderr, " Location of key file used for encryption and decryption of session ticket.\n");
343+
fprintf(stderr, " -T,--no-session-ticket\n");
344+
fprintf(stderr, " Disable session ticket for resumption.\n");
331345
fprintf(stderr, " -h,--help\n");
332346
fprintf(stderr, " Display this message and quit.\n");
333347

@@ -342,6 +356,7 @@ struct conn_settings {
342356
int prefer_throughput;
343357
int prefer_low_latency;
344358
int enable_mfl;
359+
int session_ticket;
345360
const char *ca_dir;
346361
const char *ca_file;
347362
int insecure;
@@ -439,10 +454,12 @@ int main(int argc, char *const *argv)
439454
const char *certificate_chain_file_path = NULL;
440455
const char *private_key_file_path = NULL;
441456
const char *ocsp_response_file_path = NULL;
457+
const char *session_ticket_key_file_path = NULL;
442458
const char *cipher_prefs = "default";
443459
struct conn_settings conn_settings = { 0 };
444460
int fips_mode = 0;
445461
int parallelize = 0;
462+
conn_settings.session_ticket = 1;
446463

447464
struct option long_options[] = {
448465
{"ciphers", required_argument, NULL, 'c'},
@@ -461,12 +478,14 @@ int main(int argc, char *const *argv)
461478
{"ca-dir", required_argument, 0, 'd'},
462479
{"ca-file", required_argument, 0, 't'},
463480
{"insecure", no_argument, 0, 'i'},
481+
{"stk-file", required_argument, 0, 'a'},
482+
{"no-session-ticket", no_argument, 0, 'T'},
464483
/* Per getopt(3) the last element of the array has to be filled with all zeros */
465484
{ 0 },
466485
};
467486
while (1) {
468487
int option_index = 0;
469-
int c = getopt_long(argc, argv, "c:hmnst:d:i", long_options, &option_index);
488+
int c = getopt_long(argc, argv, "c:hmnst:d:i:T", long_options, &option_index);
470489
if (c == -1) {
471490
break;
472491
}
@@ -519,7 +538,13 @@ int main(int argc, char *const *argv)
519538
break;
520539
case 'i':
521540
conn_settings.insecure = 1;
522-
break;
541+
break;
542+
case 'a':
543+
session_ticket_key_file_path = optarg;
544+
break;
545+
case 'T':
546+
conn_settings.session_ticket = 0;
547+
break;
523548
case '?':
524549
default:
525550
fprintf(stdout, "getopt_long returned: %d", c);
@@ -713,6 +738,45 @@ int main(int argc, char *const *argv)
713738
exit(1);
714739
}
715740

741+
if (conn_settings.session_ticket) {
742+
if (s2n_config_set_session_tickets_onoff(config, 1) < 0) {
743+
fprintf(stderr, "Error enabling session tickets: '%s'\n", s2n_strerror(s2n_errno, "EN"));
744+
exit(1);
745+
}
746+
747+
/* Key initialization */
748+
uint8_t *st_key;
749+
uint32_t st_key_length;
750+
751+
if (session_ticket_key_file_path) {
752+
int fd = open(session_ticket_key_file_path, O_RDONLY);
753+
if (fd < 0) {
754+
error(1, errno, "Error opening session ticket key file");
755+
exit(1);
756+
}
757+
758+
struct stat st;
759+
if (fstat(fd, &st) < 0) {
760+
error(1, errno, "Error fstat-ing session ticket key file");
761+
exit(1);
762+
}
763+
764+
st_key = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
765+
S2N_ERROR_IF(st_key == MAP_FAILED, S2N_ERR_MMAP);
766+
767+
st_key_length = st.st_size;
768+
769+
close(fd);
770+
} else {
771+
st_key = default_ticket_key;
772+
st_key_length = strlen((char *)default_ticket_key);
773+
}
774+
775+
if (s2n_config_add_ticket_crypto_key(config, ticket_key_name, strlen((char *) ticket_key_name), st_key, st_key_length, 0) != 0) {
776+
fprintf(stderr, "Error adding ticket key: '%s'\n", s2n_strerror(s2n_errno, "EN"));
777+
exit(1);
778+
}
779+
}
716780

717781
int fd;
718782
while ((fd = accept(sockfd, ai->ai_addr, &ai->ai_addrlen)) > 0) {

docs/USAGE-GUIDE.md

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1139,30 +1139,63 @@ const char * s2n_connection_get_curve(struct s2n_connection *conn);
11391139

11401140
**s2n_connection_get_curve** returns a string indicating the elliptic curve used during ECDHE key exchange. The string "NONE" is returned if no curve has was used.
11411141

1142-
### Session State Related calls
1142+
### Session Resumption Related calls
11431143

11441144
```c
1145+
int s2n_config_set_session_state_lifetime(struct s2n_config *config, uint32_t lifetime_in_secs);
1146+
11451147
int s2n_connection_set_session(struct s2n_connection *conn, const uint8_t *session, size_t length);
11461148
int s2n_connection_get_session(struct s2n_connection *conn, uint8_t *session, size_t max_length);
1149+
int s2n_connection_get_session_ticket_lifetime_hint(struct s2n_connection *conn);
11471150
ssize_t s2n_connection_get_session_length(struct s2n_connection *conn);
11481151
ssize_t s2n_connection_get_session_id_length(struct s2n_connection *conn);
11491152
int s2n_connection_is_session_resumed(struct s2n_connection *conn);
11501153
```
11511154
1152-
- **session** session will contain serialized session related information needed to resume handshake.
1155+
- **lifetime_in_secs** lifetime of the cached session state required to resume a handshake
1156+
- **session** session will contain serialized session related information needed to resume handshake either using session id or session ticket.
11531157
- **length** length of the serialized session state.
11541158
- **max_length** Max number of bytes to copy into the **session** buffer.
11551159
1156-
**s2n_connection_set_session** de-serializes the session state and updates the connection accrodingly.
1160+
**s2n_config_set_session_state_lifetime** sets the lifetime of the cached session state. The default value is 15 hours.
1161+
1162+
**s2n_connection_set_session** de-serializes the session state and updates the connection accordingly.
1163+
1164+
**s2n_connection_get_session** serializes the session state from connection and copies into the **session** buffer and returns the number of bytes that were copied. If the first byte in **session** is 1, then the next 2 bytes will contain the session ticket length, followed by session ticket and session state. If the first byte in **session** is 0, then the next byte will contain session id length, followed by session id and session state.
11571165
1158-
**s2n_connection_get_session** serializes the session state from connection and copies into the **session** buffer and returns the number of bytes that were copied.
1166+
**s2n_connection_get_session_ticket_lifetime_hint** returns the session ticket lifetime hint in seconds from the server or -1 when session ticket was not used for resumption.
11591167
1160-
**s2n_connection_get_session_length** returns number of bytes needed to store serailized session state; it can be used to allocate the **session** buffer.
1168+
**s2n_connection_get_session_length** returns number of bytes needed to store serialized session state; it can be used to allocate the **session** buffer.
11611169
11621170
**s2n_connection_get_session_id_length** returns session id length from the connection.
11631171
11641172
**s2n_connection_is_session_resumed** checks if the handshake is abbreviated or not.
11651173
1174+
### Session Ticket Specific calls
1175+
1176+
```c
1177+
int s2n_config_set_session_tickets_onoff(struct s2n_config *config, uint8_t enabled);
1178+
int s2n_config_set_ticket_encrypt_decrypt_key_lifetime(struct s2n_config *config, uint64_t lifetime_in_secs);
1179+
int s2n_config_set_ticket_decrypt_key_lifetime(struct s2n_config *config, uint64_t lifetime_in_secs);
1180+
int s2n_config_add_ticket_crypto_key(struct s2n_config *config, const uint8_t *name, uint32_t name_len, uint8_t *key, uint32_t key_len, uint64_t intro_time_in_seconds_from_epoch);
1181+
```
1182+
1183+
- **enabled** when set to 0 will disable session resumption using session ticket
1184+
- **name** name of the session ticket key that should be randomly generated to avoid collisions
1185+
- **name_len** length of session ticket key name
1186+
- **key** key used to perform encryption/decryption of session ticket
1187+
- **key_len** length of the session ticket key
1188+
- **intro_time_in_seconds_from_epoch** time at which the session ticket key is introduced. If this is 0, then intro_time_in_seconds_from_epoch is set to now.
1189+
1190+
**s2n_config_set_session_tickets_onoff** enables and disables session resumption using session ticket
1191+
1192+
**s2n_config_set_ticket_encrypt_decrypt_key_lifetime** sets how long a session ticket key will be in a state where it can be used for both encryption and decryption of tickets on the server side. The default value is 2 hours.
1193+
1194+
**s2n_config_set_ticket_decrypt_key_lifetime** sets how long a session ticket key will be in a state where it can used just for decryption of already assigned tickets on the server side. Once decrypted, the session will resume and the server will issue a new session ticket encrypted using a key in encrypt-decrypt state. The default value is 13 hours.
1195+
1196+
**s2n_config_add_ticket_crypto_key** adds session ticket key on the server side. It would be ideal to add new keys after every (encrypt_decrypt_key_lifetime_in_nanos/2) nanos because
1197+
this will allow for gradual and linear transition of a key from encrypt-decrypt state to decrypt-only state.
1198+
11661199
### s2n\_connection\_wipe
11671200

11681201
```c

error/s2n_errno.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,14 @@ struct s2n_error_translation EN[] = {
141141
{S2N_ERR_MAX_FRAG_LEN_MISMATCH, "Negotiated Maximum Fragmentation Length from server does not match the requested length by client"},
142142
{S2N_ERR_INVALID_SERIALIZED_SESSION_STATE, "Serialized session state is not in valid format"},
143143
{S2N_ERR_SERIALIZED_SESSION_STATE_TOO_LONG, "Serialized session state is too long"},
144+
{S2N_ERR_CLIENT_AUTH_NOT_SUPPORTED_IN_SESSION_RESUMPTION_MODE, "Client Auth is not supported in session resumption mode"},
145+
{S2N_ERR_INVALID_TICKET_KEY_LENGTH, "Session ticket key length cannot be zero"},
146+
{S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH, "Session ticket key name should be unique and the name length cannot be zero"},
147+
{S2N_ERR_TICKET_KEY_LIMIT, "Limit reached for unexpired session ticket keys"},
148+
{S2N_ERR_NO_TICKET_ENCRYPT_DECRYPT_KEY, "No key in encrypt-decrypt state is available to encrypt session ticket"},
149+
{S2N_ERR_ENCRYPT_DECRYPT_KEY_SELECTION_FAILED, "Failed to select a key from keys in encrypt-decrypt state"},
150+
{S2N_ERR_KEY_USED_IN_SESSION_TICKET_NOT_FOUND, "Key used in already assigned session ticket not found for decryption"},
151+
{S2N_ERR_SENDING_NST, "Error in session ticket status encountered before sending NST"},
144152
{S2N_ERR_INVALID_DYNAMIC_THRESHOLD, "invalid dynamic record threshold"}
145153
};
146154

error/s2n_errno.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,14 @@ typedef enum {
156156
S2N_ERR_CANCELLED,
157157
S2N_ERR_INVALID_SERIALIZED_SESSION_STATE,
158158
S2N_ERR_SERIALIZED_SESSION_STATE_TOO_LONG,
159+
S2N_ERR_CLIENT_AUTH_NOT_SUPPORTED_IN_SESSION_RESUMPTION_MODE,
160+
S2N_ERR_INVALID_TICKET_KEY_LENGTH,
161+
S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH,
162+
S2N_ERR_TICKET_KEY_LIMIT,
163+
S2N_ERR_NO_TICKET_ENCRYPT_DECRYPT_KEY,
164+
S2N_ERR_ENCRYPT_DECRYPT_KEY_SELECTION_FAILED,
165+
S2N_ERR_KEY_USED_IN_SESSION_TICKET_NOT_FOUND,
166+
S2N_ERR_SENDING_NST,
159167
S2N_ERR_INVALID_DYNAMIC_THRESHOLD,
160168
} s2n_error;
161169

0 commit comments

Comments
 (0)