+ return EXT_RETURN_SENT;
+}
+
+EXT_RETURN tls_construct_ctos_cookie(SSL *s, WPACKET *pkt, unsigned int context,
+ X509 *x, size_t chainidx, int *al)
+{
+ EXT_RETURN ret = EXT_RETURN_FAIL;
+
+ /* Should only be set if we've had an HRR */
+ if (s->ext.tls13_cookie_len == 0)
+ return EXT_RETURN_NOT_SENT;
+
+ if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_cookie)
+ /* Extension data sub-packet */
+ || !WPACKET_start_sub_packet_u16(pkt)
+ || !WPACKET_sub_memcpy_u16(pkt, s->ext.tls13_cookie,
+ s->ext.tls13_cookie_len)
+ || !WPACKET_close(pkt)) {
+ SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_COOKIE, ERR_R_INTERNAL_ERROR);
+ goto end;
+ }
+
+ ret = EXT_RETURN_SENT;
+ end:
+ OPENSSL_free(s->ext.tls13_cookie);
+ s->ext.tls13_cookie = NULL;
+ s->ext.tls13_cookie_len = 0;
+
+ return ret;
+}
+
+EXT_RETURN tls_construct_ctos_early_data(SSL *s, WPACKET *pkt,
+ unsigned int context, X509 *x,
+ size_t chainidx, int *al)
+{
+ const unsigned char *id;
+ size_t idlen;
+ SSL_SESSION *psksess = NULL;
+ SSL_SESSION *edsess = NULL;
+ const EVP_MD *handmd = NULL;
+
+ 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)
+ || (psksess != NULL
+ && psksess->ssl_version != TLS1_3_VERSION))) {
+ SSL_SESSION_free(psksess);
+ SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_EARLY_DATA, SSL_R_BAD_PSK);
+ return EXT_RETURN_FAIL;
+ }
+
+ SSL_SESSION_free(s->psksession);
+ s->psksession = psksess;
+ if (psksess != NULL) {
+ OPENSSL_free(s->psksession_id);
+ s->psksession_id = OPENSSL_memdup(id, idlen);
+ if (s->psksession_id == NULL) {
+ SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_EARLY_DATA, ERR_R_INTERNAL_ERROR);
+ return EXT_RETURN_FAIL;
+ }
+ s->psksession_id_len = idlen;
+ }
+
+ if (s->early_data_state != SSL_EARLY_DATA_CONNECTING
+ || (s->session->ext.max_early_data == 0
+ && (psksess == NULL || psksess->ext.max_early_data == 0))) {
+ s->max_early_data = 0;
+ return EXT_RETURN_NOT_SENT;
+ }
+ edsess = s->session->ext.max_early_data != 0 ? s->session : psksess;
+ s->max_early_data = edsess->ext.max_early_data;
+
+ if ((s->ext.hostname == NULL && edsess->ext.hostname != NULL)
+ || (s->ext.hostname != NULL
+ && (edsess->ext.hostname == NULL
+ || strcmp(s->ext.hostname, edsess->ext.hostname) != 0))) {
+ SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_EARLY_DATA,
+ SSL_R_INCONSISTENT_EARLY_DATA_SNI);
+ return EXT_RETURN_FAIL;
+ }
+
+ if ((s->ext.alpn == NULL && edsess->ext.alpn_selected != NULL)) {
+ SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_EARLY_DATA,
+ SSL_R_INCONSISTENT_EARLY_DATA_ALPN);
+ return EXT_RETURN_FAIL;
+ }
+
+ /*
+ * Verify that we are offering an ALPN protocol consistent with the early
+ * data.
+ */
+ if (edsess->ext.alpn_selected != NULL) {
+ PACKET prots, alpnpkt;
+ int found = 0;
+
+ if (!PACKET_buf_init(&prots, s->ext.alpn, s->ext.alpn_len)) {
+ SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_EARLY_DATA, ERR_R_INTERNAL_ERROR);
+ return EXT_RETURN_FAIL;
+ }
+ while (PACKET_get_length_prefixed_1(&prots, &alpnpkt)) {
+ if (PACKET_equal(&alpnpkt, edsess->ext.alpn_selected,
+ edsess->ext.alpn_selected_len)) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_EARLY_DATA,
+ SSL_R_INCONSISTENT_EARLY_DATA_ALPN);
+ return EXT_RETURN_FAIL;
+ }
+ }
+
+ if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_early_data)
+ || !WPACKET_start_sub_packet_u16(pkt)
+ || !WPACKET_close(pkt)) {
+ SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_EARLY_DATA, ERR_R_INTERNAL_ERROR);
+ return EXT_RETURN_FAIL;
+ }
+
+ /*
+ * We set this to rejected here. Later, if the server acknowledges the
+ * extension, we set it to accepted.
+ */
+ s->ext.early_data = SSL_EARLY_DATA_REJECTED;
+
+ return EXT_RETURN_SENT;