+
+ /* Close the sub-packet created by create_ticket_prequel() */
+ if (!WPACKET_close(pkt)) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR,
+ SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+
+ ok = 1;
+ err:
+ OPENSSL_free(senc);
+ EVP_CIPHER_CTX_free(ctx);
+ HMAC_CTX_free(hctx);
+ return ok;
+}
+
+int tls_construct_new_session_ticket(SSL *s, WPACKET *pkt)
+{
+ SSL_CTX *tctx = s->session_ctx;
+ unsigned char tick_nonce[TICKET_NONCE_SIZE];
+ union {
+ unsigned char age_add_c[sizeof(uint32_t)];
+ uint32_t age_add;
+ } age_add_u;
+
+ age_add_u.age_add = 0;
+
+ if (SSL_IS_TLS13(s)) {
+ size_t i, hashlen;
+ uint64_t nonce;
+ static const unsigned char nonce_label[] = "resumption";
+ const EVP_MD *md = ssl_handshake_md(s);
+ void (*cb) (const SSL *ssl, int type, int val) = NULL;
+ int hashleni = EVP_MD_size(md);
+
+ /* Ensure cast to size_t is safe */
+ if (!ossl_assert(hashleni >= 0)) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR,
+ SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET,
+ ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+ hashlen = (size_t)hashleni;
+
+ if (s->info_callback != NULL)
+ cb = s->info_callback;
+ else if (s->ctx->info_callback != NULL)
+ cb = s->ctx->info_callback;
+
+ if (cb != NULL) {
+ /*
+ * We don't start and stop the handshake in between each ticket when
+ * sending more than one - but it should appear that way to the info
+ * callback.
+ */
+ if (s->sent_tickets != 0) {
+ ossl_statem_set_in_init(s, 0);
+ cb(s, SSL_CB_HANDSHAKE_DONE, 1);
+ ossl_statem_set_in_init(s, 1);
+ }
+ cb(s, SSL_CB_HANDSHAKE_START, 1);
+ }
+ /*
+ * If we already sent one NewSessionTicket, or we resumed then
+ * s->session may already be in a cache and so we must not modify it.
+ * Instead we need to take a copy of it and modify that.
+ */
+ if (s->sent_tickets != 0 || s->hit) {
+ SSL_SESSION *new_sess = ssl_session_dup(s->session, 0);
+
+ if (new_sess == NULL) {
+ /* SSLfatal already called */
+ goto err;
+ }
+
+ SSL_SESSION_free(s->session);
+ s->session = new_sess;
+ }
+
+ if (!ssl_generate_session_id(s, s->session)) {
+ /* SSLfatal() already called */
+ goto err;
+ }
+ if (RAND_bytes(age_add_u.age_add_c, sizeof(age_add_u)) <= 0) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR,
+ SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET,
+ ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+ s->session->ext.tick_age_add = age_add_u.age_add;
+
+ nonce = s->next_ticket_nonce;
+ for (i = TICKET_NONCE_SIZE; i > 0; i--) {
+ tick_nonce[i - 1] = (unsigned char)(nonce & 0xff);
+ nonce >>= 8;
+ }
+
+ if (!tls13_hkdf_expand(s, md, s->resumption_master_secret,
+ nonce_label,
+ sizeof(nonce_label) - 1,
+ tick_nonce,
+ TICKET_NONCE_SIZE,
+ s->session->master_key,
+ hashlen)) {
+ /* SSLfatal() already called */
+ goto err;
+ }
+ s->session->master_key_length = hashlen;
+
+ s->session->time = (long)time(NULL);
+ if (s->s3->alpn_selected != NULL) {
+ OPENSSL_free(s->session->ext.alpn_selected);
+ s->session->ext.alpn_selected =
+ OPENSSL_memdup(s->s3->alpn_selected, s->s3->alpn_selected_len);
+ if (s->session->ext.alpn_selected == NULL) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR,
+ SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET,
+ ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+ s->session->ext.alpn_selected_len = s->s3->alpn_selected_len;
+ }
+ s->session->ext.max_early_data = s->max_early_data;
+ }
+
+ if (tctx->generate_ticket_cb != NULL &&
+ tctx->generate_ticket_cb(s, tctx->ticket_cb_data) == 0)
+ goto err;
+
+ if (!construct_stateless_ticket(s, pkt, age_add_u.age_add, tick_nonce)) {
+ /* SSLfatal() already called */
+ goto err;
+ }
+