diff options
author | Matt Caswell <matt@openssl.org> | 2017-01-13 17:00:49 +0000 |
---|---|---|
committer | Matt Caswell <matt@openssl.org> | 2017-01-30 10:18:19 +0000 |
commit | ec15acb6bc554b8f87a519c3519f5bf4d367ded9 (patch) | |
tree | ca7810034fa64bb1218a04b3d4c22741bc166667 /ssl/statem | |
parent | a2b7e65526d92123f143cc7f248b4ac017372faf (diff) |
Construct the client side psk extension for TLSv1.3
Reviewed-by: Rich Salz <rsalz@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/2259)
Diffstat (limited to 'ssl/statem')
-rw-r--r-- | ssl/statem/extensions.c | 10 | ||||
-rw-r--r-- | ssl/statem/extensions_clnt.c | 164 | ||||
-rw-r--r-- | ssl/statem/statem_lib.c | 2 | ||||
-rw-r--r-- | ssl/statem/statem_locl.h | 2 | ||||
-rw-r--r-- | ssl/statem/statem_srvr.c | 2 |
5 files changed, 177 insertions, 3 deletions
diff --git a/ssl/statem/extensions.c b/ssl/statem/extensions.c index dc992010ec..5837720eb7 100644 --- a/ssl/statem/extensions.c +++ b/ssl/statem/extensions.c @@ -264,12 +264,20 @@ static const EXTENSION_DEFINITION ext_defs[] = { NULL, NULL, NULL, tls_construct_stoc_cryptopro_bug, NULL, NULL }, { - /* Last in the list because it must be added as the last extension */ + /* Must be immediately before pre_shared_key */ + /* TODO(TLS1.3): Fix me */ TLSEXT_TYPE_padding, EXT_CLIENT_HELLO, NULL, /* We send this, but don't read it */ NULL, NULL, NULL, tls_construct_ctos_padding, NULL + }, + { + /* Required by the TLSv1.3 spec to always be the last extension */ + TLSEXT_TYPE_psk, + EXT_CLIENT_HELLO | EXT_TLS1_3_SERVER_HELLO | EXT_TLS_IMPLEMENTATION_ONLY + | EXT_TLS1_3_ONLY, + NULL, NULL, NULL, NULL, tls_construct_ctos_psk, NULL } }; diff --git a/ssl/statem/extensions_clnt.c b/ssl/statem/extensions_clnt.c index 57f6564678..c6a8124c9e 100644 --- a/ssl/statem/extensions_clnt.c +++ b/ssl/statem/extensions_clnt.c @@ -657,6 +657,170 @@ int tls_construct_ctos_padding(SSL *s, WPACKET *pkt, X509 *x, size_t chainidx, } /* + * Construct the pre_shared_key extension + */ +int tls_construct_ctos_psk(SSL *s, WPACKET *pkt, X509 *x, size_t chainidx, + int *al) +{ +#ifndef OPENSSL_NO_TLS1_3 + const SSL_CIPHER *cipher; + uint32_t now, ages, agems; + size_t hashsize, bindersize, binderoffset, msglen; + unsigned char *binder = NULL, *msgstart = NULL; + EVP_PKEY *mackey = NULL; + const EVP_MD *md; + EVP_MD_CTX *mctx = NULL; + unsigned char hash[EVP_MAX_MD_SIZE], binderkey[EVP_MAX_MD_SIZE]; + unsigned char finishedkey[EVP_MAX_MD_SIZE]; + const char resumption_label[] = "resumption psk binder key"; + int ret = 0; + + s->session->ext.tick_identity = TLSEXT_PSK_BAD_IDENTITY; + + /* + * If this is a new session then we have nothing to resume so don't add + * this extension. + */ + if (s->session->ext.ticklen == 0) + return 1; + + /* + * Technically the C standard just says time() returns a time_t and says + * nothing about the encoding of that type. In practice most implementations + * follow POSIX which holds it as an integral type in seconds since epoch. + * We've already made the assumption that we can do this in multiple places + * in the code, so portability shouldn't be an issue. + */ + now = (uint32_t)time(NULL); + ages = now - (uint32_t)s->session->time; + + /* + * Calculate age in ms. We're just doing it to nearest second. Should be + * good enough. + */ + agems = ages * (uint32_t)1000; + + if (ages != 0 && agems / (uint32_t)1000 != ages) { + /* + * Overflow. Shouldn't happen unless this is a *really* old session. If + * so we just ignore it. + */ + return 1; + } + + /* TODO(TLS1.3): Obfuscate the age here */ + + cipher = ssl3_get_cipher_by_id(s->session->cipher_id); + if (cipher == NULL) { + /* Don't recognise this cipher so we can't use the session. Ignore it */ + return 1; + } + md = ssl_md(cipher->algorithm2); + if (md == NULL) { + /* Shouldn't happen!! */ + SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_PSK, ERR_R_INTERNAL_ERROR); + return 0; + } + hashsize = EVP_MD_size(md); + + /* Create the extension, but skip over the binder for now */ + if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_psk) + || !WPACKET_start_sub_packet_u16(pkt) + || !WPACKET_start_sub_packet_u16(pkt) + || !WPACKET_sub_memcpy_u16(pkt, s->session->ext.tick, + s->session->ext.ticklen) + || !WPACKET_put_bytes_u32(pkt, agems) + || !WPACKET_close(pkt) + || !WPACKET_get_total_written(pkt, &binderoffset) + || !WPACKET_start_sub_packet_u16(pkt) + || !WPACKET_sub_allocate_bytes_u8(pkt, hashsize, &binder) + || !WPACKET_close(pkt) + || !WPACKET_close(pkt) + || !WPACKET_get_total_written(pkt, &msglen) + /* + * We need to fill in all the sub-packet lengths now so we can + * calculate the HMAC of the message up to the binders + */ + || !WPACKET_fill_lengths(pkt)) { + SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_PSK, ERR_R_INTERNAL_ERROR); + goto err; + } + + msgstart = WPACKET_get_curr(pkt) - msglen; + + /* Generate the early_secret */ + if (!tls13_generate_secret(s, md, NULL, s->session->master_key, + s->session->master_key_length, + (unsigned char *)&s->early_secret)) { + SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_PSK, ERR_R_INTERNAL_ERROR); + goto err; + } + + /* + * Create the handshake hash for the binder key...the messages so far are + * empty! + */ + mctx = EVP_MD_CTX_new(); + if (mctx == NULL + || EVP_DigestInit_ex(mctx, md, NULL) <= 0 + || EVP_DigestFinal_ex(mctx, hash, NULL) <= 0) { + SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_PSK, ERR_R_INTERNAL_ERROR); + goto err; + } + + /* Generate the binder key */ + if (!tls13_hkdf_expand(s, md, s->early_secret, + (unsigned char *)resumption_label, + sizeof(resumption_label) - 1, hash, binderkey, + hashsize)) { + SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_PSK, ERR_R_INTERNAL_ERROR); + goto err; + } + + /* Generate the finished key */ + if (!tls13_derive_finishedkey(s, md, binderkey, finishedkey, hashsize)) { + SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_PSK, ERR_R_INTERNAL_ERROR); + goto err; + } + + /* + * Get a hash of the ClientHello up to the start of the binders. + * TODO(TLS1.3): This will need to be tweaked when we implement + * HelloRetryRequest to include the digest of the previous messages here. + */ + if (EVP_DigestInit_ex(mctx, md, NULL) <= 0 + || EVP_DigestUpdate(mctx, msgstart, binderoffset) <= 0 + || EVP_DigestFinal_ex(mctx, hash, NULL) <= 0) { + SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_PSK, ERR_R_INTERNAL_ERROR); + goto err; + } + + mackey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, finishedkey, hashsize); + bindersize = hashsize; + if (binderkey == NULL + || EVP_DigestSignInit(mctx, NULL, md, NULL, mackey) <= 0 + || EVP_DigestSignUpdate(mctx, hash, hashsize) <= 0 + || EVP_DigestSignFinal(mctx, binder, &bindersize) <= 0 + || bindersize != hashsize) { + SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_PSK, ERR_R_INTERNAL_ERROR); + goto err; + } + + s->session->ext.tick_identity = 0; + + ret = 1; + err: + OPENSSL_cleanse(binderkey, sizeof(binderkey)); + EVP_PKEY_free(mackey); + EVP_MD_CTX_free(mctx); + + return ret; +#else + return 1; +#endif +} + +/* * Parse the server's renegotiation binding and abort if it's not right */ int tls_parse_stoc_renegotiate(SSL *s, PACKET *pkt, X509 *x, size_t chainidx, diff --git a/ssl/statem/statem_lib.c b/ssl/statem/statem_lib.c index ad1466f9a9..3a6b672a0f 100644 --- a/ssl/statem/statem_lib.c +++ b/ssl/statem/statem_lib.c @@ -650,7 +650,7 @@ MSG_PROCESS_RETURN tls_process_finished(SSL *s, PACKET *pkt) } } else { if (!s->method->ssl3_enc->generate_master_secret(s, - s->session->master_key, s->handshake_secret, 0, + s->master_secret, s->handshake_secret, 0, &s->session->master_key_length)) { SSLerr(SSL_F_TLS_PROCESS_FINISHED, SSL_R_CANNOT_CHANGE_CIPHER); goto f_err; diff --git a/ssl/statem/statem_locl.h b/ssl/statem/statem_locl.h index d15c69686e..67858057f7 100644 --- a/ssl/statem/statem_locl.h +++ b/ssl/statem/statem_locl.h @@ -290,6 +290,8 @@ int tls_construct_ctos_psk_kex_modes(SSL *s, WPACKET *pkt, X509 *x, size_t chainidx, int *al); int tls_construct_ctos_padding(SSL *s, WPACKET *pkt, X509 *x, size_t chainidx, int *al); +int tls_construct_ctos_psk(SSL *s, WPACKET *pkt, X509 *x, size_t chainidx, + int *al); int tls_parse_stoc_renegotiate(SSL *s, PACKET *pkt, X509 *x, size_t chainidx, int *al); int tls_parse_stoc_server_name(SSL *s, PACKET *pkt, X509 *x, size_t chainidx, diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c index 6b3d4f7749..20e521a9d6 100644 --- a/ssl/statem/statem_srvr.c +++ b/ssl/statem/statem_srvr.c @@ -800,7 +800,7 @@ WORK_STATE ossl_statem_server_post_work(SSL *s, WORK_STATE wst) #endif if (SSL_IS_TLS13(s)) { if (!s->method->ssl3_enc->generate_master_secret(s, - s->session->master_key, s->handshake_secret, 0, + s->master_secret, s->handshake_secret, 0, &s->session->master_key_length) || !s->method->ssl3_enc->change_cipher_state(s, SSL3_CC_APPLICATION | SSL3_CHANGE_CIPHER_SERVER_WRITE)) |