aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatt Caswell <matt@openssl.org>2017-06-12 13:30:21 +0100
committerMatt Caswell <matt@openssl.org>2017-06-21 14:45:35 +0100
commit9368f86526d9feb1f511fab9c0eee8e8dc869b87 (patch)
treeb10c253f6a3f118a4009ecfc3120cfec403d2d54
parent3a7c56b2c0e6680e4ad3d83387a970110efdf1ba (diff)
downloadopenssl-9368f86526d9feb1f511fab9c0eee8e8dc869b87.zip
openssl-9368f86526d9feb1f511fab9c0eee8e8dc869b87.tar.gz
openssl-9368f86526d9feb1f511fab9c0eee8e8dc869b87.tar.bz2
Add TLSv1.3 client side external PSK support
Reviewed-by: Rich Salz <rsalz@openssl.org> (Merged from https://github.com/openssl/openssl/pull/3670)
-rw-r--r--crypto/err/openssl.txt1
-rw-r--r--include/openssl/ssl.h4
-rw-r--r--include/openssl/sslerr.h1
-rw-r--r--ssl/ssl_err.c1
-rw-r--r--ssl/ssl_lib.c3
-rw-r--r--ssl/ssl_locl.h6
-rw-r--r--ssl/statem/extensions.c18
-rw-r--r--ssl/statem/extensions_clnt.c194
8 files changed, 170 insertions, 58 deletions
diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt
index e2cf2c5..813ca74 100644
--- a/crypto/err/openssl.txt
+++ b/crypto/err/openssl.txt
@@ -2137,6 +2137,7 @@ SSL_R_BAD_KEY_UPDATE:122:bad key update
SSL_R_BAD_LENGTH:271:bad length
SSL_R_BAD_PACKET_LENGTH:115:bad packet length
SSL_R_BAD_PROTOCOL_VERSION_NUMBER:116:bad protocol version number
+SSL_R_BAD_PSK:219:bad psk
SSL_R_BAD_PSK_IDENTITY:114:bad psk identity
SSL_R_BAD_RECORD_TYPE:443:bad record type
SSL_R_BAD_RSA_ENCRYPT:119:bad rsa encrypt
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 2dfa7f6..41edb38 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -767,6 +767,10 @@ typedef int (*SSL_psk_find_session_cb_func)(SSL *ssl,
const unsigned char *identity,
size_t identity_len,
SSL_SESSION **sess);
+typedef int (*SSL_psk_use_session_cb_func)(SSL *ssl, const EVP_MD *md,
+ const unsigned char **id,
+ size_t *idlen,
+ SSL_SESSION **sess);
void SSL_CTX_set_psk_server_callback(SSL_CTX *ctx, SSL_psk_server_cb_func cb);
void SSL_set_psk_server_callback(SSL *ssl, SSL_psk_server_cb_func cb);
diff --git a/include/openssl/sslerr.h b/include/openssl/sslerr.h
index 8dfc974..540baff 100644
--- a/include/openssl/sslerr.h
+++ b/include/openssl/sslerr.h
@@ -381,6 +381,7 @@ int ERR_load_SSL_strings(void);
# define SSL_R_BAD_LENGTH 271
# define SSL_R_BAD_PACKET_LENGTH 115
# define SSL_R_BAD_PROTOCOL_VERSION_NUMBER 116
+# define SSL_R_BAD_PSK 219
# define SSL_R_BAD_PSK_IDENTITY 114
# define SSL_R_BAD_RECORD_TYPE 443
# define SSL_R_BAD_RSA_ENCRYPT 119
diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c
index eccc5af..0b82c0e 100644
--- a/ssl/ssl_err.c
+++ b/ssl/ssl_err.c
@@ -594,6 +594,7 @@ static const ERR_STRING_DATA SSL_str_reasons[] = {
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_PACKET_LENGTH), "bad packet length"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_PROTOCOL_VERSION_NUMBER),
"bad protocol version number"},
+ {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_PSK), "bad psk"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_PSK_IDENTITY), "bad psk identity"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_RECORD_TYPE), "bad record type"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_RSA_ENCRYPT), "bad rsa encrypt"},
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index a46ede1..4bcbfa8 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -419,6 +419,8 @@ int SSL_clear(SSL *s)
SSL_SESSION_free(s->session);
s->session = NULL;
}
+ SSL_SESSION_free(s->psksession);
+ s->psksession = NULL;
s->error = 0;
s->hit = 0;
@@ -969,6 +971,7 @@ void SSL_free(SSL *s)
ssl_clear_bad_session(s);
SSL_SESSION_free(s->session);
}
+ SSL_SESSION_free(s->psksession);
clear_ciphers(s);
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index b26db6a..7889ab5 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -485,6 +485,8 @@ struct ssl_session_st {
* in here? */
size_t master_key_length;
+ /* TLSv1.3 early_secret used for external PSKs */
+ unsigned char early_secret[EVP_MAX_MD_SIZE];
/*
* For <=TLS1.2 this is the master_key. For TLS1.3 this is the resumption
* master secret
@@ -953,6 +955,7 @@ struct ssl_ctx_st {
SSL_psk_server_cb_func psk_server_callback;
# endif
SSL_psk_find_session_cb_func psk_find_session_cb;
+ SSL_psk_use_session_cb_func psk_use_session_cb;
# ifndef OPENSSL_NO_SRP
SRP_CTX srp_ctx; /* ctx for SRP authentication */
@@ -1103,6 +1106,8 @@ struct ssl_st {
unsigned char sid_ctx[SSL_MAX_SID_CTX_LENGTH];
/* This can also be in the session once a session is established */
SSL_SESSION *session;
+ /* TLSv1.3 PSK session */
+ SSL_SESSION *psksession;
/* Default generate session ID callback. */
GEN_SESSION_CB generate_session_id;
/* Used in SSL3 */
@@ -1124,6 +1129,7 @@ struct ssl_st {
SSL_psk_server_cb_func psk_server_callback;
# endif
SSL_psk_find_session_cb_func psk_find_session_cb;
+ SSL_psk_use_session_cb_func psk_use_session_cb;
SSL_CTX *ctx;
/* Verified chain of peer */
STACK_OF(X509) *verified_chain;
diff --git a/ssl/statem/extensions.c b/ssl/statem/extensions.c
index 7defbb1..4965231 100644
--- a/ssl/statem/extensions.c
+++ b/ssl/statem/extensions.c
@@ -1234,6 +1234,7 @@ int tls_psk_do_binder(SSL *s, const EVP_MD *md, const unsigned char *msgstart,
EVP_MD_CTX *mctx = NULL;
unsigned char hash[EVP_MAX_MD_SIZE], binderkey[EVP_MAX_MD_SIZE];
unsigned char finishedkey[EVP_MAX_MD_SIZE], tmpbinder[EVP_MAX_MD_SIZE];
+ unsigned char *early_secret;
const char resumption_label[] = "res binder";
const char external_label[] = "ext binder";
const char *label;
@@ -1248,10 +1249,19 @@ int tls_psk_do_binder(SSL *s, const EVP_MD *md, const unsigned char *msgstart,
labelsize = sizeof(resumption_label) - 1;
}
- /* Generate the early_secret */
+ /*
+ * Generate the early_secret. On the server side we've selected a PSK to
+ * resume with (internal or external) so we always do this. On the client
+ * side we do this for a non-external (i.e. resumption) PSK so that it
+ * is in place for sending early data. For client side external PSK we
+ * generate it but store it away for later use.
+ */
+ if (s->server || !external)
+ early_secret = (unsigned char *)s->early_secret;
+ else
+ early_secret = (unsigned char *)sess->early_secret;
if (!tls13_generate_secret(s, md, NULL, sess->master_key,
- sess->master_key_length,
- (unsigned char *)&s->early_secret)) {
+ sess->master_key_length, early_secret)) {
SSLerr(SSL_F_TLS_PSK_DO_BINDER, ERR_R_INTERNAL_ERROR);
goto err;
}
@@ -1269,7 +1279,7 @@ int tls_psk_do_binder(SSL *s, const EVP_MD *md, const unsigned char *msgstart,
}
/* Generate the binder key */
- if (!tls13_hkdf_expand(s, md, s->early_secret, (unsigned char *)label,
+ if (!tls13_hkdf_expand(s, md, early_secret, (unsigned char *)label,
labelsize, hash, binderkey, hashsize)) {
SSLerr(SSL_F_TLS_PSK_DO_BINDER, ERR_R_INTERNAL_ERROR);
goto err;
diff --git a/ssl/statem/extensions_clnt.c b/ssl/statem/extensions_clnt.c
index 2f6d16e..5733a11 100644
--- a/ssl/statem/extensions_clnt.c
+++ b/ssl/statem/extensions_clnt.c
@@ -791,10 +791,13 @@ EXT_RETURN tls_construct_ctos_psk(SSL *s, WPACKET *pkt, unsigned int context,
{
#ifndef OPENSSL_NO_TLS1_3
uint32_t now, agesec, agems;
- size_t hashsize, binderoffset, msglen;
- unsigned char *binder = NULL, *msgstart = NULL;
- const EVP_MD *md;
+ size_t reshashsize, pskhashsize, binderoffset, msglen, idlen;
+ unsigned char *resbinder = NULL, *pskbinder = NULL, *msgstart = NULL;
+ const unsigned char *id;
+ const EVP_MD *handmd = NULL, *mdres, *mdpsk;
EXT_RETURN ret = EXT_RETURN_FAIL;
+ SSL_SESSION *psksess = NULL;
+ int dores = 0;
s->session->ext.tick_identity = TLSEXT_PSK_BAD_IDENTITY;
@@ -809,76 +812,136 @@ EXT_RETURN tls_construct_ctos_psk(SSL *s, WPACKET *pkt, unsigned int context,
* so don't add this extension.
*/
if (s->session->ssl_version != TLS1_3_VERSION
- || s->session->ext.ticklen == 0)
+ || (s->session->ext.ticklen == 0 && s->psk_use_session_cb == NULL))
return EXT_RETURN_NOT_SENT;
- if (s->session->cipher == NULL) {
- SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_PSK, ERR_R_INTERNAL_ERROR);
+ if (s->hello_retry_request)
+ handmd = ssl_handshake_md(s);
+
+ if (s->psk_use_session_cb != NULL
+ && !s->psk_use_session_cb(s, handmd, &id, &idlen, &psksess)) {
+ SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_PSK, SSL_R_BAD_PSK);
goto err;
}
- md = ssl_md(s->session->cipher->algorithm2);
- if (md == NULL) {
- /* Don't recognize this cipher so we can't use the session. Ignore it */
- return EXT_RETURN_NOT_SENT;
- }
+ if (s->session->ext.ticklen != 0) {
+ if (s->session->cipher == NULL) {
+ SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_PSK, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+
+ mdres = ssl_md(s->session->cipher->algorithm2);
+ if (mdres == NULL) {
+ /* Don't recognize this cipher so we can't use the session. Ignore it */
+ goto dopsksess;
+ }
+
+ if (s->hello_retry_request && mdres != handmd) {
+ /*
+ * Selected ciphersuite hash does not match the hash for the session so
+ * we can't use it.
+ */
+ goto dopsksess;
+ }
- if (s->hello_retry_request && md != ssl_handshake_md(s)) {
/*
- * Selected ciphersuite hash does not match the hash for the session so
- * we can't use it.
+ * 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.
*/
- return EXT_RETURN_NOT_SENT;
- }
+ now = (uint32_t)time(NULL);
+ agesec = now - (uint32_t)s->session->time;
- /*
- * 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);
- agesec = now - (uint32_t)s->session->time;
+ if (s->session->ext.tick_lifetime_hint < agesec) {
+ /* Ticket is too old. Ignore it. */
+ goto dopsksess;
+ }
- if (s->session->ext.tick_lifetime_hint < agesec) {
- /* Ticket is too old. Ignore it. */
- return EXT_RETURN_NOT_SENT;
- }
+ /*
+ * Calculate age in ms. We're just doing it to nearest second. Should be
+ * good enough.
+ */
+ agems = agesec * (uint32_t)1000;
- /*
- * Calculate age in ms. We're just doing it to nearest second. Should be
- * good enough.
- */
- agems = agesec * (uint32_t)1000;
+ if (agesec != 0 && agems / (uint32_t)1000 != agesec) {
+ /*
+ * Overflow. Shouldn't happen unless this is a *really* old session. If
+ * so we just ignore it.
+ */
+ goto dopsksess;
+ }
- if (agesec != 0 && agems / (uint32_t)1000 != agesec) {
/*
- * Overflow. Shouldn't happen unless this is a *really* old session. If
- * so we just ignore it.
+ * Obfuscate the age. Overflow here is fine, this addition is supposed to
+ * be mod 2^32.
*/
- return EXT_RETURN_NOT_SENT;
+ agems += s->session->ext.tick_age_add;
+
+ reshashsize = EVP_MD_size(mdres);
+ dores = 1;
}
- /*
- * Obfuscate the age. Overflow here is fine, this addition is supposed to
- * be mod 2^32.
- */
- agems += s->session->ext.tick_age_add;
+ dopsksess:
+ if (!dores && psksess == NULL)
+ return EXT_RETURN_NOT_SENT;
- hashsize = EVP_MD_size(md);
+ if (psksess != NULL) {
+ mdpsk = ssl_md(psksess->cipher->algorithm2);
+ if (mdpsk == NULL) {
+ /*
+ * Don't recognize this cipher so we can't use the session.
+ * If this happens it's an application bug.
+ */
+ SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_PSK, SSL_R_BAD_PSK);
+ goto err;
+ }
+
+ if (s->hello_retry_request && mdres != handmd) {
+ /*
+ * Selected ciphersuite hash does not match the hash for the PSK
+ * session. This is an application bug.
+ */
+ SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_PSK, SSL_R_BAD_PSK);
+ goto err;
+ }
+
+ pskhashsize = EVP_MD_size(mdpsk);
+ }
/* 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_start_sub_packet_u16(pkt)) {
+ SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_PSK, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+
+ if (dores) {
+ if (!WPACKET_sub_memcpy_u16(pkt, s->session->ext.tick,
+ s->session->ext.ticklen)
+ || !WPACKET_put_bytes_u32(pkt, agems)) {
+ SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_PSK, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+ }
+
+ if (psksess != NULL) {
+ if (!WPACKET_sub_memcpy_u16(pkt, id, idlen)
+ || !WPACKET_put_bytes_u32(pkt, 0)) {
+ SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_PSK, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+ }
+
+ if (!WPACKET_close(pkt)
|| !WPACKET_get_total_written(pkt, &binderoffset)
|| !WPACKET_start_sub_packet_u16(pkt)
- || !WPACKET_sub_allocate_bytes_u8(pkt, hashsize, &binder)
+ || (dores
+ && !WPACKET_sub_allocate_bytes_u8(pkt, reshashsize, &resbinder))
+ || (psksess != NULL
+ && !WPACKET_sub_allocate_bytes_u8(pkt, pskhashsize, &pskbinder))
|| !WPACKET_close(pkt)
|| !WPACKET_close(pkt)
|| !WPACKET_get_total_written(pkt, &msglen)
@@ -893,13 +956,24 @@ EXT_RETURN tls_construct_ctos_psk(SSL *s, WPACKET *pkt, unsigned int context,
msgstart = WPACKET_get_curr(pkt) - msglen;
- if (tls_psk_do_binder(s, md, msgstart, binderoffset, NULL, binder,
- s->session, 1, 0) != 1) {
+ if (dores && tls_psk_do_binder(s, mdres, msgstart, binderoffset, NULL,
+ resbinder, s->session, 1, 0) != 1) {
SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_PSK, ERR_R_INTERNAL_ERROR);
goto err;
}
- s->session->ext.tick_identity = 0;
+ if (psksess != NULL && tls_psk_do_binder(s, mdpsk, msgstart,
+ binderoffset, NULL, pskbinder,
+ psksess, 1, 1) != 1) {
+ SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_PSK, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+
+ if (dores)
+ s->session->ext.tick_identity = 0;
+ s->psksession = psksess;
+ if (psksess != NULL)
+ s->psksession->ext.tick_identity = (dores ? 1 : 0);
ret = EXT_RETURN_SENT;
err:
@@ -1508,12 +1582,24 @@ int tls_parse_stoc_psk(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
return 0;
}
- if (s->session->ext.tick_identity != (int)identity) {
+ if (s->session->ext.tick_identity == (int)identity) {
+ s->hit = 1;
+ SSL_SESSION_free(s->psksession);
+ s->psksession = NULL;
+ return 1;
+ }
+
+ if (s->psksession == NULL
+ || s->psksession->ext.tick_identity != (int)identity) {
*al = SSL_AD_ILLEGAL_PARAMETER;
SSLerr(SSL_F_TLS_PARSE_STOC_PSK, SSL_R_BAD_PSK_IDENTITY);
return 0;
}
+ SSL_SESSION_free(s->session);
+ s->session = s->psksession;
+ s->psksession = NULL;
+ memcpy(s->early_secret, s->session->early_secret, EVP_MAX_MD_SIZE);
s->hit = 1;
#endif