Disconnect handshake message creation from init_buf.
This allows us to use CBB for all handshake messages. Now, SSL_PROTOCOL_METHOD
is responsible for implementing a trio of CBB-related hooks to assemble
handshake messages.
Change-Id: I144d3cac4f05b6637bf45d3f838673fc5c854405
Reviewed-on: https://boringssl-review.googlesource.com/8440
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/handshake_server.c b/ssl/handshake_server.c
index 7f7f3b8..128bb8d 100644
--- a/ssl/handshake_server.c
+++ b/ssl/handshake_server.c
@@ -1060,7 +1060,7 @@
static int ssl3_send_server_hello(SSL *ssl) {
if (ssl->state == SSL3_ST_SW_SRVR_HELLO_B) {
- return ssl_do_write(ssl);
+ return ssl->method->write_message(ssl);
}
assert(ssl->state == SSL3_ST_SW_SRVR_HELLO_A);
@@ -1085,71 +1085,63 @@
return -1;
}
- CBB cbb, session_id;
- size_t length;
- CBB_zero(&cbb);
- if (!CBB_init_fixed(&cbb, ssl_handshake_start(ssl),
- ssl->init_buf->max - SSL_HM_HEADER_LENGTH(ssl)) ||
- !CBB_add_u16(&cbb, ssl->version) ||
- !CBB_add_bytes(&cbb, ssl->s3->server_random, SSL3_RANDOM_SIZE) ||
- !CBB_add_u8_length_prefixed(&cbb, &session_id) ||
+ CBB cbb, body, session_id;
+ if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_SERVER_HELLO) ||
+ !CBB_add_u16(&body, ssl->version) ||
+ !CBB_add_bytes(&body, ssl->s3->server_random, SSL3_RANDOM_SIZE) ||
+ !CBB_add_u8_length_prefixed(&body, &session_id) ||
!CBB_add_bytes(&session_id, ssl->session->session_id,
ssl->session->session_id_length) ||
- !CBB_add_u16(&cbb, ssl_cipher_get_value(ssl->s3->tmp.new_cipher)) ||
- !CBB_add_u8(&cbb, 0 /* no compression */) ||
- !ssl_add_serverhello_tlsext(ssl, &cbb) ||
- !CBB_finish(&cbb, NULL, &length) ||
- !ssl_set_handshake_header(ssl, SSL3_MT_SERVER_HELLO, length)) {
+ !CBB_add_u16(&body, ssl_cipher_get_value(ssl->s3->tmp.new_cipher)) ||
+ !CBB_add_u8(&body, 0 /* no compression */) ||
+ !ssl_add_serverhello_tlsext(ssl, &body) ||
+ !ssl->method->finish_message(ssl, &cbb)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
CBB_cleanup(&cbb);
return -1;
}
ssl->state = SSL3_ST_SW_SRVR_HELLO_B;
- return ssl_do_write(ssl);
+ return ssl->method->write_message(ssl);
}
static int ssl3_send_server_certificate(SSL *ssl) {
- if (ssl->state == SSL3_ST_SW_CERT_A) {
- if (!ssl3_output_cert_chain(ssl)) {
- return 0;
- }
- ssl->state = SSL3_ST_SW_CERT_B;
+ if (ssl->state == SSL3_ST_SW_CERT_B) {
+ return ssl->method->write_message(ssl);
}
- /* SSL3_ST_SW_CERT_B */
- return ssl_do_write(ssl);
+ if (!ssl3_output_cert_chain(ssl)) {
+ return 0;
+ }
+ ssl->state = SSL3_ST_SW_CERT_B;
+ return ssl->method->write_message(ssl);
}
static int ssl3_send_certificate_status(SSL *ssl) {
- if (ssl->state == SSL3_ST_SW_CERT_STATUS_A) {
- CBB out, ocsp_response;
- size_t length;
-
- CBB_zero(&out);
- if (!CBB_init_fixed(&out, ssl_handshake_start(ssl),
- ssl->init_buf->max - SSL_HM_HEADER_LENGTH(ssl)) ||
- !CBB_add_u8(&out, TLSEXT_STATUSTYPE_ocsp) ||
- !CBB_add_u24_length_prefixed(&out, &ocsp_response) ||
- !CBB_add_bytes(&ocsp_response, ssl->ctx->ocsp_response,
- ssl->ctx->ocsp_response_length) ||
- !CBB_finish(&out, NULL, &length) ||
- !ssl_set_handshake_header(ssl, SSL3_MT_CERTIFICATE_STATUS, length)) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- CBB_cleanup(&out);
- return -1;
- }
-
- ssl->state = SSL3_ST_SW_CERT_STATUS_B;
+ if (ssl->state == SSL3_ST_SW_CERT_STATUS_B) {
+ return ssl->method->write_message(ssl);
}
- /* SSL3_ST_SW_CERT_STATUS_B */
- return ssl_do_write(ssl);
+ CBB cbb, body, ocsp_response;
+ if (!ssl->method->init_message(ssl, &cbb, &body,
+ SSL3_MT_CERTIFICATE_STATUS) ||
+ !CBB_add_u8(&body, TLSEXT_STATUSTYPE_ocsp) ||
+ !CBB_add_u24_length_prefixed(&body, &ocsp_response) ||
+ !CBB_add_bytes(&ocsp_response, ssl->ctx->ocsp_response,
+ ssl->ctx->ocsp_response_length) ||
+ !ssl->method->finish_message(ssl, &cbb)) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+ CBB_cleanup(&cbb);
+ return -1;
+ }
+
+ ssl->state = SSL3_ST_SW_CERT_STATUS_B;
+ return ssl->method->write_message(ssl);
}
static int ssl3_send_server_key_exchange(SSL *ssl) {
if (ssl->state == SSL3_ST_SW_KEY_EXCH_C) {
- return ssl_do_write(ssl);
+ return ssl->method->write_message(ssl);
}
CBB cbb, child;
@@ -1243,9 +1235,10 @@
}
/* Assemble the message. */
- if (!CBB_init_fixed(&cbb, ssl_handshake_start(ssl),
- ssl->init_buf->max - SSL_HM_HEADER_LENGTH(ssl)) ||
- !CBB_add_bytes(&cbb, ssl->s3->tmp.server_params,
+ CBB body;
+ if (!ssl->method->init_message(ssl, &cbb, &body,
+ SSL3_MT_SERVER_KEY_EXCHANGE) ||
+ !CBB_add_bytes(&body, ssl->s3->tmp.server_params,
ssl->s3->tmp.server_params_len)) {
goto err;
}
@@ -1261,7 +1254,7 @@
const EVP_MD *md;
if (ssl3_protocol_version(ssl) >= TLS1_2_VERSION) {
md = tls1_choose_signing_digest(ssl);
- if (!tls12_add_sigandhash(ssl, &cbb, md)) {
+ if (!tls12_add_sigandhash(ssl, &body, md)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
goto err;
@@ -1275,7 +1268,7 @@
/* Add space for the signature. */
const size_t max_sig_len = ssl_private_key_max_signature_len(ssl);
uint8_t *ptr;
- if (!CBB_add_u16_length_prefixed(&cbb, &child) ||
+ if (!CBB_add_u16_length_prefixed(&body, &child) ||
!CBB_reserve(&child, &ptr, max_sig_len)) {
goto err;
}
@@ -1322,9 +1315,7 @@
}
}
- size_t length;
- if (!CBB_finish(&cbb, NULL, &length) ||
- !ssl_set_handshake_header(ssl, SSL3_MT_SERVER_KEY_EXCHANGE, length)) {
+ if (!ssl->method->finish_message(ssl, &cbb)) {
goto err;
}
@@ -1333,91 +1324,118 @@
ssl->s3->tmp.server_params_len = 0;
ssl->state = SSL3_ST_SW_KEY_EXCH_C;
- return ssl_do_write(ssl);
+ return ssl->method->write_message(ssl);
err:
CBB_cleanup(&cbb);
return -1;
}
-static int ssl3_send_certificate_request(SSL *ssl) {
- uint8_t *p, *d;
+static int add_cert_types(SSL *ssl, CBB *cbb) {
+ /* Get configured signature algorithms. */
+ int have_rsa_sign = 0;
+ int have_ecdsa_sign = 0;
+ const uint8_t *sig;
+ size_t siglen = tls12_get_psigalgs(ssl, &sig);
size_t i;
- int j, nl, off, n;
- STACK_OF(X509_NAME) *sk = NULL;
- X509_NAME *name;
- BUF_MEM *buf;
+ for (i = 0; i < siglen; i += 2, sig += 2) {
+ switch (sig[1]) {
+ case TLSEXT_signature_rsa:
+ have_rsa_sign = 1;
+ break;
- if (ssl->state == SSL3_ST_SW_CERT_REQ_A) {
- buf = ssl->init_buf;
-
- d = p = ssl_handshake_start(ssl);
-
- /* get the list of acceptable cert types */
- p++;
- n = ssl3_get_req_cert_type(ssl, p);
- d[0] = n;
- p += n;
- n++;
-
- if (ssl3_protocol_version(ssl) >= TLS1_2_VERSION) {
- const uint8_t *psigs;
- nl = tls12_get_psigalgs(ssl, &psigs);
- s2n(nl, p);
- memcpy(p, psigs, nl);
- p += nl;
- n += nl + 2;
+ case TLSEXT_signature_ecdsa:
+ have_ecdsa_sign = 1;
+ break;
}
-
- off = n;
- p += 2;
- n += 2;
-
- sk = SSL_get_client_CA_list(ssl);
- nl = 0;
- if (sk != NULL) {
- for (i = 0; i < sk_X509_NAME_num(sk); i++) {
- name = sk_X509_NAME_value(sk, i);
- j = i2d_X509_NAME(name, NULL);
- if (!BUF_MEM_grow_clean(buf, SSL_HM_HEADER_LENGTH(ssl) + n + j + 2)) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB);
- goto err;
- }
- p = ssl_handshake_start(ssl) + n;
- s2n(j, p);
- i2d_X509_NAME(name, &p);
- n += 2 + j;
- nl += 2 + j;
- }
- }
-
- /* else no CA names */
- p = ssl_handshake_start(ssl) + off;
- s2n(nl, p);
-
- if (!ssl_set_handshake_header(ssl, SSL3_MT_CERTIFICATE_REQUEST, n)) {
- goto err;
- }
- ssl->state = SSL3_ST_SW_CERT_REQ_B;
}
- /* SSL3_ST_SW_CERT_REQ_B */
- return ssl_do_write(ssl);
+ if (have_rsa_sign && !CBB_add_u8(cbb, SSL3_CT_RSA_SIGN)) {
+ return 0;
+ }
+
+ /* ECDSA certs can be used with RSA cipher suites as well so we don't need to
+ * check for SSL_kECDH or SSL_kECDHE. */
+ if (ssl->version >= TLS1_VERSION && have_ecdsa_sign &&
+ !CBB_add_u8(cbb, TLS_CT_ECDSA_SIGN)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+static int ssl3_send_certificate_request(SSL *ssl) {
+ if (ssl->state == SSL3_ST_SW_CERT_REQ_B) {
+ return ssl->method->write_message(ssl);
+ }
+
+ CBB cbb, body, cert_types, sigalgs_cbb, names_cbb, name_cbb;
+ if (!ssl->method->init_message(ssl, &cbb, &body,
+ SSL3_MT_CERTIFICATE_REQUEST) ||
+ !CBB_add_u8_length_prefixed(&body, &cert_types) ||
+ !add_cert_types(ssl, &cert_types)) {
+ goto err;
+ }
+
+ if (ssl3_protocol_version(ssl) >= TLS1_2_VERSION) {
+ const uint8_t *sigalgs;
+ size_t sigalgs_len = tls12_get_psigalgs(ssl, &sigalgs);
+ if (!CBB_add_u16_length_prefixed(&body, &sigalgs_cbb) ||
+ !CBB_add_bytes(&sigalgs_cbb, sigalgs, sigalgs_len)) {
+ goto err;
+ }
+ }
+
+ STACK_OF(X509_NAME) *sk = SSL_get_client_CA_list(ssl);
+ if (sk != NULL) {
+ if (!CBB_add_u16_length_prefixed(&body, &names_cbb)) {
+ goto err;
+ }
+
+ size_t i;
+ for (i = 0; i < sk_X509_NAME_num(sk); i++) {
+ X509_NAME *name = sk_X509_NAME_value(sk, i);
+ int len = i2d_X509_NAME(name, NULL);
+ if (len < 0) {
+ goto err;
+ }
+ uint8_t *ptr;
+ if (!CBB_add_u16_length_prefixed(&names_cbb, &name_cbb) ||
+ !CBB_add_space(&name_cbb, &ptr, (size_t)len) ||
+ (len > 0 && i2d_X509_NAME(name, &ptr) < 0)) {
+ goto err;
+ }
+ }
+ }
+
+ if (!ssl->method->finish_message(ssl, &cbb)) {
+ goto err;
+ }
+
+ ssl->state = SSL3_ST_SW_CERT_REQ_B;
+ return ssl->method->write_message(ssl);
err:
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+ CBB_cleanup(&cbb);
return -1;
}
static int ssl3_send_server_hello_done(SSL *ssl) {
- if (ssl->state == SSL3_ST_SW_SRVR_DONE_A) {
- if (!ssl_set_handshake_header(ssl, SSL3_MT_SERVER_HELLO_DONE, 0)) {
- return -1;
- }
- ssl->state = SSL3_ST_SW_SRVR_DONE_B;
+ if (ssl->state == SSL3_ST_SW_SRVR_DONE_B) {
+ return ssl->method->write_message(ssl);
}
- /* SSL3_ST_SW_SRVR_DONE_B */
- return ssl_do_write(ssl);
+ CBB cbb, body;
+ if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_SERVER_HELLO_DONE) ||
+ !ssl->method->finish_message(ssl, &cbb)) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+ CBB_cleanup(&cbb);
+ return -1;
+ }
+
+ ssl->state = SSL3_ST_SW_SRVR_DONE_B;
+ return ssl->method->write_message(ssl);
}
static int ssl3_get_client_certificate(SSL *ssl) {
@@ -2064,130 +2082,106 @@
return ret;
}
-/* send a new session ticket (not necessarily for a new session) */
static int ssl3_send_new_session_ticket(SSL *ssl) {
- int ret = -1;
- uint8_t *session = NULL;
- size_t session_len;
- EVP_CIPHER_CTX ctx;
- HMAC_CTX hctx;
-
- EVP_CIPHER_CTX_init(&ctx);
- HMAC_CTX_init(&hctx);
-
- if (ssl->state == SSL3_ST_SW_SESSION_TICKET_A) {
- uint8_t *p, *macstart;
- int len;
- unsigned int hlen;
- SSL_CTX *tctx = ssl->initial_ctx;
- uint8_t iv[EVP_MAX_IV_LENGTH];
- uint8_t key_name[16];
- /* The maximum overhead of encrypting the session is 16 (key name) + IV +
- * one block of encryption overhead + HMAC. */
- const size_t max_ticket_overhead =
- 16 + EVP_MAX_IV_LENGTH + EVP_MAX_BLOCK_LENGTH + EVP_MAX_MD_SIZE;
-
- /* Serialize the SSL_SESSION to be encoded into the ticket. */
- if (!SSL_SESSION_to_bytes_for_ticket(ssl->session, &session,
- &session_len)) {
- goto err;
- }
-
- /* If the session is too long, emit a dummy value rather than abort the
- * connection. */
- if (session_len > 0xFFFF - max_ticket_overhead) {
- static const char kTicketPlaceholder[] = "TICKET TOO LARGE";
- const size_t placeholder_len = strlen(kTicketPlaceholder);
-
- OPENSSL_free(session);
- session = NULL;
-
- p = ssl_handshake_start(ssl);
- /* Emit ticket_lifetime_hint. */
- l2n(0, p);
- /* Emit ticket. */
- s2n(placeholder_len, p);
- memcpy(p, kTicketPlaceholder, placeholder_len);
- p += placeholder_len;
-
- len = p - ssl_handshake_start(ssl);
- if (!ssl_set_handshake_header(ssl, SSL3_MT_NEW_SESSION_TICKET, len)) {
- goto err;
- }
- ssl->state = SSL3_ST_SW_SESSION_TICKET_B;
- return ssl_do_write(ssl);
- }
-
- /* Grow buffer if need be: the length calculation is as follows:
- * handshake_header_length + 4 (ticket lifetime hint) + 2 (ticket length) +
- * max_ticket_overhead + * session_length */
- if (!BUF_MEM_grow(ssl->init_buf, SSL_HM_HEADER_LENGTH(ssl) + 6 +
- max_ticket_overhead + session_len)) {
- goto err;
- }
- p = ssl_handshake_start(ssl);
- /* Initialize HMAC and cipher contexts. If callback present it does all the
- * work otherwise use generated values from parent ctx. */
- if (tctx->tlsext_ticket_key_cb) {
- if (tctx->tlsext_ticket_key_cb(ssl, key_name, iv, &ctx, &hctx,
- 1 /* encrypt */) < 0) {
- goto err;
- }
- } else {
- if (!RAND_bytes(iv, 16) ||
- !EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL,
- tctx->tlsext_tick_aes_key, iv) ||
- !HMAC_Init_ex(&hctx, tctx->tlsext_tick_hmac_key, 16, tlsext_tick_md(),
- NULL)) {
- goto err;
- }
- memcpy(key_name, tctx->tlsext_tick_key_name, 16);
- }
-
- /* Ticket lifetime hint (advisory only): We leave this unspecified for
- * resumed session (for simplicity), and guess that tickets for new
- * sessions will live as long as their sessions. */
- l2n(ssl->hit ? 0 : ssl->session->timeout, p);
-
- /* Skip ticket length for now */
- p += 2;
- /* Output key name */
- macstart = p;
- memcpy(p, key_name, 16);
- p += 16;
- /* output IV */
- memcpy(p, iv, EVP_CIPHER_CTX_iv_length(&ctx));
- p += EVP_CIPHER_CTX_iv_length(&ctx);
- /* Encrypt session data */
- if (!EVP_EncryptUpdate(&ctx, p, &len, session, session_len)) {
- goto err;
- }
- p += len;
- if (!EVP_EncryptFinal_ex(&ctx, p, &len)) {
- goto err;
- }
- p += len;
-
- if (!HMAC_Update(&hctx, macstart, p - macstart) ||
- !HMAC_Final(&hctx, p, &hlen)) {
- goto err;
- }
-
- p += hlen;
- /* Now write out lengths: p points to end of data written */
- /* Total length */
- len = p - ssl_handshake_start(ssl);
- /* Skip ticket lifetime hint */
- p = ssl_handshake_start(ssl) + 4;
- s2n(len - 6, p);
- if (!ssl_set_handshake_header(ssl, SSL3_MT_NEW_SESSION_TICKET, len)) {
- goto err;
- }
- ssl->state = SSL3_ST_SW_SESSION_TICKET_B;
+ if (ssl->state == SSL3_ST_SW_SESSION_TICKET_B) {
+ return ssl->method->write_message(ssl);
}
- /* SSL3_ST_SW_SESSION_TICKET_B */
- ret = ssl_do_write(ssl);
+ /* Serialize the SSL_SESSION to be encoded into the ticket. */
+ uint8_t *session = NULL;
+ size_t session_len;
+ if (!SSL_SESSION_to_bytes_for_ticket(ssl->session, &session,
+ &session_len)) {
+ return -1;
+ }
+
+ EVP_CIPHER_CTX ctx;
+ EVP_CIPHER_CTX_init(&ctx);
+ HMAC_CTX hctx;
+ HMAC_CTX_init(&hctx);
+
+ int ret = -1;
+ CBB cbb, body, ticket;
+ if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_NEW_SESSION_TICKET) ||
+ /* Ticket lifetime hint (advisory only): We leave this unspecified for
+ * resumed session (for simplicity), and guess that tickets for new
+ * sessions will live as long as their sessions. */
+ !CBB_add_u32(&body, ssl->hit ? 0 : ssl->session->timeout) ||
+ !CBB_add_u16_length_prefixed(&body, &ticket)) {
+ goto err;
+ }
+
+ /* If the session is too long, emit a dummy value rather than abort the
+ * connection. */
+ const size_t max_ticket_overhead =
+ 16 + EVP_MAX_IV_LENGTH + EVP_MAX_BLOCK_LENGTH + EVP_MAX_MD_SIZE;
+ if (session_len > 0xffff - max_ticket_overhead) {
+ static const char kTicketPlaceholder[] = "TICKET TOO LARGE";
+
+ if (!CBB_add_bytes(&ticket, (const uint8_t *)kTicketPlaceholder,
+ strlen(kTicketPlaceholder)) ||
+ !ssl->method->finish_message(ssl, &cbb)) {
+ goto err;
+ }
+
+ ssl->state = SSL3_ST_SW_SESSION_TICKET_B;
+ ret = 1;
+ goto err;
+ }
+
+ /* Initialize HMAC and cipher contexts. If callback present it does all the
+ * work otherwise use generated values from parent ctx. */
+ SSL_CTX *tctx = ssl->initial_ctx;
+ uint8_t iv[EVP_MAX_IV_LENGTH];
+ uint8_t key_name[16];
+ if (tctx->tlsext_ticket_key_cb != NULL) {
+ if (tctx->tlsext_ticket_key_cb(ssl, key_name, iv, &ctx, &hctx,
+ 1 /* encrypt */) < 0) {
+ goto err;
+ }
+ } else {
+ if (!RAND_bytes(iv, 16) ||
+ !EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL,
+ tctx->tlsext_tick_aes_key, iv) ||
+ !HMAC_Init_ex(&hctx, tctx->tlsext_tick_hmac_key, 16, tlsext_tick_md(),
+ NULL)) {
+ goto err;
+ }
+ memcpy(key_name, tctx->tlsext_tick_key_name, 16);
+ }
+
+ uint8_t *ptr;
+ if (!CBB_add_bytes(&ticket, key_name, 16) ||
+ !CBB_add_bytes(&ticket, iv, EVP_CIPHER_CTX_iv_length(&ctx)) ||
+ !CBB_reserve(&ticket, &ptr, session_len + EVP_MAX_BLOCK_LENGTH)) {
+ goto err;
+ }
+
+ int len;
+ size_t total = 0;
+ if (!EVP_EncryptUpdate(&ctx, ptr + total, &len, session, session_len)) {
+ goto err;
+ }
+ total += len;
+ if (!EVP_EncryptFinal_ex(&ctx, ptr + total, &len)) {
+ goto err;
+ }
+ total += len;
+ if (!CBB_did_write(&ticket, total)) {
+ goto err;
+ }
+
+ unsigned hlen;
+ if (!HMAC_Update(&hctx, CBB_data(&ticket), CBB_len(&ticket)) ||
+ !CBB_reserve(&ticket, &ptr, EVP_MAX_MD_SIZE) ||
+ !HMAC_Final(&hctx, ptr, &hlen) ||
+ !CBB_did_write(&ticket, hlen) ||
+ !ssl->method->finish_message(ssl, &cbb)) {
+ goto err;
+ }
+
+ ssl->state = SSL3_ST_SW_SESSION_TICKET_B;
+ ret = ssl->method->write_message(ssl);
err:
OPENSSL_free(session);