# ifndef OPENSSL_NO_ECH
-int ossl_ech_get_sh_offsets(const unsigned char *sh, size_t sh_len,
- size_t *exts, size_t *echoffset,
- uint16_t *echtype);
-int ossl_ech_make_enc_info(unsigned char *encoding, size_t encoding_length,
+int ossl_ech_make_enc_info(const unsigned char *encoding,
+ size_t encoding_length,
unsigned char *info, size_t *info_len);
# endif
static const char OSSL_ECH_CONTEXT_STRING[] = "\x74\x6c\x73\x20\x65\x63\x68";
/*
- * Given a SH (or HRR) find the offsets of the ECH (if any)
- * sh is the SH buffer
- * sh_len is the length of the SH
- * exts points to offset of extensions
- * echoffset points to offset of ECH
- * echtype points to the ext type of the ECH
- * return 1 for success, zero otherwise
- *
- * Offsets are returned to the type or length field in question.
- * Offsets are set to zero if relevant thing not found.
- *
- * Note: input here is untrusted!
- */
-int ossl_ech_get_sh_offsets(const unsigned char *sh, size_t sh_len,
- size_t *exts, size_t *echoffset,
- uint16_t *echtype)
-{
- unsigned int etype = 0, pi_tmp = 0;
- const unsigned char *pp_tmp = NULL, *shstart = NULL;
- PACKET pkt, session_id, extpkt, oneext;
- size_t extlens = 0;
- int done = 0;
-#ifdef OSSL_ECH_SUPERVERBOSE
- size_t echlen = 0; /* length of ECH, including type & ECH-internal length */
- size_t sessid_offset = 0, sessid_len = 0;
-#endif
-
- if (sh == NULL || sh_len == 0 || exts == NULL || echoffset == NULL
- || echtype == NULL)
- return 0;
- *exts = *echoffset = *echtype = 0;
- if (!PACKET_buf_init(&pkt, sh, sh_len))
- return 0;
- shstart = PACKET_data(&pkt);
- if (!PACKET_get_net_2(&pkt, &pi_tmp))
- return 0;
- /*
- * TODO(ECH): we've had a TLSv1.2 test in the past where we add an
- * ECH to a TLSv1.2 CH to ensure server code ignores that properly.
- * We might or might not keep that, if we don't then the test below
- * should allow TLSv1.3 only.
- */
- /* if we're not TLSv1.2+ then we can bail, but it's not an error */
- if (pi_tmp != TLS1_2_VERSION && pi_tmp != TLS1_3_VERSION)
- return 1;
- if (!PACKET_get_bytes(&pkt, &pp_tmp, SSL3_RANDOM_SIZE)
-#ifdef OSSL_ECH_SUPERVERBOSE
- || (sessid_offset = PACKET_data(&pkt) - shstart) == 0
-#endif
- || !PACKET_get_length_prefixed_1(&pkt, &session_id)
-#ifdef OSSL_ECH_SUPERVERBOSE
- || (sessid_len = PACKET_remaining(&session_id)) == 0
-#endif
- || !PACKET_get_net_2(&pkt, &pi_tmp) /* ciphersuite */
- || !PACKET_get_1(&pkt, &pi_tmp) /* compression */
- || (*exts = PACKET_data(&pkt) - shstart) == 0
- || !PACKET_as_length_prefixed_2(&pkt, &extpkt)
- || PACKET_remaining(&pkt) != 0)
- return 0;
- extlens = PACKET_remaining(&extpkt);
- if (extlens == 0) /* not an error, in theory */
- return 1;
- while (PACKET_remaining(&extpkt) > 0 && done < 1) {
- if (!PACKET_get_net_2(&extpkt, &etype)
- || !PACKET_get_length_prefixed_2(&extpkt, &oneext))
- return 0;
- if (etype == TLSEXT_TYPE_ech) {
- if (PACKET_remaining(&oneext) != 8)
- return 0;
- *echoffset = PACKET_data(&oneext) - shstart - 4;
- *echtype = etype;
-#ifdef OSSL_ECH_SUPERVERBOSE
- echlen = PACKET_remaining(&oneext) + 4; /* type/length included */
-#endif
- done++;
- }
- }
-#ifdef OSSL_ECH_SUPERVERBOSE
- OSSL_TRACE_BEGIN(TLS) {
- BIO_printf(trc_out, "orig SH/ECH type: %4x\n", *echtype);
- } OSSL_TRACE_END(TLS);
- ossl_ech_pbuf("orig SH", (unsigned char *)sh, sh_len);
- ossl_ech_pbuf("orig SH session_id", (unsigned char *)sh + sessid_offset,
- sessid_len);
- ossl_ech_pbuf("orig SH exts", (unsigned char *)sh + *exts, extlens);
- ossl_ech_pbuf("orig SH/ECH ", (unsigned char *)sh + *echoffset, echlen);
-#endif
- return 1;
-}
-
-/*
- * make up HPKE "info" input as per spec
+ * Construct HPKE "info" input as per spec
* encoding is the ECHconfig being used
- * encodinglen is the length of ECHconfig being used
+ * encoding_length is the length of ECHconfig being used
* info is a caller-allocated buffer for results
* info_len is the buffer size on input, used-length on output
* return 1 for success, zero otherwise
*/
-int ossl_ech_make_enc_info(unsigned char *encoding, size_t encoding_length,
+int ossl_ech_make_enc_info(const unsigned char *encoding,
+ size_t encoding_length,
unsigned char *info, size_t *info_len)
{
WPACKET ipkt = { 0 };
- BUF_MEM *ipkt_mem = NULL;
if (encoding == NULL || info == NULL || info_len == NULL)
return 0;
- if (*info_len < (sizeof(OSSL_ECH_CONTEXT_STRING) + encoding_length))
- return 0;
- if ((ipkt_mem = BUF_MEM_new()) == NULL
- || !WPACKET_init(&ipkt, ipkt_mem)
+ if (!WPACKET_init_static_len(&ipkt, info, *info_len, 0)
|| !WPACKET_memcpy(&ipkt, OSSL_ECH_CONTEXT_STRING,
sizeof(OSSL_ECH_CONTEXT_STRING) - 1)
/*
* the context string being NUL terminated
*/
|| !WPACKET_put_bytes_u8(&ipkt, 0)
- || !WPACKET_memcpy(&ipkt, encoding, encoding_length)) {
+ || !WPACKET_memcpy(&ipkt, encoding, encoding_length)
+ || !WPACKET_get_total_written(&ipkt, info_len)) {
WPACKET_cleanup(&ipkt);
- BUF_MEM_free(ipkt_mem);
return 0;
}
- *info_len = sizeof(OSSL_ECH_CONTEXT_STRING) + encoding_length;
- memcpy(info, WPACKET_get_curr(&ipkt) - *info_len, *info_len);
WPACKET_cleanup(&ipkt);
- BUF_MEM_free(ipkt_mem);
return 1;
}
#include "../ssl_local.h"
#include "ech_local.h"
#include <openssl/rand.h>
-#include <internal/ech_helpers.h>
+#include "../statem/statem_local.h"
+#include "internal/ech_helpers.h"
#include <openssl/kdf.h>
#ifndef OPENSSL_NO_ECH
}
/* trace out transcript */
-void ossl_ech_ptranscript(SSL_CONNECTION *s, const char *msg)
+static void ossl_ech_ptranscript(SSL_CONNECTION *s, const char *msg)
{
- OSSL_TRACE_BEGIN(TLS) {
- size_t hdatalen = 0;
- unsigned char *hdata = NULL;
- unsigned char ddata[EVP_MAX_MD_SIZE];
- size_t ddatalen;
+ size_t hdatalen = 0;
+ unsigned char *hdata = NULL;
+ unsigned char ddata[EVP_MAX_MD_SIZE];
+ size_t ddatalen;
- if (s == NULL)
- return;
- hdatalen = BIO_get_mem_data(s->s3.handshake_buffer, &hdata);
- ossl_ech_pbuf(msg, hdata, hdatalen);
- if (s->s3.handshake_dgst != NULL) {
- if (ssl_handshake_hash(s, ddata, sizeof(ddata), &ddatalen) == 0)
+ if (s == NULL)
+ return;
+ hdatalen = BIO_get_mem_data(s->s3.handshake_buffer, &hdata);
+ ossl_ech_pbuf(msg, hdata, hdatalen);
+ if (s->s3.handshake_dgst != NULL) {
+ if (ssl_handshake_hash(s, ddata, sizeof(ddata), &ddatalen) == 0) {
+ OSSL_TRACE_BEGIN(TLS) {
BIO_printf(trc_out, "ssl_handshake_hash failed\n");
+ } OSSL_TRACE_END(TLS);
ossl_ech_pbuf(msg, ddata, ddatalen);
- } else {
- BIO_printf(trc_out, "handshake_dgst is NULL\n");
}
+ }
+ OSSL_TRACE_BEGIN(TLS) {
+ BIO_printf(trc_out, "new transbuf:\n");
} OSSL_TRACE_END(TLS);
+ ossl_ech_pbuf(msg, s->ext.ech.transbuf, s->ext.ech.transbuf_len);
return;
}
# endif
OPENSSL_free(ec->outer_hostname);
OPENSSL_free(ec->alpn_outer);
OPENSSL_free(ec->former_inner);
+ OPENSSL_free(ec->transbuf);
OPENSSL_free(ec->innerch);
- OPENSSL_free(ec->encoded_innerch);
- OPENSSL_free(ec->innerch1);
- OPENSSL_free(ec->kepthrr);
OPENSSL_free(ec->grease_suite);
OPENSSL_free(ec->sent);
OPENSSL_free(ec->returned);
OPENSSL_free(ec->pub);
OSSL_HPKE_CTX_free(ec->hpke_ctx);
EVP_PKEY_free(ec->tmp_pkey);
+ OPENSSL_free(ec->encoded_inner);
return;
}
OSSL_ECHSTORE *es = NULL;
OSSL_ECHSTORE_ENTRY *ee = NULL;
int i, num = 0;
- size_t retslen = 0, encilen = 0;
- unsigned char *tmp = NULL, *enci = NULL, *rets = NULL;
+ size_t retslen = 0;
+ unsigned char *tmp = NULL, *rets = NULL;
if (s == NULL || rcfgs == NULL || rcfgslen == NULL)
return 0;
for (i = 0; i != num; i++) {
ee = sk_OSSL_ECHSTORE_ENTRY_value(es->entries, i);
if (ee != NULL && ee->for_retry == OSSL_ECH_FOR_RETRY) {
- encilen = ee->encoded_len;
- if (encilen < 2)
- goto err;
- encilen -= 2;
- enci = ee->encoded + 2;
- tmp = (unsigned char *)OPENSSL_realloc(rets, retslen + encilen);
+ tmp = (unsigned char *)OPENSSL_realloc(rets,
+ retslen + ee->encoded_len);
if (tmp == NULL)
goto err;
rets = tmp;
- memcpy(rets + retslen, enci, encilen);
- retslen += encilen;
+ memcpy(rets + retslen, ee->encoded, ee->encoded_len);
+ retslen += ee->encoded_len;
}
}
*rcfgs = rets;
size_t cipher_len = 0, cipher_len_jitter = 0;
unsigned char cid, senderpub[OSSL_ECH_MAX_GREASE_PUB];
unsigned char cipher[OSSL_ECH_MAX_GREASE_CT];
- unsigned char *pp = WPACKET_get_curr(pkt);
if (s == NULL)
return 0;
}
WPACKET_get_total_written(pkt, &pp_at_start);
/* randomly select cipher_len to be one of 144, 176, 208, 244 */
- if (RAND_bytes_ex(s->ssl.ctx->libctx, &cid, 1,
- RAND_DRBG_STRENGTH) <= 0) {
+ if (RAND_bytes_ex(s->ssl.ctx->libctx, &cid, 1, 0) <= 0) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
cipher_len = 144;
cipher_len += 32 * cipher_len_jitter;
/* generate a random (1 octet) client id */
- if (RAND_bytes_ex(s->ssl.ctx->libctx, &cid, 1,
- RAND_DRBG_STRENGTH) <= 0) {
+ if (RAND_bytes_ex(s->ssl.ctx->libctx, &cid, 1, 0) <= 0) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
- memcpy(s->ext.ech.sent, pp, s->ext.ech.sent_len);
+ memcpy(s->ext.ech.sent, WPACKET_get_curr(pkt) - s->ext.ech.sent_len,
+ s->ext.ech.sent_len);
s->ext.ech.grease = OSSL_ECH_IS_GREASE;
OSSL_TRACE_BEGIN(TLS) {
BIO_printf(trc_out, "ECH - sending GREASE\n");
if (s == NULL || s->ext.ech.es == NULL || ee == NULL || suite == NULL)
return 0;
+ *ee = NULL;
es = s->ext.ech.es;
if (es->entries == NULL)
return 0;
hnlen = 0;
nameoverride = 1;
}
- for (cind = 0; cind != num && (suitematch == 0 || namematch == 0); cind++) {
+ for (cind = 0; cind < num && (suitematch == 0 || namematch == 0); cind++) {
lee = sk_OSSL_ECHSTORE_ENTRY_value(es->entries, cind);
if (lee == NULL || lee->version != OSSL_ECH_RFCXXXX_VERSION)
continue;
if (hnlen == 0
|| (lee->public_name != NULL
&& strlen(lee->public_name) == hnlen
- && !OPENSSL_strncasecmp(hn, (char *)lee->public_name,
- hnlen)))
+ && OPENSSL_strncasecmp(hn, (char *)lee->public_name,
+ hnlen) == 0))
namematch = 1;
}
suitematch = 0;
}
}
}
- if (nameoverride == 1 && (namematch == 0 || suitematch == 0)) {
+ if (tee != NULL && nameoverride == 1
+ && (namematch == 0 || suitematch == 0)) {
*suite = tee->suites[tsuite];
*ee = tee;
} else if (namematch == 0 || suitematch == 0) {
}
/* Make up the ClientHelloInner and EncodedClientHelloInner buffers */
-int ossl_ech_encode_inner(SSL_CONNECTION *s)
+int ossl_ech_encode_inner(SSL_CONNECTION *s, unsigned char **encoded,
+ size_t *encoded_len)
{
int rv = 0;
size_t nraws = 0, ind = 0, innerlen = 0;
- unsigned char *innerch_full = NULL;
WPACKET inner = { 0 }; /* "fake" pkt for inner */
BUF_MEM *inner_mem = NULL;
RAW_EXTENSION *raws = NULL;
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
goto err;
}
- innerch_full = OPENSSL_malloc(innerlen);
- if (innerch_full == NULL) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
- goto err;
- }
- memcpy(innerch_full, inner_mem->data, innerlen);
- OPENSSL_free(s->ext.ech.encoded_innerch);
- s->ext.ech.encoded_innerch = innerch_full;
- s->ext.ech.encoded_innerch_len = innerlen;
+ *encoded = (unsigned char *)inner_mem->data;
+ inner_mem->data = NULL; /* keep BUF_MEM_free happy */
+ *encoded_len = innerlen;
/* and clean up */
rv = 1;
err:
* return: 1 for success, 0 otherwise
*/
int ossl_ech_find_confirm(SSL_CONNECTION *s, int hrr,
- unsigned char acbuf[OSSL_ECH_SIGNAL_LEN],
- const unsigned char *shbuf, const size_t shlen)
+ unsigned char acbuf[OSSL_ECH_SIGNAL_LEN])
{
- PACKET pkt;
- const unsigned char *acp = NULL, *pp_tmp;
- unsigned int pi_tmp, etype, elen;
- int done = 0;
+ unsigned char *acp = NULL;
if (hrr == 0) {
acp = s->s3.server_random + SSL3_RANDOM_SIZE - OSSL_ECH_SIGNAL_LEN;
- memcpy(acbuf, acp, OSSL_ECH_SIGNAL_LEN);
- return 1;
- } else {
- if (!PACKET_buf_init(&pkt, shbuf, shlen)
- || !PACKET_get_net_2(&pkt, &pi_tmp)
- || !PACKET_get_bytes(&pkt, &pp_tmp, SSL3_RANDOM_SIZE)
- || !PACKET_get_1(&pkt, &pi_tmp) /* sessid len */
- || !PACKET_get_bytes(&pkt, &pp_tmp, pi_tmp) /* sessid */
- || !PACKET_get_net_2(&pkt, &pi_tmp) /* ciphersuite */
- || !PACKET_get_1(&pkt, &pi_tmp) /* compression */
- || !PACKET_get_net_2(&pkt, &pi_tmp)) /* len(extensions) */
+ } else { /* was set in extension handler */
+ if (s->ext.ech.hrrsignal_p == NULL)
return 0;
- while (PACKET_remaining(&pkt) > 0 && done < 1) {
- if (!PACKET_get_net_2(&pkt, &etype)
- || !PACKET_get_net_2(&pkt, &elen))
- return 0;
- if (etype == TLSEXT_TYPE_ech) {
- if (elen != OSSL_ECH_SIGNAL_LEN
- || !PACKET_get_bytes(&pkt, &acp, elen))
- return 0;
- memcpy(acbuf, acp, elen);
- done++;
- } else {
- if (!PACKET_get_bytes(&pkt, &pp_tmp, elen))
- return 0;
- }
- }
- return done;
- }
- return 0;
-}
-
-/*
- * make up a buffer to use to reset transcript
- * for_hrr is 1 if we've just seen HRR, 0 otherwise
- * shbuf is the output buffer
- * shlen is the length of that buffer
- * tbuf is the output buffer
- * tlen is the length of that buffer
- * chend returns the offset of the end of the last CH in the buffer
- * fixedshbuf_len returns the fixed up length of the SH
- * return 1 for good, 0 otherwise
- */
-int ossl_ech_make_transcript_buffer(SSL_CONNECTION *s, int for_hrr,
- const unsigned char *shbuf, size_t shlen,
- unsigned char **tbuf, size_t *tlen,
- size_t *chend, size_t *fixedshbuf_len)
-{
- unsigned char *fixedshbuf = NULL, *hashin = NULL, hashval[EVP_MAX_MD_SIZE];
- unsigned int hashlen = 0, hashin_len = 0;
- EVP_MD_CTX *ctx = NULL;
- EVP_MD *md = NULL;
- WPACKET tpkt = { 0 }, shpkt = { 0 };
- BUF_MEM *tpkt_mem = NULL, *shpkt_mem = NULL;
-
- /*
- * SH preamble has bad length at this point on server
- * and is missing on client so we'll fix
- */
- if ((shpkt_mem = BUF_MEM_new()) == NULL
- || !WPACKET_init(&shpkt, shpkt_mem)) {
- BUF_MEM_free(shpkt_mem);
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
- goto err;
- }
- if (!WPACKET_put_bytes_u8(&shpkt, SSL3_MT_SERVER_HELLO)
- || (s->server == 1 && !WPACKET_put_bytes_u24(&shpkt, shlen - 4))
- || (s->server == 1 && !WPACKET_memcpy(&shpkt, shbuf + 4, shlen -4))
- || (s->server == 0 && !WPACKET_put_bytes_u24(&shpkt, shlen))
- || (s->server == 0 && !WPACKET_memcpy(&shpkt, shbuf, shlen))
- || !WPACKET_get_length(&shpkt, fixedshbuf_len)) {
- BUF_MEM_free(shpkt_mem);
- WPACKET_cleanup(&shpkt);
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
- goto err;
- }
- fixedshbuf = (unsigned char *)shpkt_mem->data;
- WPACKET_cleanup(&shpkt);
-# ifdef OSSL_ECH_SUPERVERBOSE
- ossl_ech_pbuf("cx: fixed sh buf", fixedshbuf, *fixedshbuf_len);
-# endif
- if ((tpkt_mem = BUF_MEM_new()) == NULL
- || !WPACKET_init(&tpkt, tpkt_mem)) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
- goto err;
- }
- if (s->hello_retry_request == SSL_HRR_NONE) {
- if (!WPACKET_memcpy(&tpkt, s->ext.ech.innerch,
- s->ext.ech.innerch_len)
- || !WPACKET_get_length(&tpkt, chend)
- || !WPACKET_memcpy(&tpkt, fixedshbuf, *fixedshbuf_len)
- || !WPACKET_get_length(&tpkt, tlen)) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
- goto err;
- }
- *tbuf = OPENSSL_malloc(*tlen);
- if (*tbuf == NULL) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
- goto err;
- }
- memcpy(*tbuf, WPACKET_get_curr(&tpkt) - *tlen, *tlen);
- BUF_MEM_free(shpkt_mem);
- WPACKET_cleanup(&tpkt);
- BUF_MEM_free(tpkt_mem);
- return 1;
- }
- /* everything below only applies if we're at some stage in doing HRR */
- if ((md = (EVP_MD *)ssl_handshake_md(s)) == NULL) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
- goto err;
- }
- if (for_hrr == 0) { /* after 2nd SH rx'd */
- hashin = s->ext.ech.innerch1;
- hashin_len = s->ext.ech.innerch1_len;
- } else { /* after HRR rx'd */
- hashin = s->ext.ech.innerch;
- hashin_len = s->ext.ech.innerch_len;
- OPENSSL_free(s->ext.ech.kepthrr);
- /* stash this SH/HRR for later */
- s->ext.ech.kepthrr = OPENSSL_malloc(*fixedshbuf_len);
- if (s->ext.ech.kepthrr == NULL) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
- goto err;
- }
- memcpy(s->ext.ech.kepthrr, fixedshbuf, *fixedshbuf_len);
- s->ext.ech.kepthrr_len = *fixedshbuf_len;
- }
-# ifdef OSSL_ECH_SUPERVERBOSE
- ossl_ech_pbuf("cx: ch2hash", hashin, hashin_len);
-# endif
- if ((ctx = EVP_MD_CTX_new()) == NULL
- || EVP_DigestInit_ex(ctx, md, NULL) <= 0
- || EVP_DigestUpdate(ctx, hashin, hashin_len) <= 0
- || EVP_DigestFinal_ex(ctx, hashval, &hashlen) <= 0) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
- goto err;
- }
- EVP_MD_CTX_free(ctx);
- ctx = NULL;
- if (!WPACKET_put_bytes_u8(&tpkt, SSL3_MT_MESSAGE_HASH)
- || !WPACKET_put_bytes_u24(&tpkt, hashlen)
- || !WPACKET_memcpy(&tpkt, hashval, hashlen)) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
- goto err;
- }
- if (for_hrr == 0) { /* after 2nd SH */
- if (!WPACKET_memcpy(&tpkt, s->ext.ech.kepthrr,
- s->ext.ech.kepthrr_len)
- || !WPACKET_memcpy(&tpkt, s->ext.ech.innerch,
- s->ext.ech.innerch_len)) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
- goto err;
- }
- }
- if (!WPACKET_get_length(&tpkt, chend)
- || !WPACKET_memcpy(&tpkt, fixedshbuf, *fixedshbuf_len)
- || !WPACKET_get_length(&tpkt, tlen)) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
- goto err;
+ acp = s->ext.ech.hrrsignal;
}
- *tbuf = OPENSSL_malloc(*tlen);
- if (*tbuf == NULL) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
- goto err;
- }
- memcpy(*tbuf, WPACKET_get_curr(&tpkt) - *tlen, *tlen);
- BUF_MEM_free(shpkt_mem);
- WPACKET_cleanup(&tpkt);
- BUF_MEM_free(tpkt_mem);
+ memcpy(acbuf, acp, OSSL_ECH_SIGNAL_LEN);
return 1;
-err:
- BUF_MEM_free(shpkt_mem);
- BUF_MEM_free(tpkt_mem);
- WPACKET_cleanup(&tpkt);
- EVP_MD_CTX_free(ctx);
- return 0;
}
/*
ossl_ech_pbuf("RESET transcript to", buf, blen);
# endif
if (s->s3.handshake_buffer != NULL) {
+ if (BIO_reset(s->s3.handshake_buffer) < 0)
+ return 0;
+ } else {
+ s->s3.handshake_buffer = BIO_new(BIO_s_mem());
+ if (s->s3.handshake_buffer == NULL)
+ return 0;
(void)BIO_set_close(s->s3.handshake_buffer, BIO_CLOSE);
- BIO_free(s->s3.handshake_buffer);
- s->s3.handshake_buffer = NULL;
}
EVP_MD_CTX_free(s->s3.handshake_dgst);
s->s3.handshake_dgst = NULL;
- s->s3.handshake_buffer = BIO_new(BIO_s_mem());
- if (s->s3.handshake_buffer == NULL)
- return 0;
/* providing nothing at all is a real use (mid-HRR) */
if (buf != NULL && blen > 0)
BIO_write(s->s3.handshake_buffer, (void *)buf, (int)blen);
* better really, BUT... we might want to keep this if others (e.g.
* browsers) do it so as to not stand out compared to them.
*
- * The "+ 9" constant below is from the specifiation and is the
+ * The "+ 9" constant below is from the specification and is the
* expansion comparing a string length to an encoded SNI extension.
* Same is true of the 31/32 formula below.
*
* a padded cleartext of 128 octets, the ciphertext will be 144
* octets.
*/
-static size_t ech_calc_padding(SSL_CONNECTION *s, OSSL_ECHSTORE_ENTRY *ee)
+size_t ossl_ech_calc_padding(SSL_CONNECTION *s, OSSL_ECHSTORE_ENTRY *ee,
+ size_t encoded_len)
{
int length_of_padding = 0, length_with_snipadding = 0;
int innersnipadding = 0, length_with_padding = 0;
- size_t mnl = 0, clear_len = 0, isnilen = 0;
+ size_t mnl = 0, isnilen = 0;
if (s == NULL || ee == NULL)
return 0;
}
}
/* padding is after the inner client hello has been encoded */
- length_with_snipadding = innersnipadding + s->ext.ech.encoded_innerch_len;
+ length_with_snipadding = innersnipadding + encoded_len;
length_of_padding = 31 - ((length_with_snipadding - 1) % 32);
- length_with_padding = s->ext.ech.encoded_innerch_len
- + length_of_padding + innersnipadding;
+ length_with_padding = encoded_len + length_of_padding + innersnipadding;
/*
* Finally - make sure final result is longer than padding target
* and a multiple of our padding increment.
* it makes us stick out; or if we take out the above more (uselessly:-)
* complicated scheme, we may only need this in the end.
*/
- if (length_with_padding % OSSL_ECH_PADDING_INCREMENT)
+ if ((length_with_padding % OSSL_ECH_PADDING_INCREMENT) != 0)
length_with_padding += OSSL_ECH_PADDING_INCREMENT
- (length_with_padding % OSSL_ECH_PADDING_INCREMENT);
while (length_with_padding < OSSL_ECH_PADDING_TARGET)
length_with_padding += OSSL_ECH_PADDING_INCREMENT;
- clear_len = length_with_padding;
OSSL_TRACE_BEGIN(TLS) {
BIO_printf(trc_out, "EAAE: padding: mnl: %zu, lws: %d "
- "lop: %d, lwp: %d, clear_len: %zu, orig: %zu\n",
+ "lop: %d, clear_len (len with padding): %d, orig: %zu\n",
mnl, length_with_snipadding, length_of_padding,
- length_with_padding, clear_len,
- s->ext.ech.encoded_innerch_len);
+ length_with_padding, encoded_len);
} OSSL_TRACE_END(TLS);
- return clear_len;
+ return (size_t)length_with_padding;
}
/*
*/
int ossl_ech_aad_and_encrypt(SSL_CONNECTION *s, WPACKET *pkt)
{
- int rv = 0, hpke_mode = OSSL_HPKE_MODE_BASE;
- OSSL_ECHSTORE_ENTRY *ee = NULL;
- OSSL_HPKE_SUITE hpke_suite = OSSL_HPKE_SUITE_DEFAULT;
- unsigned char config_id_to_use = 0x00, info[SSL3_RT_MAX_PLAIN_LENGTH];
- unsigned char *clear = NULL, *cipher = NULL, *aad = NULL, *mypub = NULL;
- size_t cipherlen = 0, aad_len = 0, lenclen = 0, mypub_len = 0;
- size_t info_len = SSL3_RT_MAX_PLAIN_LENGTH, clear_len = 0;
+ int rv = 0;
+ size_t cipherlen = 0, aad_len = 0, mypub_len = 0, clear_len = 0;
+ size_t encoded_inner_len = 0;
+ unsigned char *clear = NULL, *aad = NULL, *mypub = NULL;
+ unsigned char *encoded_inner = NULL, *cipher_loc = NULL;
if (s == NULL)
return 0;
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
goto err;
}
- rv = ossl_ech_pick_matching_cfg(s, &ee, &hpke_suite);
- if (rv != 1 || ee == NULL) {
+ /* values calculated in tls_construct_ctos_ech */
+ encoded_inner = s->ext.ech.encoded_inner;
+ encoded_inner_len = s->ext.ech.encoded_inner_len;
+ clear_len = s->ext.ech.clearlen;
+ cipherlen = s->ext.ech.cipherlen;
+ if (!WPACKET_get_total_written(pkt, &aad_len) || aad_len < 4) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
goto err;
}
- s->ext.ech.attempted_type = ee->version;
- OSSL_TRACE_BEGIN(TLS) {
- BIO_printf(trc_out, "EAAE: selected: version: %4x, config %2x\n",
- ee->version, ee->config_id);
- } OSSL_TRACE_END(TLS);
- config_id_to_use = ee->config_id; /* if requested, use a random config_id instead */
- if ((s->ssl.ctx->options & SSL_OP_ECH_IGNORE_CID) != 0
- || (s->options & SSL_OP_ECH_IGNORE_CID) != 0) {
- if (RAND_bytes_ex(s->ssl.ctx->libctx, &config_id_to_use, 1,
- RAND_DRBG_STRENGTH) <= 0) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
- return 0;
- }
-# ifdef OSSL_ECH_SUPERVERBOSE
- ossl_ech_pbuf("EAAE: random config_id", &config_id_to_use, 1);
-# endif
- }
- s->ext.ech.attempted_cid = config_id_to_use;
-# ifdef OSSL_ECH_SUPERVERBOSE
- ossl_ech_pbuf("EAAE: peer pub", ee->pub, ee->pub_len);
- ossl_ech_pbuf("EAAE: clear", s->ext.ech.encoded_innerch,
- s->ext.ech.encoded_innerch_len);
- ossl_ech_pbuf("EAAE: ECHConfig", ee->encoded, ee->encoded_len);
-# endif
- /*
- * The AAD is the full outer client hello but with the correct number of
- * zeros for where the ECH ciphertext octets will later be placed. So we
- * add the ECH extension to the |pkt| but with zeros for ciphertext, that
- * forms up the AAD, then after we've encrypted, we'll splice in the actual
- * ciphertext.
- * Watch out for the "4" offsets that remove the type and 3-octet length
- * from the encoded CH as per the spec.
- */
- clear_len = ech_calc_padding(s, ee);
- if (clear_len == 0) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
- goto err;
- }
- lenclen = OSSL_HPKE_get_public_encap_size(hpke_suite);
- if (s->ext.ech.hpke_ctx == NULL) { /* 1st CH */
- if (ossl_ech_make_enc_info(ee->encoded, ee->encoded_len,
- info, &info_len) != 1) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
- goto err;
- }
-# ifdef OSSL_ECH_SUPERVERBOSE
- ossl_ech_pbuf("EAAE info", info, info_len);
-# endif
- s->ext.ech.hpke_ctx = OSSL_HPKE_CTX_new(hpke_mode, hpke_suite,
- OSSL_HPKE_ROLE_SENDER,
- NULL, NULL);
- if (s->ext.ech.hpke_ctx == NULL) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
- goto err;
- }
- mypub = OPENSSL_malloc(lenclen);
- if (mypub == NULL) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
- goto err;
- }
- mypub_len = lenclen;
- rv = OSSL_HPKE_encap(s->ext.ech.hpke_ctx, mypub, &mypub_len,
- ee->pub, ee->pub_len, info, info_len);
- if (rv != 1) {
- OPENSSL_free(mypub);
- mypub = NULL;
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
- goto err;
- }
- s->ext.ech.pub = mypub;
- s->ext.ech.pub_len = mypub_len;
- } else { /* HRR - retrieve public */
- mypub = s->ext.ech.pub;
- mypub_len = s->ext.ech.pub_len;
- if (mypub == NULL || mypub_len == 0) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
- goto err;
- }
- }
-# ifdef OSSL_ECH_SUPERVERBOSE
- ossl_ech_pbuf("EAAE: mypub", mypub, mypub_len);
- WPACKET_get_total_written(pkt, &aad_len); /* use aad_len for tracing */
- ossl_ech_pbuf("EAAE pkt b4", WPACKET_get_curr(pkt) - aad_len, aad_len);
-# endif
- cipherlen = OSSL_HPKE_get_ciphertext_size(hpke_suite, clear_len);
- if (cipherlen <= clear_len || cipherlen > OSSL_ECH_MAX_PAYLOAD_LEN) {
- SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION);
- goto err;
- }
- cipher = OPENSSL_zalloc(cipherlen);
- if (cipher == NULL) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
- goto err;
- }
- if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_ech)
- || !WPACKET_start_sub_packet_u16(pkt)
- || !WPACKET_put_bytes_u8(pkt, OSSL_ECH_OUTER_CH_TYPE)
- || !WPACKET_put_bytes_u16(pkt, hpke_suite.kdf_id)
- || !WPACKET_put_bytes_u16(pkt, hpke_suite.aead_id)
- || !WPACKET_put_bytes_u8(pkt, config_id_to_use)
- || (s->hello_retry_request == SSL_HRR_PENDING
- && !WPACKET_put_bytes_u16(pkt, 0x00)) /* no pub */
- || (s->hello_retry_request != SSL_HRR_PENDING
- && !WPACKET_sub_memcpy_u16(pkt, mypub, mypub_len))
- || !WPACKET_sub_memcpy_u16(pkt, cipher, cipherlen)
- || !WPACKET_close(pkt)
- || !WPACKET_get_total_written(pkt, &aad_len)
- || aad_len < 4) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
- goto err;
- }
- aad_len -= 4; /* aad starts after type + 3-octet len */
+ aad_len -= 4; /* ECH/HPKE aad starts after type + 3-octet len */
aad = WPACKET_get_curr(pkt) - aad_len;
+ /* where we'll replace zeros with ciphertext */
+ cipher_loc = aad + s->ext.ech.cipher_offset;
/*
* close the extensions of the CH - we skipped doing this
* earlier when encoding extensions, to allow for adding the
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
goto err;
}
- memcpy(clear, s->ext.ech.encoded_innerch, s->ext.ech.encoded_innerch_len);
+ memcpy(clear, encoded_inner, encoded_inner_len);
# ifdef OSSL_ECH_SUPERVERBOSE
ossl_ech_pbuf("EAAE: padded clear", clear, clear_len);
# endif
- rv = OSSL_HPKE_seal(s->ext.ech.hpke_ctx, cipher, &cipherlen,
- aad, aad_len, clear, clear_len);
+ /* we're done with this now */
+ OPENSSL_free(s->ext.ech.encoded_inner);
+ s->ext.ech.encoded_inner = NULL;
+ rv = OSSL_HPKE_seal(s->ext.ech.hpke_ctx, cipher_loc,
+ &cipherlen, aad, aad_len, clear, clear_len);
OPENSSL_free(clear);
if (rv != 1) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
goto err;
}
# ifdef OSSL_ECH_SUPERVERBOSE
- ossl_ech_pbuf("EAAE: cipher", cipher, cipherlen);
+ ossl_ech_pbuf("EAAE: cipher", cipher_loc, cipherlen);
ossl_ech_pbuf("EAAE: hpke mypub", mypub, mypub_len);
-# endif
- /* splice real ciphertext back in now */
- memcpy(aad + aad_len - cipherlen, cipher, cipherlen);
-# ifdef OSSL_ECH_SUPERVERBOSE
/* re-use aad_len for tracing */
WPACKET_get_total_written(pkt, &aad_len);
ossl_ech_pbuf("EAAE pkt aftr", WPACKET_get_curr(pkt) - aad_len, aad_len);
# endif
- OPENSSL_free(cipher);
return 1;
err:
- OPENSSL_free(cipher);
return 0;
}
* out is the BIO to use (e.g. stdout/whatever)
* selector OSSL_ECH_SELECT_ALL or just one of the SSL_ECH values
*/
-static void ech_status_print(BIO *out, SSL_CONNECTION *s, int selector)
+void ossl_ech_status_print(BIO *out, SSL_CONNECTION *s, int selector)
{
int num = 0, i, has_priv, for_retry;
size_t j;
return;
}
-/* size of string buffer returned via ECH callback */
-# define OSSL_ECH_PBUF_SIZE 8 * 1024
-
/*
* Swap the inner and outer after ECH success on the client
* return 0 for error, 1 for success
*/
int ossl_ech_swaperoo(SSL_CONNECTION *s)
{
- unsigned char *curr_buf = NULL, *new_buf = NULL;
- size_t curr_buflen = 0, new_buflen = 0, outer_chlen = 0, other_octets = 0;
+ unsigned char *curr_buf = NULL;
+ size_t curr_buflen = 0;
if (s == NULL)
return 0;
s->s3.group_id = s->ext.ech.group_id;
s->ext.ech.tmp_pkey = NULL;
/*
- * TODO(ECH): I suggest re-factoring transcript handling (which
- * is probably needed) after/with the PR that includes the server-
- * side ECH code. That should be much easier as at that point the
- * full set of tests can be run, whereas for now, we're limited
- * to testing the client side really works via bodged s_client
- * scripts, so there'd be a bigger risk of breaking something
- * subtly if we try re-factor now.
- *
* When not doing HRR... fix up the transcript to reflect the inner CH.
* If there's a client hello at the start of the buffer, then that's
* the outer CH and we want to replace that with the inner. We need to
- * be careful that there could be a server hello following and can't
- * lose that.
+ * be careful that there could be early data or a server hello following
+ * and we can't lose that.
*
* For HRR... HRR processing code has already done the necessary.
*/
if (s->hello_retry_request == SSL_HRR_NONE) {
- curr_buflen = BIO_get_mem_data(s->s3.handshake_buffer,
- &curr_buf);
- if (curr_buflen > 4 && curr_buf[0] == SSL3_MT_CLIENT_HELLO) {
- outer_chlen = 1 + curr_buf[1] * 256 * 256
- + curr_buf[2] * 256 + curr_buf[3];
- if (outer_chlen > curr_buflen) {
+ BIO *handbuf = s->s3.handshake_buffer;
+ PACKET pkt, subpkt;
+ unsigned int mt;
+
+ s->s3.handshake_buffer = NULL;
+ if (ssl3_init_finished_mac(s) == 0) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ BIO_free(handbuf);
+ return 0;
+ }
+ if (ssl3_finish_mac(s, s->ext.ech.innerch, s->ext.ech.innerch_len) == 0) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ BIO_free(handbuf);
+ return 0;
+ }
+ curr_buflen = BIO_get_mem_data(handbuf, &curr_buf);
+ if (PACKET_buf_init(&pkt, curr_buf, curr_buflen)
+ && PACKET_get_1(&pkt, &mt)
+ && mt == SSL3_MT_CLIENT_HELLO
+ && PACKET_remaining(&pkt) >= 3) {
+ if (!PACKET_get_length_prefixed_3(&pkt, &subpkt)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ BIO_free(handbuf);
return 0;
}
- other_octets = curr_buflen - outer_chlen;
- if (other_octets > 0) {
- new_buflen = s->ext.ech.innerch_len + other_octets;
- new_buf = OPENSSL_malloc(new_buflen);
- if (new_buf == NULL) {
+ if (PACKET_remaining(&pkt) > 0) {
+ if (ssl3_finish_mac(s, PACKET_data(&pkt), PACKET_remaining(&pkt)) == 0) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ BIO_free(handbuf);
return 0;
}
- if (s->ext.ech.innerch != NULL) /* asan check added */
- memcpy(new_buf, s->ext.ech.innerch,
- s->ext.ech.innerch_len);
- memcpy(new_buf + s->ext.ech.innerch_len,
- &curr_buf[outer_chlen], other_octets);
- } else {
- new_buf = s->ext.ech.innerch;
- new_buflen = s->ext.ech.innerch_len;
}
- } else {
- new_buf = s->ext.ech.innerch;
- new_buflen = s->ext.ech.innerch_len;
+ BIO_free(handbuf);
}
- /*
- * And now reset the handshake transcript to our buffer
- * Note ssl3_finish_mac isn't that great a name - that one just
- * adds to the transcript but doesn't actually "finish" anything
- */
- if (ssl3_init_finished_mac(s) == 0) {
- OPENSSL_free(new_buf);
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
- return 0;
- }
- if (ssl3_finish_mac(s, new_buf, new_buflen) == 0) {
- OPENSSL_free(new_buf);
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
- return 0;
- }
- OPENSSL_free(new_buf);
}
# ifdef OSSL_ECH_SUPERVERBOSE
ossl_ech_ptranscript(s, "ech_swaperoo, after");
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
- ech_status_print(biom, s, OSSL_ECHSTORE_ALL);
+ ossl_ech_status_print(biom, s, OSSL_ECHSTORE_ALL);
BIO_read(biom, pstr, OSSL_ECH_PBUF_SIZE);
cbrv = s->ext.ech.cb(&s->ssl, pstr);
BIO_free(biom);
if (for_hrr == 1) {
label = OSSL_ECH_HRR_CONFIRM_STRING;
+ labellen = sizeof(OSSL_ECH_HRR_CONFIRM_STRING) - 1;
} else {
label = OSSL_ECH_ACCEPT_CONFIRM_STRING;
+ labellen = sizeof(OSSL_ECH_ACCEPT_CONFIRM_STRING) - 1;
}
- labellen = strlen(label);
# ifdef OSSL_ECH_SUPERVERBOSE
ossl_ech_pbuf("cc: label", (unsigned char *)label, labellen);
# endif
pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
if (pctx == NULL
|| EVP_PKEY_derive_init(pctx) != 1
- || EVP_PKEY_CTX_hkdf_mode(pctx,
- EVP_PKEY_HKDEF_MODE_EXTRACT_ONLY) != 1
|| EVP_PKEY_CTX_hkdf_mode(pctx,
EVP_PKEY_HKDEF_MODE_EXTRACT_ONLY) != 1
|| EVP_PKEY_CTX_set_hkdf_md(pctx, md) != 1) {
/*
* ECH accept_confirmation calculation
* for_hrr is 1 if this is for an HRR, otherwise for SH
- * ac is (a caller allocated) 8 octet buffer
- * shbuf is a pointer to the SH buffer (incl. the type+3-octet length)
- * shlen is the length of the SH buf
+ * acbuf is an 8 octet buffer for the confirmation value
+ * shlen is the server hello length
* return: 1 for success, 0 otherwise
*
* This is a magic value in the ServerHello.random lower 8 octets
*/
int ossl_ech_calc_confirm(SSL_CONNECTION *s, int for_hrr,
unsigned char acbuf[OSSL_ECH_SIGNAL_LEN],
- const unsigned char *shbuf, const size_t shlen)
+ const size_t shlen)
{
int rv = 0;
EVP_MD_CTX *ctx = NULL;
unsigned char *tbuf = NULL, *conf_loc = NULL;
unsigned char *fixedshbuf = NULL;
size_t fixedshbuf_len = 0, tlen = 0, chend = 0;
- size_t shoffset = 6 + 24, extoffset = 0, echoffset = 0;
- uint16_t echtype;
+ /* shoffset is: 4 + 2 + 32 - 8 */
+ size_t shoffset = SSL3_HM_HEADER_LENGTH + sizeof(uint16_t)
+ + SSL3_RANDOM_SIZE - OSSL_ECH_SIGNAL_LEN;
unsigned int hashlen = 0;
- unsigned char hashval[EVP_MAX_MD_SIZE], hoval[EVP_MAX_MD_SIZE];
+ unsigned char hashval[EVP_MAX_MD_SIZE];
- if ((md = (EVP_MD *)ssl_handshake_md(s)) == NULL)
- goto err;
- if (ossl_ech_make_transcript_buffer(s, for_hrr, shbuf, shlen, &tbuf, &tlen,
- &chend, &fixedshbuf_len) != 1)
- goto err; /* SSLfatal called already */
+ if ((md = (EVP_MD *)ssl_handshake_md(s)) == NULL) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_ECH_REQUIRED);
+ goto end;
+ }
+ if (ossl_ech_intbuf_fetch(s, &tbuf, &tlen) != 1) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_ECH_REQUIRED);
+ goto end;
+ }
+ chend = tlen - shlen - 4;
+ fixedshbuf_len = shlen + 4;
+ if (s->server) {
+ chend = tlen - shlen;
+ fixedshbuf_len = shlen;
+ }
# ifdef OSSL_ECH_SUPERVERBOSE
- ossl_ech_pbuf("cx: tbuf b4", tbuf, tlen);
+ ossl_ech_pbuf("cx: tbuf b4-b4", tbuf, tlen);
# endif
/* put zeros in correct place */
if (for_hrr == 0) { /* zap magic octets at fixed place for SH */
if (s->server == 1) { /* we get to say where we put ECH:-) */
conf_loc = tbuf + tlen - OSSL_ECH_SIGNAL_LEN;
} else {
- if (ossl_ech_get_sh_offsets(shbuf, shlen, &extoffset,
- &echoffset, &echtype) != 1) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_ECH_REQUIRED);
- goto err;
- }
- if (echoffset == 0 || extoffset == 0 || echtype == 0
- || tlen < (chend + 4 + echoffset + 4 + OSSL_ECH_SIGNAL_LEN)) {
+ if (s->ext.ech.hrrsignal_p == NULL) {
/* No ECH found so we'll exit, but set random output */
if (RAND_bytes_ex(s->ssl.ctx->libctx, acbuf,
- OSSL_ECH_SIGNAL_LEN,
- RAND_DRBG_STRENGTH) <= 0) {
+ OSSL_ECH_SIGNAL_LEN, 0) <= 0) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_ECH_REQUIRED);
- goto err;
+ goto end;
}
rv = 1;
- goto err;
+ goto end;
}
- conf_loc = tbuf + chend + 4 + echoffset + 4;
+ conf_loc = s->ext.ech.hrrsignal_p;
}
}
memset(conf_loc, 0, OSSL_ECH_SIGNAL_LEN);
|| EVP_DigestUpdate(ctx, tbuf, tlen) <= 0
|| EVP_DigestFinal_ex(ctx, hashval, &hashlen) <= 0) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
- goto err;
+ goto end;
}
EVP_MD_CTX_free(ctx);
ctx = NULL;
# ifdef OSSL_ECH_SUPERVERBOSE
ossl_ech_pbuf("cx: hashval", hashval, hashlen);
# endif
- if (ech_hkdf_extract_wrap(s, md, for_hrr, hashval, hashlen, hoval) != 1) {
+ /* calculate and set the final output */
+ if (ech_hkdf_extract_wrap(s, md, for_hrr, hashval, hashlen, acbuf) != 1) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
- goto err;
+ goto end;
}
- memcpy(acbuf, hoval, OSSL_ECH_SIGNAL_LEN); /* Finally, set the output */
# ifdef OSSL_ECH_SUPERVERBOSE
ossl_ech_pbuf("cx: result", acbuf, OSSL_ECH_SIGNAL_LEN);
# endif
- /* put confirm value back into transcript vars */
- if (s->hello_retry_request != SSL_HRR_NONE && s->ext.ech.kepthrr != NULL
- && for_hrr == 1 && s->server == 1)
- memcpy(s->ext.ech.kepthrr + s->ext.ech.kepthrr_len
- - OSSL_ECH_SIGNAL_LEN, acbuf, OSSL_ECH_SIGNAL_LEN);
- memcpy(conf_loc, acbuf, OSSL_ECH_SIGNAL_LEN);
+ /* put confirm value back into transcript */
+ if (s->ext.ech.hrrsignal_p == NULL)
+ memcpy(conf_loc, acbuf, OSSL_ECH_SIGNAL_LEN);
+ else
+ memcpy(conf_loc, s->ext.ech.hrrsignal, OSSL_ECH_SIGNAL_LEN);
/* on a server, we need to reset the hs buffer now */
if (s->server && s->hello_retry_request == SSL_HRR_NONE)
ossl_ech_reset_hs_buffer(s, s->ext.ech.innerch, s->ext.ech.innerch_len);
if (s->server && s->hello_retry_request == SSL_HRR_COMPLETE)
ossl_ech_reset_hs_buffer(s, tbuf, tlen - fixedshbuf_len);
rv = 1;
-err:
+end:
OPENSSL_free(fixedshbuf);
- OPENSSL_free(tbuf);
EVP_MD_CTX_free(ctx);
return rv;
}
+
+int ossl_ech_intbuf_add(SSL_CONNECTION *s, const unsigned char *buf,
+ size_t blen, int hash_existing)
+{
+ EVP_MD_CTX *ctx = NULL;
+ EVP_MD *md = NULL;
+ unsigned int rv = 0, hashlen = 0;
+ unsigned char hashval[EVP_MAX_MD_SIZE], *t1;
+ size_t tlen;
+ WPACKET tpkt = { 0 };
+ BUF_MEM *tpkt_mem = NULL;
+
+ if (s == NULL || buf == NULL || blen == 0)
+ goto err;
+ if (hash_existing == 1) {
+ /* hash existing buffer, needed during HRR */
+ if (s->ext.ech.transbuf == NULL
+ || (md = (EVP_MD *)ssl_handshake_md(s)) == NULL
+ || (ctx = EVP_MD_CTX_new()) == NULL
+ || EVP_DigestInit_ex(ctx, md, NULL) <= 0
+ || EVP_DigestUpdate(ctx, s->ext.ech.transbuf,
+ s->ext.ech.transbuf_len) <= 0
+ || EVP_DigestFinal_ex(ctx, hashval, &hashlen) <= 0
+ || (tpkt_mem = BUF_MEM_new()) == NULL
+ || !WPACKET_init(&tpkt, tpkt_mem)
+ || !WPACKET_put_bytes_u8(&tpkt, SSL3_MT_MESSAGE_HASH)
+ || !WPACKET_put_bytes_u24(&tpkt, hashlen)
+ || !WPACKET_memcpy(&tpkt, hashval, hashlen)
+ || !WPACKET_get_length(&tpkt, &tlen)
+ || (t1 = OPENSSL_realloc(s->ext.ech.transbuf, tlen + blen)) == NULL)
+ goto err;
+ s->ext.ech.transbuf = t1;
+ memcpy(s->ext.ech.transbuf, tpkt_mem->data, tlen);
+ memcpy(s->ext.ech.transbuf + tlen, buf, blen);
+ s->ext.ech.transbuf_len = tlen + blen;
+ } else {
+ /* just add new octets */
+ if ((t1 = OPENSSL_realloc(s->ext.ech.transbuf,
+ s->ext.ech.transbuf_len + blen)) == NULL)
+ goto err;
+ s->ext.ech.transbuf = t1;
+ memcpy(s->ext.ech.transbuf + s->ext.ech.transbuf_len, buf, blen);
+ s->ext.ech.transbuf_len += blen;
+ }
+ rv = 1;
+err:
+ BUF_MEM_free(tpkt_mem);
+ WPACKET_cleanup(&tpkt);
+ EVP_MD_CTX_free(ctx);
+ return rv;
+}
+
+int ossl_ech_intbuf_fetch(SSL_CONNECTION *s, unsigned char **buf, size_t *blen)
+{
+ if (s == NULL || buf == NULL || blen == NULL || s->ext.ech.transbuf == NULL)
+ return 0;
+ *buf = s->ext.ech.transbuf;
+ *blen = s->ext.ech.transbuf_len;
+ return 1;
+}
#endif
# define OSSL_ECH_SIGNAL_LEN 8 /* length of ECH acceptance signal */
+/* size of string buffer returned via ECH callback */
+# define OSSL_ECH_PBUF_SIZE 8 * 1024
+
# ifndef CLIENT_VERSION_LEN
/*
* This is the legacy version length, i.e. len(0x0303). The same
* defined in a header file I could find.
*/
# define CLIENT_VERSION_LEN 2
+
# endif
/*
* the value we tried as the inner SNI for debug purposes
*/
char *former_inner;
- /*
- * TODO(ECH): The next 4 buffers (and lengths) may change if a
- * better way to handle the mutiple transcripts needed is
- * suggested/invented. I suggest re-factoring transcript handling
- * (which is probably needed) after/with the PR that includes the
- * server-side ECH code. That should be much easier as at that point
- * the full set of tests can be run, whereas for now, we're limited
- * to testing the client side really works via bodged s_client
- * scripts, so there'd be a bigger risk of breaking something
- * subtly if we try re-factor now.
- */
- /*
- * encoded inner ClientHello before/after ECH compression, which`
- * is nitty/complex (to avoid repeating the same extension value
- * in outer and inner, thus saving bandwidth) but (re-)calculating
- * the compression is a pain, so we'll store those as we make them
- */
- unsigned char *innerch; /* before compression */
+ /* inner CH transcript buffer */
+ unsigned char *transbuf;
+ size_t transbuf_len;
+ /* inner ClientHello before ECH compression */
+ unsigned char *innerch;
size_t innerch_len;
- unsigned char *encoded_innerch; /* after compression */
- size_t encoded_innerch_len;
- /*
- * in case of HRR, we need to record the 1st inner client hello, and
- * the first server hello (aka the HRR) so we can independently
- * generate the transcript and accept confirmation when making the
- * 2nd server hello
- */
- unsigned char *innerch1;
- size_t innerch1_len;
- unsigned char *kepthrr;
- size_t kepthrr_len;
+ /* encoded inner CH */
+ unsigned char *encoded_inner;
+ size_t encoded_inner_len;
+ /* lengths calculated early, used when encrypting at end of processing */
+ size_t clearlen;
+ size_t cipherlen;
+ /* location to put ciphertext, initially filled with zeros */
+ size_t cipher_offset;
/*
* Extensions are "outer-only" if the value is only sent in the
* outer CH and only the type is sent in the inner CH.
uint16_t attempted_type; /* ECH version used */
int attempted_cid; /* ECH config id sent/rx'd */
int backend; /* 1 if we're a server backend in split-mode, 0 otherwise */
+ /* When using a PSK stash the tick_identity from inner, for outer */
+ int tick_identity;
/*
* success is 1 if ECH succeeded, 0 otherwise, on the server this
* is known early, on the client we need to wait for the ECH confirm
unsigned char *pub; /* client ephemeral public kept by server in case HRR */
size_t pub_len;
OSSL_HPKE_CTX *hpke_ctx; /* HPKE context, needed for HRR */
+ /*
+ * A pointer to, and copy of, the hrrsignal from an HRR message.
+ * We need both, as we zero-out the octets when re-calculating and
+ * may need to put back what the server included so the transcript
+ * is correct when ECH acceptance failed.
+ */
+ unsigned char *hrrsignal_p;
+ unsigned char hrrsignal[OSSL_ECH_SIGNAL_LEN];
/*
* Fields that differ on client between inner and outer that we need to
* keep and swap over IFF ECH has succeeded. Same names chosen as are
# define OSSL_ECH_SAME_EXT_CONTINUE 2 /* generate a new value for outer CH */
/*
- * During extension construction (in extensions_clnt.c and surprisingly also in
+ * During extension construction (in extensions_clnt.c, and surprisingly also in
* extensions.c), we need to handle inner/outer CH cloning - ossl_ech_same_ext
* will (depending on compile time handling options) copy the value from
* CH.inner to CH.outer or else processing will continue, for a 2nd call,
# ifdef OSSL_ECH_SUPERVERBOSE
void ossl_ech_pbuf(const char *msg,
const unsigned char *buf, const size_t blen);
-void ossl_ech_ptranscript(SSL_CONNECTION *s, const char *msg);
# endif
int ossl_ech_get_retry_configs(SSL_CONNECTION *s, unsigned char **rcfgs,
size_t *rcfgslen);
int ossl_ech_send_grease(SSL_CONNECTION *s, WPACKET *pkt);
int ossl_ech_pick_matching_cfg(SSL_CONNECTION *s, OSSL_ECHSTORE_ENTRY **ee,
OSSL_HPKE_SUITE *suite);
-int ossl_ech_encode_inner(SSL_CONNECTION *s);
+int ossl_ech_encode_inner(SSL_CONNECTION *s, unsigned char **encoded,
+ size_t *encoded_len);
int ossl_ech_find_confirm(SSL_CONNECTION *s, int hrr,
- unsigned char acbuf[OSSL_ECH_SIGNAL_LEN],
- const unsigned char *shbuf, const size_t shlen);
-int ossl_ech_make_transcript_buffer(SSL_CONNECTION *s, int for_hrr,
- const unsigned char *shbuf, size_t shlen,
- unsigned char **tbuf, size_t *tlen,
- size_t *chend, size_t *fixedshbuf_len);
+ unsigned char acbuf[OSSL_ECH_SIGNAL_LEN]);
int ossl_ech_reset_hs_buffer(SSL_CONNECTION *s, const unsigned char *buf,
size_t blen);
int ossl_ech_aad_and_encrypt(SSL_CONNECTION *s, WPACKET *pkt);
int ossl_ech_swaperoo(SSL_CONNECTION *s);
int ossl_ech_calc_confirm(SSL_CONNECTION *s, int for_hrr,
unsigned char acbuf[OSSL_ECH_SIGNAL_LEN],
- const unsigned char *shbuf, const size_t shlen);
+ const size_t shlen);
/* these are internal but located in ssl/statem/extensions.c */
int ossl_ech_same_ext(SSL_CONNECTION *s, WPACKET *pkt);
int ossl_ech_same_key_share(void);
int ossl_ech_2bcompressed(int ind);
+int ossl_ech_copy_inner2outer(SSL_CONNECTION *s, uint16_t ext_type, int ind,
+ WPACKET *pkt);
+
+int ossl_ech_get_ch_offsets(SSL_CONNECTION *s, PACKET *pkt, size_t *sessid,
+ size_t *exts, size_t *echoffset, uint16_t *echtype,
+ int *inner, size_t *snioffset);
+int ossl_ech_early_decrypt(SSL_CONNECTION *s, PACKET *outerpkt, PACKET *newpkt);
+void ossl_ech_status_print(BIO *out, SSL_CONNECTION *s, int selector);
+
+int ossl_ech_intbuf_add(SSL_CONNECTION *s, const unsigned char *buf,
+ size_t blen, int hash_existing);
+int ossl_ech_intbuf_fetch(SSL_CONNECTION *s, unsigned char **buf, size_t *blen);
+size_t ossl_ech_calc_padding(SSL_CONNECTION *s, OSSL_ECHSTORE_ENTRY *ee,
+ size_t encoded_len);
# endif
#endif
return SSL_ECH_STATUS_GREASE_ECH;
return SSL_ECH_STATUS_GREASE;
}
- if (s->options & SSL_OP_ECH_GREASE)
+ if ((s->options & SSL_OP_ECH_GREASE) !=0 && s->ext.ech.attempted != 1)
return SSL_ECH_STATUS_GREASE;
if (s->ext.ech.backend == 1) {
if (s->ext.hostname != NULL
OSSL_ECHSTORE *ve = NULL;
BIO *in = NULL;
int rv = 0;
+ OSSL_LIB_CTX *libctx = NULL;
+ const char *propq = NULL;
s = SSL_CONNECTION_FROM_SSL(ssl);
if (s == NULL || ec == NULL || eclen == NULL)
* and letting the application see that might cause someone to do an
* upgrade.
*/
+ if (s->ext.ech.es != NULL) {
+ libctx = s->ext.ech.es->libctx;
+ propq = s->ext.ech.es->propq;
+ }
if ((in = BIO_new(BIO_s_mem())) == NULL
|| BIO_write(in, s->ext.ech.returned, s->ext.ech.returned_len) <= 0
- || (ve = OSSL_ECHSTORE_new(s->ext.ech.es->libctx,
- s->ext.ech.es->propq)) == NULL) {
+ || (ve = OSSL_ECHSTORE_new(libctx, propq)) == NULL) {
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
goto err;
}
goto err;
}
/* random config_id */
- if (RAND_bytes_ex(es->libctx, (unsigned char *)&config_id, 1,
- RAND_DRBG_STRENGTH) <= 0) {
+ if (RAND_bytes_ex(es->libctx, (unsigned char *)&config_id, 1, 0) <= 0) {
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
goto err;
}
ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_INVALID_ARGUMENT);
return 0;
}
- if (ee->keyshare != NULL && ee->loadtime + age >= now) {
+ if (ee->keyshare != NULL && ee->loadtime + age <= now) {
ossl_echstore_entry_free(ee);
sk_OSSL_ECHSTORE_ENTRY_delete(es->entries, i);
}
/*
* values for ext_defs ech_handling field
* exceptionally, we don't conditionally compile that field to avoid a pile of
- * fndefs all over the ext_defs values
+ * ifndefs all over the ext_defs values
*/
#define OSSL_ECH_HANDLING_CALL_BOTH 1 /* call constructor both times */
#define OSSL_ECH_HANDLING_COMPRESS 2 /* compress outer value into inner */
*/
unsigned int context;
/*
- * exceptionally, we don't conditionally compile this field to avoid a pile of
- * fndefs all over the ext_defs values
+ * exceptionally, we don't conditionally compile this field to avoid a
+ * pile of ifndefs all over the ext_defs values
*/
int ech_handling; /* how to handle ECH for this extension type */
/*
* inner CH must have been pre-decoded into s->clienthello->pre_proc_exts
* already.
*/
-static int ech_copy_inner2outer(SSL_CONNECTION *s, uint16_t ext_type,
- int ind, WPACKET *pkt)
+int ossl_ech_copy_inner2outer(SSL_CONNECTION *s, uint16_t ext_type,
+ int ind, WPACKET *pkt)
{
RAW_EXTENSION *myext = NULL, *raws = NULL;
# endif
if (s == NULL || s->ext.ech.es == NULL)
return OSSL_ECH_SAME_EXT_CONTINUE; /* nothing to do */
+ /* TODO(ECH): we need a better way to handle indexing exts */
tind = s->ext.ech.ext_ind;
/* If this index'd extension won't be compressed, we're done */
if (tind < 0 || tind >= nexts)
if (ext_defs[tind].ech_handling == OSSL_ECH_HANDLING_CALL_BOTH)
return OSSL_ECH_SAME_EXT_CONTINUE;
else
- return ech_copy_inner2outer(s, type, tind, pkt);
+ return ossl_ech_copy_inner2outer(s, type, tind, pkt);
}
/* just in case - shouldn't happen */
return OSSL_ECH_SAME_EXT_ERR;
#ifndef OPENSSL_NO_ECH
/*
- * Two passes - we first construct the to-be-ECH-compressed
- * extensions, and then go around again constructing those that
- * aren't to be ECH-compressed. We need to ensure this ordering
- * so that all the ECH-compressed extensions are contiguous
- * in the encoding. The actual compression happens later in
- * ech_encode_inner().
+ * Two passes if doing real ECH - we first construct the
+ * to-be-ECH-compressed extensions, and then go around again
+ * constructing those that aren't to be ECH-compressed. We
+ * need to ensure this ordering so that all the ECH-compressed
+ * extensions are contiguous in the encoding. The actual
+ * compression happens later in ech_encode_inner().
*/
for (pass = 0; pass <= 1; pass++)
#endif
int ret = -1;
int usepskfored = 0;
SSL_CTX *sctx = SSL_CONNECTION_GET_CTX(s);
-#ifndef OPENSSL_NO_ECH
- unsigned char hashval[EVP_MAX_MD_SIZE];
- unsigned int hashlen = 0;
- EVP_MD_CTX *ctx = NULL;
- WPACKET tpkt;
- BUF_MEM *tpkt_mem = NULL;
-#endif
/* Ensure cast to size_t is safe */
if (!ossl_assert(hashsizei > 0)) {
#ifndef OPENSSL_NO_ECH
/* handle the hashing as per ECH needs (on client) */
if (s->ext.ech.attempted == 1 && s->ext.ech.ch_depth == 1) {
- if ((tpkt_mem = BUF_MEM_new()) == NULL
- || !BUF_MEM_grow(tpkt_mem, SSL3_RT_MAX_PLAIN_LENGTH)
- || !WPACKET_init(&tpkt, tpkt_mem)) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
- goto err;
- }
- hashlen = EVP_MD_size(md);
- if ((ctx = EVP_MD_CTX_new()) == NULL
- || EVP_DigestInit_ex(ctx, md, NULL) <= 0
- || EVP_DigestUpdate(ctx, s->ext.ech.innerch1,
- s->ext.ech.innerch1_len) <= 0
- || EVP_DigestFinal_ex(ctx, hashval, &hashlen) <= 0) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
- goto err;
- }
- EVP_MD_CTX_free(ctx);
- ctx = NULL;
- if (!WPACKET_put_bytes_u8(&tpkt, SSL3_MT_MESSAGE_HASH)
- || !WPACKET_put_bytes_u24(&tpkt, hashlen)
- || !WPACKET_memcpy(&tpkt, hashval, hashlen)
- || !WPACKET_memcpy(&tpkt, s->ext.ech.kepthrr,
- s->ext.ech.kepthrr_len)
- || !WPACKET_get_length(&tpkt, &hdatalen)) {
+ if (ossl_ech_intbuf_fetch(s, (unsigned char **)&hdata, &hdatalen) != 1) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
goto err;
}
- hdata = WPACKET_get_curr(&tpkt) - hdatalen;
} else {
#endif
hdatalen = hdatalen_l =
OPENSSL_cleanse(finishedkey, sizeof(finishedkey));
EVP_PKEY_free(mackey);
EVP_MD_CTX_free(mctx);
-#ifndef OPENSSL_NO_ECH
- EVP_MD_CTX_free(ctx);
- if (tpkt_mem != NULL) {
- WPACKET_cleanup(&tpkt);
- BUF_MEM_free(tpkt_mem);
- }
-#endif
-
return ret;
}
#include "statem_local.h"
#ifndef OPENSSL_NO_ECH
# include <openssl/rand.h>
+#include "internal/ech_helpers.h"
+/*
+ * the max HPKE 'info' we'll process is the max ECHConfig size
+ * (OSSL_ECH_MAX_ECHCONFIG_LEN) plus OSSL_ECH_CONTEXT_STRING(len=7) + 1
+ */
+#define OSSL_ECH_MAX_INFO_LEN (OSSL_ECH_MAX_ECHCONFIG_LEN + 8)
#endif
EXT_RETURN tls_construct_ctos_renegotiate(SSL_CONNECTION *s, WPACKET *pkt,
unsigned int context, X509 *x,
size_t chainidx)
{
-#ifndef OPENSSL_NO_ECH
char *chosen = s->ext.hostname;
+#ifndef OPENSSL_NO_ECH
OSSL_HPKE_SUITE suite;
OSSL_ECHSTORE_ENTRY *ee = NULL;
chosen = ee->public_name;
}
}
+#endif
if (chosen == NULL)
return EXT_RETURN_NOT_SENT;
/* Add TLS extension servername to the Client Hello message */
- if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_server_name)
- || !WPACKET_start_sub_packet_u16(pkt)
- || !WPACKET_start_sub_packet_u16(pkt)
- || !WPACKET_put_bytes_u8(pkt, TLSEXT_NAMETYPE_host_name)
- || !WPACKET_sub_memcpy_u16(pkt, chosen, strlen(chosen))
- || !WPACKET_close(pkt)
- || !WPACKET_close(pkt)) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
- return EXT_RETURN_FAIL;
- }
- return EXT_RETURN_SENT;
-#else
- if (s->ext.hostname == NULL)
- return EXT_RETURN_NOT_SENT;
-
- /* Add TLS extension servername to the Client Hello message */
if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_server_name)
/* Sub-packet for server_name extension */
|| !WPACKET_start_sub_packet_u16(pkt)
/* Sub-packet for servername list (always 1 hostname)*/
|| !WPACKET_start_sub_packet_u16(pkt)
|| !WPACKET_put_bytes_u8(pkt, TLSEXT_NAMETYPE_host_name)
- || !WPACKET_sub_memcpy_u16(pkt, s->ext.hostname,
- strlen(s->ext.hostname))
+ || !WPACKET_sub_memcpy_u16(pkt, chosen, strlen(chosen))
|| !WPACKET_close(pkt)
|| !WPACKET_close(pkt)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return EXT_RETURN_FAIL;
}
-
return EXT_RETURN_SENT;
-#endif
}
/* Push a Max Fragment Len extension into ClientHello */
unsigned int context,
X509 *x, size_t chainidx)
{
-#ifndef OPENSSL_NO_ECH
- unsigned char *aval = NULL;
- size_t alen = 0;
-#endif
+ unsigned char *aval = s->ext.alpn;
+ size_t alen = s->ext.alpn_len;
s->s3.alpn_sent = 0;
+ if (!SSL_IS_FIRST_HANDSHAKE(s))
+ return EXT_RETURN_NOT_SENT;
#ifndef OPENSSL_NO_ECH
/*
* If we have different alpn and alpn_outer values, then we set
* If you don't want the inner value in the outer, you have to
* pick what to send in the outer and send that.
*/
- if (!SSL_IS_FIRST_HANDSHAKE(s))
- return EXT_RETURN_NOT_SENT;
- aval = s->ext.alpn;
- alen = s->ext.alpn_len;
if (s->ext.ech.ch_depth == 1 && s->ext.alpn == NULL) /* inner */
return EXT_RETURN_NOT_SENT;
if (s->ext.ech.ch_depth == 0 && s->ext.alpn == NULL
aval = s->ext.ech.alpn_outer;
alen = s->ext.ech.alpn_outer_len;
}
+#endif
+ if (aval == NULL)
+ return EXT_RETURN_NOT_SENT;
if (!WPACKET_put_bytes_u16(pkt,
- TLSEXT_TYPE_application_layer_protocol_negotiation)
+ TLSEXT_TYPE_application_layer_protocol_negotiation)
+ /* Sub-packet ALPN extension */
|| !WPACKET_start_sub_packet_u16(pkt)
|| !WPACKET_sub_memcpy_u16(pkt, aval, alen)
|| !WPACKET_close(pkt)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return EXT_RETURN_FAIL;
}
-#else
- if (s->ext.alpn == NULL || !SSL_IS_FIRST_HANDSHAKE(s))
- return EXT_RETURN_NOT_SENT;
-
- if (!WPACKET_put_bytes_u16(pkt,
- TLSEXT_TYPE_application_layer_protocol_negotiation)
- /* Sub-packet ALPN extension */
- || !WPACKET_start_sub_packet_u16(pkt)
- || !WPACKET_sub_memcpy_u16(pkt, s->ext.alpn, s->ext.alpn_len)
- || !WPACKET_close(pkt)) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
- return EXT_RETURN_FAIL;
- }
-#endif
s->s3.alpn_sent = 1;
-
return EXT_RETURN_SENT;
}
# ifndef OPENSSL_NO_ECH
if (s->ext.ech.ch_depth == 1) { /* stash inner */
- EVP_PKEY_up_ref(key_share_key);
+ if (EVP_PKEY_up_ref(key_share_key) != 1) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
EVP_PKEY_free(s->ext.ech.tmp_pkey);
s->ext.ech.tmp_pkey = key_share_key;
s->ext.ech.group_id = group_id;
if ((s->options & SSL_OP_TLSEXT_PADDING) == 0)
return EXT_RETURN_NOT_SENT;
+#ifndef OPENSSL_NO_ECH
+ ECH_SAME_EXT(s, pkt);
+#endif
/*
* Add padding to workaround bugs in F5 terminators. See RFC7685.
goto dopsksess;
}
+#ifndef OPENSSL_NO_ECH
+ /*
+ * When doing ECH, we get here twice (for inner then outer). The
+ * 2nd time (for outer) we can skip some checks as we know how
+ * those went last time.
+ */
+ if (s->ext.ech.es != NULL && s->ext.ech.ch_depth == 0) {
+ s->ext.tick_identity = s->ext.ech.tick_identity;
+ dores = (s->ext.tick_identity > 0);
+ goto dopsksess;
+ }
+#endif
+
/*
* Technically the C standard just says time() returns a time_t and says
* nothing about the encoding of that type. In practice most
*/
t = ossl_time_subtract(ossl_time_now(), s->session->time);
agesec = (uint32_t)ossl_time2seconds(t);
+
/*
* We calculate the age in seconds but the server may work in ms. Due to
* rounding errors we could overestimate the age by up to 1s. It is
if (reshashsize <= 0)
goto dopsksess;
s->ext.tick_identity++;
+#ifndef OPENSSL_NO_ECH
+ /* stash this for re-use in outer CH */
+ if (s->ext.ech.es != NULL && s->ext.ech.ch_depth == 1)
+ s->ext.ech.tick_identity = s->ext.tick_identity;
+#endif
dores = 1;
}
* with random values of the same length.
*/
if (s->ext.ech.es != NULL && s->ext.ech.ch_depth == 0) {
- unsigned char *rndbuf = NULL;
+ /* TODO(ECH): changes here need testing with server-side code PR */
+ unsigned char *rndbuf = NULL, *rndbufp = NULL;
size_t totalrndsize = 0;
if (s->session == NULL) {
return EXT_RETURN_FAIL;
}
totalrndsize = s->session->ext.ticklen
- + 4 /* agems */
+ + sizeof(agems)
+ s->psksession_id_len
+ reshashsize
+ pskhashsize;
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return EXT_RETURN_FAIL;
}
- /* outer CH allocate a similar sized random value */
- if (RAND_bytes_ex(s->ssl.ctx->libctx, rndbuf, totalrndsize,
- RAND_DRBG_STRENGTH) <= 0) {
+ /* for outer CH allocate a similar sized random value */
+ if (RAND_bytes_ex(s->ssl.ctx->libctx, rndbuf, totalrndsize, 0) <= 0) {
OPENSSL_free(rndbuf);
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return EXT_RETURN_FAIL;
}
/* set agems from random buffer */
- agems = *((uint32_t *)(rndbuf + s->session->ext.ticklen));
+ rndbufp = rndbuf;
+ agems = *((uint32_t *)(rndbufp));
+ rndbufp += sizeof(agems);
if (dores != 0) {
- if (!WPACKET_sub_memcpy_u16(pkt, rndbuf,
+ if (!WPACKET_sub_memcpy_u16(pkt, rndbufp,
s->session->ext.ticklen)
|| !WPACKET_put_bytes_u32(pkt, agems)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
OPENSSL_free(rndbuf);
return EXT_RETURN_FAIL;
}
+ rndbufp += s->session->ext.ticklen;
}
if (s->psksession != NULL) {
- if (!WPACKET_sub_memcpy_u16(pkt,
- rndbuf + s->session->ext.ticklen + 4,
- s->psksession_id_len)
+ if (!WPACKET_sub_memcpy_u16(pkt, rndbufp, s->psksession_id_len)
|| !WPACKET_put_bytes_u32(pkt, 0)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
OPENSSL_free(rndbuf);
return EXT_RETURN_FAIL;
}
+ rndbufp += s->psksession_id_len;
}
if (!WPACKET_close(pkt)
- || !WPACKET_get_total_written(pkt, &binderoffset)
|| !WPACKET_start_sub_packet_u16(pkt)
|| (dores == 1
- && !WPACKET_sub_memcpy_u8(pkt,
- rndbuf + s->session->ext.ticklen
- + 4 + s->psksession_id_len,
- reshashsize))
+ && !WPACKET_sub_memcpy_u8(pkt, rndbufp, reshashsize))
|| (s->psksession != NULL
- && !WPACKET_sub_memcpy_u8(pkt,
- rndbuf + s->session->ext.ticklen
- + 4 + s->psksession_id_len
- + reshashsize,
- pskhashsize))
+ && !WPACKET_sub_memcpy_u8(pkt, rndbufp, pskhashsize))
|| !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)) {
+ || !WPACKET_close(pkt)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
OPENSSL_free(rndbuf);
return EXT_RETURN_FAIL;
unsigned int context,
X509 *x, size_t chainidx)
{
-#ifndef OPENSSL_NO_ECH
char *eff_sni = s->ext.hostname;
+#ifndef OPENSSL_NO_ECH
/* if we tried ECH and failed, the outer is what's expected */
if (s->ext.ech.es != NULL && s->ext.ech.success == 0)
eff_sni = s->ext.ech.outer_hostname;
+#endif
if (eff_sni == NULL) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
return 0;
}
}
-#else
- if (s->ext.hostname == NULL) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
- return 0;
- }
-
- if (PACKET_remaining(pkt) > 0) {
- SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION);
- return 0;
- }
-
- if (!s->hit) {
- if (s->session->ext.hostname != NULL) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
- return 0;
- }
- s->session->ext.hostname = OPENSSL_strdup(s->ext.hostname);
- if (s->session->ext.hostname == NULL) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
- return 0;
- }
- }
-#endif
return 1;
}
unsigned int context, X509 *x,
size_t chainidx)
{
- if (s->ext.ech.attempted_type != TLSEXT_TYPE_ech
- && s->ext.ech.grease != OSSL_ECH_IS_GREASE
- && !(s->options & SSL_OP_ECH_GREASE))
+ int rv = 0, hpke_mode = OSSL_HPKE_MODE_BASE;
+ OSSL_ECHSTORE_ENTRY *ee = NULL;
+ OSSL_HPKE_SUITE hpke_suite = OSSL_HPKE_SUITE_DEFAULT;
+ unsigned char config_id_to_use = 0x00, info[OSSL_ECH_MAX_INFO_LEN];
+ unsigned char *encoded = NULL, *mypub = NULL;
+ size_t cipherlen = 0, aad_len = 0, lenclen = 0, mypub_len = 0;
+ size_t info_len = OSSL_ECH_MAX_INFO_LEN, clear_len = 0, encoded_len = 0;
+
+ /* whether or not we've been asked to GREASE, one way or another */
+ int grease_opt_set = (s->ext.ech.grease == OSSL_ECH_IS_GREASE
+ || ((s->options & SSL_OP_ECH_GREASE) != 0));
+
+ /* if we're not doing real ECH and not GREASEing then exit */
+ if (s->ext.ech.attempted_type != TLSEXT_TYPE_ech && grease_opt_set == 0)
return EXT_RETURN_NOT_SENT;
/* send grease if not really attempting ECH */
- if (s->ext.ech.attempted == 0
- && (s->ext.ech.grease == OSSL_ECH_IS_GREASE
- || (s->options & SSL_OP_ECH_GREASE))) {
+ if (s->ext.ech.attempted == 0 && grease_opt_set == 1) {
if (s->hello_retry_request == SSL_HRR_PENDING
&& s->ext.ech.sent != NULL) {
/* re-tx already sent GREASEy ECH */
}
return EXT_RETURN_SENT;
}
- /*
- * If not GREASEing we fake sending the outer value - after the
- * entire thing has been constructed we only then finally encode
- * and encrypt - need to do it that way as we need the rest of
- * the outer CH as AAD input to the encryption.
- */
- if (s->ext.ech.ch_depth == 0)
- return EXT_RETURN_NOT_SENT;
+
/* For the inner CH - we simply include one of these saying "inner" */
if (s->ext.ech.ch_depth == 1) {
if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_ech)
}
return EXT_RETURN_SENT;
}
+
+ /*
+ * If not GREASEing we prepare sending the outer value - after the
+ * entire thing has been constructed, putting in zeros for now where
+ * we'd otherwise include ECH ciphertext, we later encode and encrypt.
+ * We need to do it that way as we need the rest of the outer CH to
+ * be known and used as AAD input before we do encryption.
+ */
+ if (s->ext.ech.ch_depth != 0)
+ return EXT_RETURN_NOT_SENT;
+ /* Make ClientHelloInner and EncodedClientHelloInner as per spec. */
+ if (ossl_ech_encode_inner(s, &encoded, &encoded_len) != 1) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+ s->ext.ech.encoded_inner = encoded;
+ s->ext.ech.encoded_inner_len = encoded_len;
+# ifdef OSSL_ECH_SUPERVERBOSE
+ ossl_ech_pbuf("encoded inner CH", encoded, encoded_len);
+# endif
+ rv = ossl_ech_pick_matching_cfg(s, &ee, &hpke_suite);
+ if (rv != 1 || ee == NULL) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+ s->ext.ech.attempted_type = ee->version;
+ OSSL_TRACE_BEGIN(TLS) {
+ BIO_printf(trc_out, "EAAE: selected: version: %4x, config %2x\n",
+ ee->version, ee->config_id);
+ } OSSL_TRACE_END(TLS);
+ config_id_to_use = ee->config_id; /* if requested, use a random config_id instead */
+ if ((s->options & SSL_OP_ECH_IGNORE_CID) != 0) {
+ if (RAND_bytes_ex(s->ssl.ctx->libctx, &config_id_to_use, 1, 0) <= 0) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+# ifdef OSSL_ECH_SUPERVERBOSE
+ ossl_ech_pbuf("EAAE: random config_id", &config_id_to_use, 1);
+# endif
+ }
+ s->ext.ech.attempted_cid = config_id_to_use;
+# ifdef OSSL_ECH_SUPERVERBOSE
+ ossl_ech_pbuf("EAAE: peer pub", ee->pub, ee->pub_len);
+ ossl_ech_pbuf("EAAE: clear", encoded, encoded_len);
+ ossl_ech_pbuf("EAAE: ECHConfig", ee->encoded, ee->encoded_len);
+# endif
+ /*
+ * The AAD is the full outer client hello but with the correct number of
+ * zeros for where the ECH ciphertext octets will later be placed. So we
+ * add the ECH extension to the |pkt| but with zeros for ciphertext, that
+ * forms up the AAD, then after we've encrypted, we'll splice in the actual
+ * ciphertext.
+ * Watch out for the "4" offsets that remove the type and 3-octet length
+ * from the encoded CH as per the spec.
+ */
+ clear_len = ossl_ech_calc_padding(s, ee, encoded_len);
+ if (clear_len == 0) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+ lenclen = OSSL_HPKE_get_public_encap_size(hpke_suite);
+ if (s->ext.ech.hpke_ctx == NULL) { /* 1st CH */
+ if (ossl_ech_make_enc_info(ee->encoded, ee->encoded_len,
+ info, &info_len) != 1) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+# ifdef OSSL_ECH_SUPERVERBOSE
+ ossl_ech_pbuf("EAAE info", info, info_len);
+# endif
+ s->ext.ech.hpke_ctx = OSSL_HPKE_CTX_new(hpke_mode, hpke_suite,
+ OSSL_HPKE_ROLE_SENDER,
+ NULL, NULL);
+ if (s->ext.ech.hpke_ctx == NULL) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+ mypub = OPENSSL_malloc(lenclen);
+ if (mypub == NULL) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+ mypub_len = lenclen;
+ rv = OSSL_HPKE_encap(s->ext.ech.hpke_ctx, mypub, &mypub_len,
+ ee->pub, ee->pub_len, info, info_len);
+ if (rv != 1) {
+ OPENSSL_free(mypub);
+ mypub = NULL;
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+ s->ext.ech.pub = mypub;
+ s->ext.ech.pub_len = mypub_len;
+ } else { /* HRR - retrieve public */
+ mypub = s->ext.ech.pub;
+ mypub_len = s->ext.ech.pub_len;
+ if (mypub == NULL || mypub_len == 0) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+ }
+# ifdef OSSL_ECH_SUPERVERBOSE
+ ossl_ech_pbuf("EAAE: mypub", mypub, mypub_len);
+ WPACKET_get_total_written(pkt, &aad_len); /* use aad_len for tracing */
+ ossl_ech_pbuf("EAAE pkt b4", WPACKET_get_curr(pkt) - aad_len, aad_len);
+# endif
+ cipherlen = OSSL_HPKE_get_ciphertext_size(hpke_suite, clear_len);
+ if (cipherlen <= clear_len || cipherlen > OSSL_ECH_MAX_PAYLOAD_LEN) {
+ SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION);
+ goto err;
+ }
+ s->ext.ech.clearlen = clear_len;
+ s->ext.ech.cipherlen = cipherlen;
+ if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_ech)
+ || !WPACKET_start_sub_packet_u16(pkt)
+ || !WPACKET_put_bytes_u8(pkt, OSSL_ECH_OUTER_CH_TYPE)
+ || !WPACKET_put_bytes_u16(pkt, hpke_suite.kdf_id)
+ || !WPACKET_put_bytes_u16(pkt, hpke_suite.aead_id)
+ || !WPACKET_put_bytes_u8(pkt, config_id_to_use)
+ || (s->hello_retry_request == SSL_HRR_PENDING
+ && !WPACKET_put_bytes_u16(pkt, 0x00)) /* no pub */
+ || (s->hello_retry_request != SSL_HRR_PENDING
+ && !WPACKET_sub_memcpy_u16(pkt, mypub, mypub_len))
+ || !WPACKET_start_sub_packet_u16(pkt)
+ || !WPACKET_get_total_written(pkt, &s->ext.ech.cipher_offset)
+ || !WPACKET_memset(pkt, 0, cipherlen)
+ || !WPACKET_close(pkt)
+ || !WPACKET_close(pkt)) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+ /* don't count the type + 3-octet length */
+ s->ext.ech.cipher_offset -= 4;
+ return EXT_RETURN_SENT;
+err:
return EXT_RETURN_FAIL;
}
unsigned int rlen = 0;
const unsigned char *rval = NULL;
unsigned char *srval = NULL;
+ PACKET rcfgs_pkt;
/*
- * An HRR will have an ECH extension with the
- * 8-octet confirmation value, already handled
+ * An HRR will have an ECH extension with the 8-octet confirmation value.
+ * Store it away for when we check it later
*/
- if (context == SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST)
+ if (context == SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST) {
+ if (PACKET_remaining(pkt) != OSSL_ECH_SIGNAL_LEN) {
+ SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_MISMATCH);
+ return 0;
+ }
+ s->ext.ech.hrrsignal_p = (unsigned char *)PACKET_data(pkt);
+ memcpy(s->ext.ech.hrrsignal, s->ext.ech.hrrsignal_p,
+ OSSL_ECH_SIGNAL_LEN);
return 1;
+ }
/* othewise we expect retry-configs */
- if (!PACKET_get_net_2(pkt, &rlen)) {
+ if (!PACKET_get_length_prefixed_2(pkt, &rcfgs_pkt)) {
SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_MISMATCH);
return 0;
}
- if (!PACKET_get_bytes(pkt, &rval, rlen)) {
- SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_LENGTH_MISMATCH);
- return 0;
- }
+ rval = PACKET_data(&rcfgs_pkt);
+ rlen = PACKET_remaining(&rcfgs_pkt);
OPENSSL_free(s->ext.ech.returned);
s->ext.ech.returned = NULL;
srval = OPENSSL_malloc(rlen + 2);
if (!(meth->ext_flags & SSL_EXT_FLAG_RECEIVED))
continue;
}
+
+#ifndef OPENSSL_NO_ECH
+ if ((context & SSL_EXT_CLIENT_HELLO) != 0
+ && s->ext.ech.attempted == 1) {
+ if (s->ext.ech.ch_depth == 1) {
+ /* mark custom CH ext for ECH compression, if doing ECH */
+ if (s->ext.ech.n_outer_only >= OSSL_ECH_OUTERS_MAX) {
+ OSSL_TRACE_BEGIN(TLS) {
+ BIO_printf(trc_out,
+ "Too many outers to compress (max=%d)\n",
+ OSSL_ECH_OUTERS_MAX);
+ } OSSL_TRACE_END(TLS);
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_BAD_EXTENSION);
+ return 0;
+ }
+ s->ext.ech.outer_only[s->ext.ech.n_outer_only] = meth->ext_type;
+ s->ext.ech.n_outer_only++;
+ OSSL_TRACE_BEGIN(TLS) {
+ BIO_printf(trc_out, "ECH compressing type "
+ "0x%04x (tot: %d)\n",
+ (int) meth->ext_type,
+ (int) s->ext.ech.n_outer_only);
+ } OSSL_TRACE_END(TLS);
+ }
+ if (s->ext.ech.ch_depth == 0) {
+ /* TODO(ECH): we need a better way to handle indexing exts */
+ /* copy over the extension octets (if any) to outer */
+ int j, tind = -1;
+ RAW_EXTENSION *raws = NULL;
+
+ /* we gotta find the relevant index to copy over this ext */
+ if (s->clienthello == NULL
+ || s->clienthello->pre_proc_exts == NULL) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_BAD_EXTENSION);
+ return 0;
+ }
+ raws = s->clienthello->pre_proc_exts;
+ for (j = 0; j != (int) s->clienthello->pre_proc_exts_len; j++) {
+ if (raws[j].type == meth->ext_type) {
+ tind = j;
+ break;
+ }
+ }
+ if (tind == -1) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_BAD_EXTENSION);
+ return 0;
+ }
+ if (ossl_ech_copy_inner2outer(s, meth->ext_type, tind, pkt)
+ != OSSL_ECH_SAME_EXT_DONE) {
+ /* for custom exts, we really should have found it */
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_BAD_EXTENSION);
+ return 0;
+ }
+ /* we're done with that one now */
+ continue;
+ }
+ }
+#endif
+
/*
* We skip it if the callback is absent - except for a ClientHello where
* we add an empty extension.
* for extension types that previously were not supported, but now are.
*/
if (SSL_extension_supported(ext_type)
+#if !defined(OPENSSL_NO_ECH) && defined(OPENSSL_ECH_ALLOW_CUST_INJECT)
+ /*
+ * Do this conditionally so we can test an ECH in TLSv1.2
+ * via the custom extensions API.
+ * OPENSSL_ECH_ALLOW_CUST_INJECT is defined (or not) in
+ * include/openssl/ech.h and if defined enables a test in
+ * test/ech_test.c
+ */
+ && ext_type != TLSEXT_TYPE_ech
&& ext_type != TLSEXT_TYPE_signed_certificate_timestamp)
+#else
+ && ext_type != TLSEXT_TYPE_signed_certificate_timestamp)
+#endif
return 0;
/* Extension type must fit in 16 bits */
case TLSEXT_TYPE_compress_certificate:
case TLSEXT_TYPE_client_cert_type:
case TLSEXT_TYPE_server_cert_type:
+#ifndef OPENSSL_NO_ECH
+ case TLSEXT_TYPE_ech:
+ case TLSEXT_TYPE_outer_extensions:
+#endif
return 1;
default:
return 0;
#include "internal/ssl_unwrap.h"
static MSG_PROCESS_RETURN tls_process_as_hello_retry_request(SSL_CONNECTION *s,
- PACKET *pkt);
+ RAW_EXTENSION *extensions);
static MSG_PROCESS_RETURN tls_process_encrypted_extensions(SSL_CONNECTION *s,
PACKET *pkt);
#ifndef OPENSSL_NO_ECH
/*
- * Wrap the existing ClientHello construction with ECH code.
+ * Wrap ClientHello construction with ECH code.
*
- * As needed, we'll call the existing CH constructor twice,
- * first for inner, and then for outer.
+ * As needed, we'll call the CH constructor twice, first for
+ * inner, and then for outer.
*
- * So the old tls_construct_client_hello is renamed to the _aux
- * variant, and the new tls_construct_client_hello just calls
+ * `tls_construct_client_hello_aux` is the pre-ECH code
+ * and the ECH-aware tls_construct_client_hello just calls
* that if there's no ECH involved, but otherwise does ECH
* things around calls to the _aux variant.
*
__owur CON_FUNC_RETURN tls_construct_client_hello(SSL_CONNECTION *s,
WPACKET *pkt)
{
- unsigned char *innerch_full = NULL, *innerch_end = NULL;
WPACKET inner; /* "fake" pkt for inner */
BUF_MEM *inner_mem = NULL;
PACKET rpkt; /* we'll decode back the inner ch to help make the outer */
/* note version we're attempting and that an attempt is being made */
if (s->ext.ech.es->entries != NULL) {
if (ossl_ech_pick_matching_cfg(s, &ee, &suite) != 1 || ee == NULL) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, protverr);
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_UNSUPPORTED);
return 0;
}
if (ee->version != OSSL_ECH_RFCXXXX_VERSION) {
/* we only support that version for now */
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, protverr);
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_UNSUPPORTED);
return 0;
}
s->ext.ech.attempted_type = TLSEXT_TYPE_ech;
s->tmp_session_id_len = sess_id_len;
if (s->hello_retry_request == SSL_HRR_NONE
&& RAND_bytes_ex(s->ssl.ctx->libctx, s->tmp_session_id,
- sess_id_len, RAND_DRBG_STRENGTH) <= 0) {
+ sess_id_len, 0) <= 0) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
memcpy(s->tmp_session_id, s->session->session_id, sess_id_len);
}
}
- if (s->hello_retry_request != SSL_HRR_NONE) {
+ if (s->hello_retry_request != SSL_HRR_NONE)
s->ext.ech.n_outer_only = 0; /* reset count of "compressed" exts */
- OPENSSL_free(s->ext.ech.encoded_innerch);
- s->ext.ech.encoded_innerch = NULL;
- s->ext.ech.encoded_innerch_len = 0;
- if (s->ext.ech.innerch != NULL) {
- OPENSSL_free(s->ext.ech.innerch1);
- s->ext.ech.innerch1 = s->ext.ech.innerch;
- s->ext.ech.innerch1_len = s->ext.ech.innerch_len;
- s->ext.ech.innerch_len = 0;
- s->ext.ech.innerch = NULL;
- }
- }
/*
* Set CH depth flag so that other code (e.g. extension handlers)
* know where we're at: 1 is "inner CH", 0 is "outer CH"
|| tls_construct_client_hello_aux(s, &inner) != 1
|| !WPACKET_close(&inner)
|| !WPACKET_get_length(&inner, &innerlen)) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, protverr);
- goto err;
- }
- innerch_full = OPENSSL_malloc(innerlen);
- if (innerch_full == NULL) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
goto err;
}
- innerch_end = WPACKET_get_curr(&inner);
- memcpy(innerch_full, innerch_end - innerlen, innerlen);
OPENSSL_free(s->ext.ech.innerch);
- s->ext.ech.innerch = innerch_full;
+ s->ext.ech.innerch = (unsigned char*)inner_mem->data;
+ inner_mem->data = NULL;
s->ext.ech.innerch_len = innerlen;
+ /* add inner to transcript */
+ if (ossl_ech_intbuf_add(s, s->ext.ech.innerch, innerlen, 0) != 1) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
WPACKET_cleanup(&inner);
BUF_MEM_free(inner_mem);
inner_mem = NULL;
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
goto err;
}
- /* Make ClientHelloInner and EncodedClientHelloInner as per spec. */
- if (ossl_ech_encode_inner(s) != 1) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
- goto err;
- }
-# ifdef OSSL_ECH_SUPERVERBOSE
- ossl_ech_pbuf("encoded inner CH", s->ext.ech.encoded_innerch,
- s->ext.ech.encoded_innerch_len);
-# endif
+
s->ext.ech.ch_depth = 0; /* set depth for outer CH */
/*
* If we want different key shares for inner and outer, then
/* Make second call into CH construction for outer CH. */
rv = tls_construct_client_hello_aux(s, pkt);
if (rv != 1) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, protverr);
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
goto err;
}
# ifdef OSSL_ECH_SUPERVERBOSE
if (s->ext.ech.es != NULL && s->ext.ech.ch_depth == 1)
p = s->ext.ech.client_random;
else
- p = s->s3.client_random;
-#else
- p = s->s3.client_random;
#endif
+ p = s->s3.client_random;
/*
* for DTLS if client_random is initialized, reuse it, we are
* For TLS 1.3 we always set the ClientHello version to 1.2 and rely on the
* supported_versions extension for the real supported versions.
*/
-#ifndef OPENSSL_NO_ECH
if (!WPACKET_put_bytes_u16(pkt, s->client_version)
|| !WPACKET_memcpy(pkt, p, SSL3_RANDOM_SIZE)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return CON_FUNC_ERROR;
}
-#else
- if (!WPACKET_put_bytes_u16(pkt, s->client_version)
- || !WPACKET_memcpy(pkt, s->s3.client_random, SSL3_RANDOM_SIZE)) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
- return CON_FUNC_ERROR;
- }
-#endif
/* Session ID */
session_id = s->session->session_id;
#endif
#ifndef OPENSSL_NO_ECH
const unsigned char *shbuf = NULL;
- size_t shlen, chend, fixedshbuf_len, alen;
+ size_t shlen, alen;
/*
* client and server accept signal buffers, initialise in case of
* e.g. memory fail when calculating, only really applies when
* SUPERVERBOSE is defined and we trace these.
*/
- unsigned char c_signal[OSSL_ECH_SIGNAL_LEN] = { 0 };
+ unsigned char c_signal[OSSL_ECH_SIGNAL_LEN] = { 0 };
unsigned char s_signal[OSSL_ECH_SIGNAL_LEN] = { 0xff };
unsigned char *abuf = NULL;
goto err;
}
+ /* TLS extensions */
+ if (PACKET_remaining(pkt) == 0 && !hrr) {
+ PACKET_null_init(&extpkt);
+ } else if (!PACKET_as_length_prefixed_2(pkt, &extpkt)
+ || PACKET_remaining(pkt) != 0) {
+ SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_BAD_LENGTH);
+ goto err;
+ }
+
+ if (hrr) {
+ if (!tls_collect_extensions(s, &extpkt, SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST,
+ &extensions, NULL, 1)
+ || !tls_parse_extension(s, TLSEXT_IDX_ech,
+ SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST,
+ extensions, NULL, 0)) {
+ /* SSLfatal() already called */
+ goto err;
+ }
+ } else {
+ if (!tls_collect_extensions(s, &extpkt,
+ SSL_EXT_TLS1_2_SERVER_HELLO
+ | SSL_EXT_TLS1_3_SERVER_HELLO,
+ &extensions, NULL, 1)) {
+ /* SSLfatal() already called */
+ goto err;
+ }
+ }
+
#ifndef OPENSSL_NO_ECH
/*
* If we sent an ECH then check if that worked based on the
&& s->ext.ech.done != 1 && s->ext.ech.ch_depth == 0
&& s->ext.ech.grease == OSSL_ECH_NOT_GREASE
&& s->ext.ech.attempted_type == TLSEXT_TYPE_ech) {
- /* try set this earlier see what happens */
if (!set_client_ciphersuite(s, cipherchars)) {
/* SSLfatal() already called */
goto err;
}
+ /* add any SH/HRR to inner transcript if we tried ECH */
+ if (s->ext.ech.attempted == 1) {
+ unsigned char prelude[4];
+
+ prelude[0] = SSL3_MT_SERVER_HELLO;
+ prelude[1] = (shlen >> 16) & 0xff;
+ prelude[2] = (shlen >> 8) & 0xff;
+ prelude[3] = shlen & 0xff;
+ if (ossl_ech_intbuf_add(s, prelude, sizeof(prelude), hrr) != 1
+ || ossl_ech_intbuf_add(s, shbuf, shlen, 0) != 1) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+ }
/* check the ECH accept signal */
- if (ossl_ech_calc_confirm(s, hrr, c_signal, shbuf, shlen) != 1
- || ossl_ech_find_confirm(s, hrr, s_signal, shbuf, shlen) != 1
+ if (ossl_ech_calc_confirm(s, hrr, c_signal, shlen) != 1) {
+ /* SSLfatal() already called */
+ OSSL_TRACE(TLS, "ECH calc confim failed\n");
+ goto err;
+ }
+ if (ossl_ech_find_confirm(s, hrr, s_signal) != 1
|| memcmp(s_signal, c_signal, sizeof(c_signal)) != 0) {
OSSL_TRACE(TLS, "ECH accept check failed\n");
# ifdef OSSL_ECH_SUPERVERBOSE
} OSSL_TRACE_END(TLS);
s->ext.ech.success = 1;
}
+ /* we're done with that hrrsignal (if we got one) */
+ s->ext.ech.hrrsignal_p = NULL;
if (!hrr && s->ext.ech.success == 1) {
if (ossl_ech_swaperoo(s) != 1
- || ossl_ech_make_transcript_buffer(s, hrr, shbuf, shlen,
- &abuf, &alen,
- &chend, &fixedshbuf_len) != 1
+ || ossl_ech_intbuf_fetch(s, &abuf, &alen) != 1
|| ossl_ech_reset_hs_buffer(s, abuf, alen) != 1) {
- OPENSSL_free(abuf);
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
goto err;
}
- OPENSSL_free(abuf);
} else if (!hrr) {
/*
* If we got retry_configs then we should be validating
}
#endif
- /* TLS extensions */
- if (PACKET_remaining(pkt) == 0 && !hrr) {
- PACKET_null_init(&extpkt);
- } else if (!PACKET_as_length_prefixed_2(pkt, &extpkt)
- || PACKET_remaining(pkt) != 0) {
- SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_BAD_LENGTH);
- goto err;
- }
-
if (!hrr) {
- if (!tls_collect_extensions(s, &extpkt,
- SSL_EXT_TLS1_2_SERVER_HELLO
- | SSL_EXT_TLS1_3_SERVER_HELLO,
- &extensions, NULL, 1)) {
- /* SSLfatal() already called */
- goto err;
- }
-
if (!ssl_choose_client_version(s, sversion, extensions)) {
/* SSLfatal() already called */
goto err;
}
if (hrr) {
+ int ret;
+
if (!set_client_ciphersuite(s, cipherchars)) {
/* SSLfatal() already called */
goto err;
}
- return tls_process_as_hello_retry_request(s, &extpkt);
+ ret = tls_process_as_hello_retry_request(s, extensions);
+ OPENSSL_free(extensions);
+
+ return ret;
}
/*
}
static MSG_PROCESS_RETURN tls_process_as_hello_retry_request(SSL_CONNECTION *s,
- PACKET *extpkt)
+ RAW_EXTENSION *extensions)
{
- RAW_EXTENSION *extensions = NULL;
-
/*
* If we were sending early_data then any alerts should not be sent using
* the old wrlmethod.
/* We are definitely going to be using TLSv1.3 */
s->rlayer.wrlmethod->set_protocol_version(s->rlayer.wrl, TLS1_3_VERSION);
- if (!tls_collect_extensions(s, extpkt, SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST,
- &extensions, NULL, 1)
- || !tls_parse_all_extensions(s, SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST,
- extensions, NULL, 0, 1)) {
+ if (!tls_parse_all_extensions(s, SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST,
+ extensions, NULL, 0, 1)) {
/* SSLfatal() already called */
goto err;
}
- OPENSSL_free(extensions);
- extensions = NULL;
-
if (s->ext.tls13_cookie_len == 0 && s->s3.tmp.pkey != NULL) {
/*
* We didn't receive a cookie or a new key_share so the next
return MSG_PROCESS_FINISHED_READING;
err:
- OPENSSL_free(extensions);
return MSG_PROCESS_ERROR;
}
#ifndef OPENSSL_NO_ECH
/* check result of ech and return error if needed */
- if (!s->server
- && s->ext.ech.es != NULL
+ /*
+ * TODO(ECH): check that we never get here in a server
+ * during split-mode or test cases - there used be a
+ * check of !s->server added to the below.
+ */
+ if (s->ext.ech.es != NULL
&& s->ext.ech.attempted == 1
&& s->ext.ech.success != 1
&& s->ext.ech.grease != OSSL_ECH_IS_GREASE) {
labellen = sizeof(client_early_traffic) - 1;
log_label = CLIENT_EARLY_LABEL;
- handlen = BIO_get_mem_data(s->s3.handshake_buffer, &hdata);
- if (handlen <= 0) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_BAD_HANDSHAKE_LENGTH);
- goto err;
+#ifndef OPENSSL_NO_ECH
+ /* if ECH worked then use the innerch and not the h/s buffer here */
+ if (((which & SSL3_CC_SERVER) && s->ext.ech.success == 1)
+ || ((which & SSL3_CC_CLIENT) && s->ext.ech.attempted == 1)) {
+ if (s->ext.ech.innerch == NULL) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+ handlen = s->ext.ech.innerch_len;
+ hdata = s->ext.ech.innerch;
+ } else
+#endif
+ {
+ handlen = BIO_get_mem_data(s->s3.handshake_buffer, &hdata);
+ if (handlen <= 0) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR,
+ SSL_R_BAD_HANDSHAKE_LENGTH);
+ goto err;
+ }
}
if (s->early_data_state == SSL_EARLY_DATA_CONNECTING
static char *privkey = NULL;
static char *rootcert = NULL;
+/* TODO(ECH): add some testing of SSL_OP_ECH_IGNORE_CID */
+
/* callback */
static unsigned int test_cb(SSL *s, const char *str)
{
|| !TEST_false(OSSL_ECHSTORE_write_pem(es, 100, out)))
goto end;
flush_time = time(0);
+ /*
+ * Occasionally, flush_time will be 1 more than add_time. We'll
+ * check for that as that should catch a few more code paths
+ * in the flush_keys API.
+ */
if (!TEST_true(OSSL_ECHSTORE_flush_keys(es, flush_time - add_time))
|| !TEST_int_eq(OSSL_ECHSTORE_num_keys(es, &keysaftr), 1)
- || !TEST_int_eq(keysaftr, 0))
+ || ((flush_time <= add_time) && !TEST_int_eq(keysaftr, 0))
+ || ((flush_time > add_time) && !TEST_int_eq(keysaftr, 1))) {
+ TEST_info("Flush time: %lld, add_time: %lld", (long long)flush_time,
+ (long long)add_time);
goto end;
+ }
rv = 1;
end:
OPENSSL_free(pn);