From 8b17fbaf46ae8a1319be5975ad8da3e0f4932e77 Mon Sep 17 00:00:00 2001 From: Nicola Tuveri Date: Mon, 28 Sep 2020 04:32:03 +0300 Subject: [ssl] Support ssl_encapsulate on server side Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/13018) --- ssl/s3_lib.c | 65 +++++++++++++++++++++++++- ssl/ssl_local.h | 4 ++ ssl/statem/extensions_srvr.c | 106 +++++++++++++++++++++++++++++++++---------- 3 files changed, 149 insertions(+), 26 deletions(-) (limited to 'ssl') diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c index 96569ae..1fd424a 100644 --- a/ssl/s3_lib.c +++ b/ssl/s3_lib.c @@ -4833,7 +4833,6 @@ EVP_PKEY *ssl_generate_param_group(SSL *s, uint16_t id) } /* Generate secrets from pms */ -__owur static int ssl_gensecret(SSL *s, unsigned char *pms, size_t pmslen) { int rv = 0; @@ -4973,6 +4972,70 @@ int ssl_decapsulate(SSL *s, EVP_PKEY *privkey, return rv; } +int ssl_encapsulate(SSL *s, EVP_PKEY *pubkey, + unsigned char **ctp, size_t *ctlenp, + int gensecret) +{ + int rv = 0; + unsigned char *pms = NULL, *ct = NULL; + size_t pmslen = 0, ctlen = 0; + EVP_PKEY_CTX *pctx; + + if (pubkey == NULL) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL_ENCAPSULATE, + ERR_R_INTERNAL_ERROR); + return 0; + } + + pctx = EVP_PKEY_CTX_new_from_pkey(s->ctx->libctx, pubkey, s->ctx->propq); + + if (EVP_PKEY_encapsulate_init(pctx) <= 0 + || EVP_PKEY_encapsulate(pctx, NULL, &ctlen, NULL, &pmslen) <= 0 + || pmslen == 0 || ctlen == 0) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL_ENCAPSULATE, + ERR_R_INTERNAL_ERROR); + goto err; + } + + pms = OPENSSL_malloc(pmslen); + ct = OPENSSL_malloc(ctlen); + if (pms == NULL || ct == NULL) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL_ENCAPSULATE, + ERR_R_MALLOC_FAILURE); + goto err; + } + + if (EVP_PKEY_encapsulate(pctx, ct, &ctlen, pms, &pmslen) <= 0) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL_ENCAPSULATE, + ERR_R_INTERNAL_ERROR); + goto err; + } + + if (gensecret) { + /* SSLfatal() called as appropriate in the below functions */ + rv = ssl_gensecret(s, pms, pmslen); + } else { + /* Save premaster secret */ + s->s3.tmp.pms = pms; + s->s3.tmp.pmslen = pmslen; + pms = NULL; + rv = 1; + } + + if (rv > 0) { + /* Pass ownership of ct to caller */ + *ctp = ct; + *ctlenp = ctlen; + ct = NULL; + } + + err: + OPENSSL_clear_free(pms, pmslen); + OPENSSL_free(ct); + EVP_PKEY_CTX_free(pctx); + return rv; +} + #ifndef OPENSSL_NO_DH EVP_PKEY *ssl_dh_to_pkey(DH *dh) { diff --git a/ssl/ssl_local.h b/ssl/ssl_local.h index 3a4727f..66a84cf 100644 --- a/ssl/ssl_local.h +++ b/ssl/ssl_local.h @@ -2454,11 +2454,15 @@ __owur int ssl_fill_hello_random(SSL *s, int server, unsigned char *field, __owur int ssl_generate_master_secret(SSL *s, unsigned char *pms, size_t pmslen, int free_pms); __owur EVP_PKEY *ssl_generate_pkey(SSL *s, EVP_PKEY *pm); +__owur int ssl_gensecret(SSL *s, unsigned char *pms, size_t pmslen); __owur int ssl_derive(SSL *s, EVP_PKEY *privkey, EVP_PKEY *pubkey, int genmaster); __owur int ssl_decapsulate(SSL *s, EVP_PKEY *privkey, const unsigned char *ct, size_t ctlen, int gensecret); +__owur int ssl_encapsulate(SSL *s, EVP_PKEY *pubkey, + unsigned char **ctp, size_t *ctlenp, + int gensecret); __owur EVP_PKEY *ssl_dh_to_pkey(DH *dh); __owur unsigned int ssl_get_max_send_fragment(const SSL *ssl); __owur unsigned int ssl_get_split_send_fragment(const SSL *ssl); diff --git a/ssl/statem/extensions_srvr.c b/ssl/statem/extensions_srvr.c index 9ec48ef..eb24d0a 100644 --- a/ssl/statem/extensions_srvr.c +++ b/ssl/statem/extensions_srvr.c @@ -1696,6 +1696,7 @@ EXT_RETURN tls_construct_stoc_key_share(SSL *s, WPACKET *pkt, unsigned char *encodedPoint; size_t encoded_pt_len = 0; EVP_PKEY *ckey = s->s3.peer_tmp, *skey = NULL; + const TLS_GROUP_INFO *ginf = NULL; if (s->hello_retry_request == SSL_HRR_PENDING) { if (ckey != NULL) { @@ -1733,37 +1734,92 @@ EXT_RETURN tls_construct_stoc_key_share(SSL *s, WPACKET *pkt, return EXT_RETURN_FAIL; } - skey = ssl_generate_pkey(s, ckey); - if (skey == NULL) { - SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_STOC_KEY_SHARE, - ERR_R_MALLOC_FAILURE); + if ((ginf = tls1_group_id_lookup(s->ctx, s->s3.group_id)) == NULL) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, + SSL_F_TLS_CONSTRUCT_STOC_KEY_SHARE, ERR_R_INTERNAL_ERROR); return EXT_RETURN_FAIL; } - /* Generate encoding of server key */ - encoded_pt_len = EVP_PKEY_get1_tls_encodedpoint(skey, &encodedPoint); - if (encoded_pt_len == 0) { - SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_STOC_KEY_SHARE, - ERR_R_EC_LIB); - EVP_PKEY_free(skey); - return EXT_RETURN_FAIL; - } + if (!ginf->is_kem) { + /* Regular KEX */ + skey = ssl_generate_pkey(s, ckey); + if (skey == NULL) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, + SSL_F_TLS_CONSTRUCT_STOC_KEY_SHARE, + ERR_R_MALLOC_FAILURE); + return EXT_RETURN_FAIL; + } - if (!WPACKET_sub_memcpy_u16(pkt, encodedPoint, encoded_pt_len) - || !WPACKET_close(pkt)) { - SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_STOC_KEY_SHARE, - ERR_R_INTERNAL_ERROR); - EVP_PKEY_free(skey); + /* Generate encoding of server key */ + encoded_pt_len = EVP_PKEY_get1_tls_encodedpoint(skey, &encodedPoint); + if (encoded_pt_len == 0) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, + SSL_F_TLS_CONSTRUCT_STOC_KEY_SHARE, + ERR_R_EC_LIB); + EVP_PKEY_free(skey); + return EXT_RETURN_FAIL; + } + + if (!WPACKET_sub_memcpy_u16(pkt, encodedPoint, encoded_pt_len) + || !WPACKET_close(pkt)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, + SSL_F_TLS_CONSTRUCT_STOC_KEY_SHARE, + ERR_R_INTERNAL_ERROR); + EVP_PKEY_free(skey); + OPENSSL_free(encodedPoint); + return EXT_RETURN_FAIL; + } OPENSSL_free(encodedPoint); - return EXT_RETURN_FAIL; - } - OPENSSL_free(encodedPoint); - /* This causes the crypto state to be updated based on the derived keys */ - s->s3.tmp.pkey = skey; - if (ssl_derive(s, skey, ckey, 1) == 0) { - /* SSLfatal() already called */ - return EXT_RETURN_FAIL; + /* + * This causes the crypto state to be updated based on the derived keys + */ + s->s3.tmp.pkey = skey; + if (ssl_derive(s, skey, ckey, 1) == 0) { + /* SSLfatal() already called */ + return EXT_RETURN_FAIL; + } + } else { + /* KEM mode */ + unsigned char *ct = NULL; + size_t ctlen = 0; + + /* + * This does not update the crypto state. + * + * The generated pms is stored in `s->s3.tmp.pms` to be later used via + * ssl_gensecret(). + */ + if (ssl_encapsulate(s, ckey, &ct, &ctlen, 0) == 0) { + /* SSLfatal() already called */ + return EXT_RETURN_FAIL; + } + + if (ctlen == 0) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, + SSL_F_TLS_CONSTRUCT_STOC_KEY_SHARE, + ERR_R_INTERNAL_ERROR); + OPENSSL_free(ct); + return EXT_RETURN_FAIL; + } + + if (!WPACKET_sub_memcpy_u16(pkt, ct, ctlen) + || !WPACKET_close(pkt)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, + SSL_F_TLS_CONSTRUCT_STOC_KEY_SHARE, + ERR_R_INTERNAL_ERROR); + OPENSSL_free(ct); + return EXT_RETURN_FAIL; + } + OPENSSL_free(ct); + + /* + * This causes the crypto state to be updated based on the generated pms + */ + if (ssl_gensecret(s, s->s3.tmp.pms, s->s3.tmp.pmslen) == 0) { + /* SSLfatal() already called */ + return EXT_RETURN_FAIL; + } } return EXT_RETURN_SENT; #else -- cgit v1.1