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/include/openssl/ssl.h b/include/openssl/ssl.h
index a768fd7..6ce9412 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -4140,6 +4140,10 @@
* version. */
const SSL3_ENC_METHOD *enc_method;
+ /* pending_message is the current outgoing handshake message. */
+ uint8_t *pending_message;
+ uint32_t pending_message_len;
+
/* State pertaining to the pending handshake.
*
* TODO(davidben): State is current spread all over the place. Move
diff --git a/ssl/d1_both.c b/ssl/d1_both.c
index f021d0a..d6f4e7b 100644
--- a/ssl/d1_both.c
+++ b/ssl/d1_both.c
@@ -305,9 +305,16 @@
return 1;
}
-int dtls1_do_handshake_write(SSL *ssl, size_t *out_offset, const uint8_t *in,
- size_t offset, size_t len,
- enum dtls1_use_epoch_t use_epoch) {
+/* dtls1_do_handshake_write writes handshake message |in| using the given epoch,
+ * starting |offset| bytes into the message body. It returns one on success. On
+ * error, it returns <= 0 and sets |*out_offset| to the number of bytes of body
+ * that were successfully written. This may be used to retry the write
+ * later. |in| must be a reassembled handshake message with the full DTLS
+ * handshake header. */
+static int dtls1_do_handshake_write(SSL *ssl, size_t *out_offset,
+ const uint8_t *in, size_t offset,
+ size_t len,
+ enum dtls1_use_epoch_t use_epoch) {
dtls1_update_mtu(ssl);
int ret = -1;
@@ -404,6 +411,108 @@
return ret;
}
+void dtls_clear_outgoing_messages(SSL *ssl) {
+ size_t i;
+ for (i = 0; i < ssl->d1->outgoing_messages_len; i++) {
+ OPENSSL_free(ssl->d1->outgoing_messages[i].data);
+ ssl->d1->outgoing_messages[i].data = NULL;
+ }
+ ssl->d1->outgoing_messages_len = 0;
+}
+
+/* dtls1_buffer_change_cipher_spec adds a ChangeCipherSpec to the current
+ * handshake flight. */
+static int dtls1_buffer_change_cipher_spec(SSL *ssl) {
+ if (ssl->d1->outgoing_messages_len >= SSL_MAX_HANDSHAKE_FLIGHT) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+
+ DTLS_OUTGOING_MESSAGE *msg =
+ &ssl->d1->outgoing_messages[ssl->d1->outgoing_messages_len];
+ msg->data = NULL;
+ msg->len = 0;
+ msg->epoch = ssl->d1->w_epoch;
+ msg->is_ccs = 1;
+
+ ssl->d1->outgoing_messages_len++;
+ return 1;
+}
+
+static int dtls1_buffer_message(SSL *ssl, uint8_t *data, size_t len) {
+ if (ssl->d1->outgoing_messages_len >= SSL_MAX_HANDSHAKE_FLIGHT) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+ OPENSSL_free(data);
+ return 0;
+ }
+
+ DTLS_OUTGOING_MESSAGE *msg =
+ &ssl->d1->outgoing_messages[ssl->d1->outgoing_messages_len];
+ msg->data = data;
+ msg->len = len;
+ msg->epoch = ssl->d1->w_epoch;
+ msg->is_ccs = 0;
+
+ ssl->d1->outgoing_messages_len++;
+ return 1;
+}
+
+int dtls1_init_message(SSL *ssl, CBB *cbb, CBB *body, uint8_t type) {
+ /* Pick a modest size hint to save most of the |realloc| calls. */
+ if (!CBB_init(cbb, 64) ||
+ !CBB_add_u8(cbb, type) ||
+ !CBB_add_u24(cbb, 0 /* length (filled in later) */) ||
+ !CBB_add_u16(cbb, ssl->d1->handshake_write_seq) ||
+ !CBB_add_u24(cbb, 0 /* offset */) ||
+ !CBB_add_u24_length_prefixed(cbb, body)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+int dtls1_finish_message(SSL *ssl, CBB *cbb) {
+ uint8_t *msg = NULL;
+ size_t len;
+ if (!CBB_finish(cbb, &msg, &len) ||
+ len > 0xffffffffu ||
+ len < DTLS1_HM_HEADER_LENGTH) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+ OPENSSL_free(msg);
+ return 0;
+ }
+
+ /* Fix up the header. Copy the fragment length into the total message
+ * length. */
+ memcpy(msg + 1, msg + DTLS1_HM_HEADER_LENGTH - 3, 3);
+
+ ssl3_update_handshake_hash(ssl, msg, len);
+
+ ssl->d1->handshake_write_seq++;
+ ssl->init_off = 0;
+ return dtls1_buffer_message(ssl, msg, len);
+}
+
+int dtls1_write_message(SSL *ssl) {
+ if (ssl->d1->outgoing_messages_len == 0) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+ return -1;
+ }
+
+ const DTLS_OUTGOING_MESSAGE *msg =
+ &ssl->d1->outgoing_messages[ssl->d1->outgoing_messages_len - 1];
+ if (msg->is_ccs) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+ return -1;
+ }
+
+ size_t offset = ssl->init_off;
+ int ret = dtls1_do_handshake_write(ssl, &offset, msg->data, offset, msg->len,
+ dtls1_use_current_epoch);
+ ssl->init_off = offset;
+ return ret;
+}
+
/* dtls1_is_next_message_complete returns one if the next handshake message is
* complete and zero otherwise. */
static int dtls1_is_next_message_complete(SSL *ssl) {
@@ -693,45 +802,6 @@
return ret;
}
-/* dtls1_buffer_change_cipher_spec adds a ChangeCipherSpec to the current
- * handshake flight. */
-static int dtls1_buffer_change_cipher_spec(SSL *ssl) {
- if (ssl->d1->outgoing_messages_len >= SSL_MAX_HANDSHAKE_FLIGHT) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- return 0;
- }
-
- DTLS_OUTGOING_MESSAGE *msg =
- &ssl->d1->outgoing_messages[ssl->d1->outgoing_messages_len];
- msg->data = NULL;
- msg->len = 0;
- msg->epoch = ssl->d1->w_epoch;
- msg->is_ccs = 1;
-
- ssl->d1->outgoing_messages_len++;
- return 1;
-}
-
-int dtls1_buffer_message(SSL *ssl) {
- if (ssl->d1->outgoing_messages_len >= SSL_MAX_HANDSHAKE_FLIGHT) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- return 0;
- }
-
- DTLS_OUTGOING_MESSAGE *msg =
- &ssl->d1->outgoing_messages[ssl->d1->outgoing_messages_len];
- msg->data = BUF_memdup(ssl->init_buf->data, ssl->init_num);
- if (msg->data == NULL) {
- return 0;
- }
- msg->len = ssl->init_num;
- msg->epoch = ssl->d1->w_epoch;
- msg->is_ccs = 0;
-
- ssl->d1->outgoing_messages_len++;
- return 1;
-}
-
int dtls1_send_change_cipher_spec(SSL *ssl, int a, int b) {
if (ssl->state == a) {
dtls1_buffer_change_cipher_spec(ssl);
@@ -741,15 +811,6 @@
return dtls1_write_change_cipher_spec(ssl, dtls1_use_current_epoch);
}
-void dtls_clear_outgoing_messages(SSL *ssl) {
- size_t i;
- for (i = 0; i < ssl->d1->outgoing_messages_len; i++) {
- OPENSSL_free(ssl->d1->outgoing_messages[i].data);
- ssl->d1->outgoing_messages[i].data = NULL;
- }
- ssl->d1->outgoing_messages_len = 0;
-}
-
void dtls_clear_incoming_messages(SSL *ssl) {
size_t i;
for (i = 0; i < SSL_MAX_HANDSHAKE_FLIGHT; i++) {
diff --git a/ssl/d1_lib.c b/ssl/d1_lib.c
index d738c57..e0853cf 100644
--- a/ssl/d1_lib.c
+++ b/ssl/d1_lib.c
@@ -289,39 +289,6 @@
#endif
}
-int dtls1_set_handshake_header(SSL *ssl, int htype, unsigned long len) {
- uint8_t *message = (uint8_t *)ssl->init_buf->data;
-
- uint16_t seq = ssl->d1->handshake_write_seq;
- ssl->d1->handshake_write_seq++;
-
- ssl->init_num = (int)len + DTLS1_HM_HEADER_LENGTH;
- ssl->init_off = 0;
-
- /* Serialize the message header as if it were a single fragment. */
- uint8_t *p = message;
- *p++ = htype;
- l2n3(len, p);
- s2n(seq, p);
- l2n3(0, p);
- l2n3(len, p);
- assert(p == message + DTLS1_HM_HEADER_LENGTH);
-
- /* Buffer the message to handle re-xmits. */
- dtls1_buffer_message(ssl);
-
- return ssl3_update_handshake_hash(ssl, message, ssl->init_num);
-}
-
-int dtls1_handshake_write(SSL *ssl) {
- size_t offset = ssl->init_off;
- int ret = dtls1_do_handshake_write(
- ssl, &offset, (const uint8_t *)ssl->init_buf->data, offset, ssl->init_num,
- dtls1_use_current_epoch);
- ssl->init_off = offset;
- return ret;
-}
-
void dtls1_expect_flight(SSL *ssl) {
dtls1_start_timer(ssl);
}
diff --git a/ssl/d1_meth.c b/ssl/d1_meth.c
index 32a4651..78119a0 100644
--- a/ssl/d1_meth.c
+++ b/ssl/d1_meth.c
@@ -70,9 +70,9 @@
dtls1_write_app_data,
dtls1_dispatch_alert,
dtls1_supports_cipher,
- DTLS1_HM_HEADER_LENGTH,
- dtls1_set_handshake_header,
- dtls1_handshake_write,
+ dtls1_init_message,
+ dtls1_finish_message,
+ dtls1_write_message,
dtls1_send_change_cipher_spec,
dtls1_expect_flight,
dtls1_received_flight,
diff --git a/ssl/handshake_client.c b/ssl/handshake_client.c
index 3b33574..e5c6e2f 100644
--- a/ssl/handshake_client.c
+++ b/ssl/handshake_client.c
@@ -611,7 +611,7 @@
static int ssl3_send_client_hello(SSL *ssl) {
if (ssl->state == SSL3_ST_CW_CLNT_HELLO_B) {
- return ssl_do_write(ssl);
+ return ssl->method->write_message(ssl);
}
/* The handshake buffer is reset on every ClientHello. Notably, in DTLS, we
@@ -662,12 +662,11 @@
int has_session = ssl->session != NULL &&
!ssl->s3->initial_handshake_complete;
- CBB child;
- if (!CBB_init_fixed(&cbb, ssl_handshake_start(ssl),
- ssl->init_buf->max - SSL_HM_HEADER_LENGTH(ssl)) ||
- !CBB_add_u16(&cbb, ssl->client_version) ||
- !CBB_add_bytes(&cbb, ssl->s3->client_random, SSL3_RANDOM_SIZE) ||
- !CBB_add_u8_length_prefixed(&cbb, &child) ||
+ CBB body, child;
+ if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CLIENT_HELLO) ||
+ !CBB_add_u16(&body, ssl->client_version) ||
+ !CBB_add_bytes(&body, ssl->s3->client_random, SSL3_RANDOM_SIZE) ||
+ !CBB_add_u8_length_prefixed(&body, &child) ||
(has_session &&
!CBB_add_bytes(&child, ssl->session->session_id,
ssl->session->session_id_length))) {
@@ -675,25 +674,24 @@
}
if (SSL_IS_DTLS(ssl)) {
- if (!CBB_add_u8_length_prefixed(&cbb, &child) ||
+ if (!CBB_add_u8_length_prefixed(&body, &child) ||
!CBB_add_bytes(&child, ssl->d1->cookie, ssl->d1->cookie_len)) {
goto err;
}
}
- size_t length;
- if (!ssl3_write_client_cipher_list(ssl, &cbb) ||
- !CBB_add_u8(&cbb, 1 /* one compression method */) ||
- !CBB_add_u8(&cbb, 0 /* null compression */) ||
- !ssl_add_clienthello_tlsext(ssl, &cbb,
- CBB_len(&cbb) + SSL_HM_HEADER_LENGTH(ssl)) ||
- !CBB_finish(&cbb, NULL, &length) ||
- !ssl_set_handshake_header(ssl, SSL3_MT_CLIENT_HELLO, length)) {
+ size_t header_len =
+ SSL_IS_DTLS(ssl) ? DTLS1_HM_HEADER_LENGTH : SSL3_HM_HEADER_LENGTH;
+ if (!ssl3_write_client_cipher_list(ssl, &body) ||
+ !CBB_add_u8(&body, 1 /* one compression method */) ||
+ !CBB_add_u8(&body, 0 /* null compression */) ||
+ !ssl_add_clienthello_tlsext(ssl, &body, header_len + CBB_len(&body)) ||
+ !ssl->method->finish_message(ssl, &cbb)) {
goto err;
}
ssl->state = SSL3_ST_CW_CLNT_HELLO_B;
- return ssl_do_write(ssl);
+ return ssl->method->write_message(ssl);
err:
CBB_cleanup(&cbb);
@@ -1594,10 +1592,10 @@
return 1;
}
- /* In TLS, send an empty Certificate message. */
- uint8_t *p = ssl_handshake_start(ssl);
- l2n3(0, p);
- if (!ssl_set_handshake_header(ssl, SSL3_MT_CERTIFICATE, 3)) {
+ CBB cbb, body;
+ if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CERTIFICATE) ||
+ !CBB_add_u24(&body, 0 /* no certificates */) ||
+ !ssl->method->finish_message(ssl, &cbb)) {
return -1;
}
} else if (!ssl3_output_cert_chain(ssl)) {
@@ -1607,7 +1605,7 @@
}
assert(ssl->state == SSL3_ST_CW_CERT_D);
- return ssl_do_write(ssl);
+ return ssl->method->write_message(ssl);
}
OPENSSL_COMPILE_ASSERT(sizeof(size_t) >= sizeof(unsigned),
@@ -1615,15 +1613,15 @@
static int ssl3_send_client_key_exchange(SSL *ssl) {
if (ssl->state == SSL3_ST_CW_KEY_EXCH_B) {
- return ssl_do_write(ssl);
+ return ssl->method->write_message(ssl);
}
assert(ssl->state == SSL3_ST_CW_KEY_EXCH_A);
uint8_t *pms = NULL;
size_t pms_len = 0;
- CBB cbb;
- if (!CBB_init_fixed(&cbb, ssl_handshake_start(ssl),
- ssl->init_buf->max - SSL_HM_HEADER_LENGTH(ssl))) {
+ CBB cbb, body;
+ if (!ssl->method->init_message(ssl, &cbb, &body,
+ SSL3_MT_CLIENT_KEY_EXCHANGE)) {
goto err;
}
@@ -1660,10 +1658,10 @@
/* Write out psk_identity. */
CBB child;
- if (!CBB_add_u16_length_prefixed(&cbb, &child) ||
+ if (!CBB_add_u16_length_prefixed(&body, &child) ||
!CBB_add_bytes(&child, (const uint8_t *)identity,
OPENSSL_strnlen(identity, sizeof(identity))) ||
- !CBB_flush(&cbb)) {
+ !CBB_flush(&body)) {
goto err;
}
}
@@ -1698,11 +1696,11 @@
goto err;
}
- CBB child, *enc_pms = &cbb;
+ CBB child, *enc_pms = &body;
size_t enc_pms_len;
/* In TLS, there is a length prefix. */
if (ssl->version > SSL3_VERSION) {
- if (!CBB_add_u16_length_prefixed(&cbb, &child)) {
+ if (!CBB_add_u16_length_prefixed(&body, &child)) {
goto err;
}
enc_pms = &child;
@@ -1715,13 +1713,13 @@
/* Log the premaster secret, if logging is enabled. */
!ssl_log_rsa_client_key_exchange(ssl, ptr, enc_pms_len, pms, pms_len) ||
!CBB_did_write(enc_pms, enc_pms_len) ||
- !CBB_flush(&cbb)) {
+ !CBB_flush(&body)) {
goto err;
}
} else if (alg_k & (SSL_kECDHE|SSL_kDHE|SSL_kCECPQ1)) {
/* Generate a keypair and serialize the public half. */
CBB child;
- if (!SSL_ECDH_CTX_add_key(&ssl->s3->tmp.ecdh_ctx, &cbb, &child)) {
+ if (!SSL_ECDH_CTX_add_key(&ssl->s3->tmp.ecdh_ctx, &body, &child)) {
goto err;
}
@@ -1733,7 +1731,7 @@
ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
goto err;
}
- if (!CBB_flush(&cbb)) {
+ if (!CBB_flush(&body)) {
goto err;
}
@@ -1783,9 +1781,7 @@
/* The message must be added to the finished hash before calculating the
* master secret. */
- size_t length;
- if (!CBB_finish(&cbb, NULL, &length) ||
- !ssl_set_handshake_header(ssl, SSL3_MT_CLIENT_KEY_EXCHANGE, length)) {
+ if (!ssl->method->finish_message(ssl, &cbb)) {
goto err;
}
ssl->state = SSL3_ST_CW_KEY_EXCH_B;
@@ -1799,8 +1795,7 @@
OPENSSL_cleanse(pms, pms_len);
OPENSSL_free(pms);
- /* SSL3_ST_CW_KEY_EXCH_B */
- return ssl_do_write(ssl);
+ return ssl->method->write_message(ssl);
err:
CBB_cleanup(&cbb);
@@ -1813,14 +1808,14 @@
static int ssl3_send_cert_verify(SSL *ssl) {
if (ssl->state == SSL3_ST_CW_CERT_VRFY_C) {
- return ssl_do_write(ssl);
+ return ssl->method->write_message(ssl);
}
assert(ssl_has_private_key(ssl));
- CBB cbb, child;
- if (!CBB_init_fixed(&cbb, ssl_handshake_start(ssl),
- ssl->init_buf->max - SSL_HM_HEADER_LENGTH(ssl))) {
+ CBB cbb, body, child;
+ if (!ssl->method->init_message(ssl, &cbb, &body,
+ SSL3_MT_CERTIFICATE_VERIFY)) {
goto err;
}
@@ -1828,7 +1823,7 @@
const EVP_MD *md = NULL;
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);
goto err;
}
@@ -1837,7 +1832,7 @@
/* Set aside 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;
}
@@ -1877,15 +1872,13 @@
goto err;
}
- size_t length;
if (!CBB_did_write(&child, sig_len) ||
- !CBB_finish(&cbb, NULL, &length) ||
- !ssl_set_handshake_header(ssl, SSL3_MT_CERTIFICATE_VERIFY, length)) {
+ !ssl->method->finish_message(ssl, &cbb)) {
goto err;
}
ssl->state = SSL3_ST_CW_CERT_VRFY_C;
- return ssl_do_write(ssl);
+ return ssl->method->write_message(ssl);
err:
CBB_cleanup(&cbb);
@@ -1894,7 +1887,7 @@
static int ssl3_send_next_proto(SSL *ssl) {
if (ssl->state == SSL3_ST_CW_NEXT_PROTO_B) {
- return ssl_do_write(ssl);
+ return ssl->method->write_message(ssl);
}
assert(ssl->state == SSL3_ST_CW_NEXT_PROTO_A);
@@ -1902,30 +1895,26 @@
static const uint8_t kZero[32] = {0};
size_t padding_len = 32 - ((ssl->s3->next_proto_negotiated_len + 2) % 32);
- CBB cbb, child;
- 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_u8_length_prefixed(&cbb, &child) ||
+ CBB cbb, body, child;
+ if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_NEXT_PROTO) ||
+ !CBB_add_u8_length_prefixed(&body, &child) ||
!CBB_add_bytes(&child, ssl->s3->next_proto_negotiated,
ssl->s3->next_proto_negotiated_len) ||
- !CBB_add_u8_length_prefixed(&cbb, &child) ||
+ !CBB_add_u8_length_prefixed(&body, &child) ||
!CBB_add_bytes(&child, kZero, padding_len) ||
- !CBB_finish(&cbb, NULL, &length) ||
- !ssl_set_handshake_header(ssl, SSL3_MT_NEXT_PROTO, length)) {
+ !ssl->method->finish_message(ssl, &cbb)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
CBB_cleanup(&cbb);
return -1;
}
ssl->state = SSL3_ST_CW_NEXT_PROTO_B;
- return ssl_do_write(ssl);
+ return ssl->method->write_message(ssl);
}
static int ssl3_send_channel_id(SSL *ssl) {
if (ssl->state == SSL3_ST_CW_CHANNEL_ID_B) {
- return ssl_do_write(ssl);
+ return ssl->method->write_message(ssl);
}
assert(ssl->state == SSL3_ST_CW_CHANNEL_ID_A);
@@ -1975,27 +1964,22 @@
goto err;
}
- CBB cbb, child;
- 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, TLSEXT_TYPE_channel_id) ||
- !CBB_add_u16_length_prefixed(&cbb, &child) ||
- !BN_bn2cbb_padded(&child, 32, x) ||
- !BN_bn2cbb_padded(&child, 32, y) ||
+ CBB cbb, body, child;
+ if (!ssl->method->init_message(ssl, &cbb, &body,
+ SSL3_MT_CHANNEL_ID_ENCRYPTED_EXTENSIONS) ||
+ !CBB_add_u16(&body, TLSEXT_TYPE_channel_id) ||
+ !CBB_add_u16_length_prefixed(&body, &child) ||
+ !BN_bn2cbb_padded(&child, 32, x) || !BN_bn2cbb_padded(&child, 32, y) ||
!BN_bn2cbb_padded(&child, 32, sig->r) ||
!BN_bn2cbb_padded(&child, 32, sig->s) ||
- !CBB_finish(&cbb, NULL, &length) ||
- !ssl_set_handshake_header(ssl, SSL3_MT_CHANNEL_ID_ENCRYPTED_EXTENSIONS,
- length)) {
+ !ssl->method->finish_message(ssl, &cbb)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
CBB_cleanup(&cbb);
goto err;
}
ssl->state = SSL3_ST_CW_CHANNEL_ID_B;
- ret = ssl_do_write(ssl);
+ ret = ssl->method->write_message(ssl);
err:
BN_free(x);
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);
diff --git a/ssl/internal.h b/ssl/internal.h
index 369bdcc..710fdd3 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -723,29 +723,17 @@
void ssl_write_buffer_clear(SSL *ssl);
+/* Certificate functions. */
+
+/* ssl_add_cert_to_cbb adds |x509| to |cbb|. It returns one on success and zero
+ * on error. */
+int ssl_add_cert_to_cbb(CBB *cbb, X509 *x509);
+
+
/* Underdocumented functions.
*
* Functions below here haven't been touched up and may be underdocumented. */
-#define l2n(l, c) \
- (*((c)++) = (uint8_t)(((l) >> 24) & 0xff), \
- *((c)++) = (uint8_t)(((l) >> 16) & 0xff), \
- *((c)++) = (uint8_t)(((l) >> 8) & 0xff), \
- *((c)++) = (uint8_t)(((l)) & 0xff))
-
-#define s2n(s, c) \
- ((c[0] = (uint8_t)(((s) >> 8) & 0xff), \
- c[1] = (uint8_t)(((s)) & 0xff)), \
- c += 2)
-
-#define l2n3(l, c) \
- ((c[0] = (uint8_t)(((l) >> 16) & 0xff), \
- c[1] = (uint8_t)(((l) >> 8) & 0xff), \
- c[2] = (uint8_t)(((l)) & 0xff)), \
- c += 3)
-
-/* LOCAL STUFF */
-
#define TLSEXT_CHANNEL_ID_SIZE 128
/* Check if an SSL structure is using DTLS */
@@ -839,12 +827,16 @@
/* supports_cipher returns one if |cipher| is supported by this protocol and
* zero otherwise. */
int (*supports_cipher)(const SSL_CIPHER *cipher);
- /* Handshake header length */
- unsigned int hhlen;
- /* Set the handshake header */
- int (*set_handshake_header)(SSL *ssl, int type, unsigned long len);
- /* Write out handshake message */
- int (*do_write)(SSL *ssl);
+ /* init_message begins a new handshake message of type |type|. |cbb| is the
+ * root CBB to be passed into |finish_message|. |*body| is set to a child CBB
+ * the caller should write to. It returns one on success and zero on error. */
+ int (*init_message)(SSL *ssl, CBB *cbb, CBB *body, uint8_t type);
+ /* finish_message finishes a handshake message and prepares it to be
+ * written. It returns one on success and zero on error. */
+ int (*finish_message)(SSL *ssl, CBB *cbb);
+ /* write_message writes the next message to the transport. It returns one on
+ * success and <= 0 on error. */
+ int (*write_message)(SSL *ssl);
/* send_change_cipher_spec sends a ChangeCipherSpec message. */
int (*send_change_cipher_spec)(SSL *ssl, int a, int b);
/* expect_flight is called when the handshake expects a flight of messages from
@@ -870,13 +862,6 @@
int (*cert_verify_mac)(SSL *, int, uint8_t *);
};
-#define SSL_HM_HEADER_LENGTH(ssl) ssl->method->hhlen
-#define ssl_handshake_start(ssl) \
- (((uint8_t *)ssl->init_buf->data) + ssl->method->hhlen)
-#define ssl_set_handshake_header(ssl, htype, len) \
- ssl->method->set_handshake_header(ssl, htype, len)
-#define ssl_do_write(ssl) ssl->method->do_write(ssl)
-
/* lengths of messages */
#define DTLS1_COOKIE_LENGTH 256
@@ -988,7 +973,7 @@
int (*cb)(SSL *ssl, void *arg), void *arg);
int ssl_verify_cert_chain(SSL *ssl, STACK_OF(X509) *cert_chain);
-int ssl_add_cert_chain(SSL *ssl, unsigned long *l);
+int ssl_add_cert_chain(SSL *ssl, CBB *cbb);
void ssl_update_cache(SSL *ssl, int mode);
/* ssl_get_compatible_server_ciphers determines the key exchange and
@@ -1008,9 +993,7 @@
int ssl3_get_finished(SSL *ssl);
int ssl3_send_change_cipher_spec(SSL *ssl, int state_a, int state_b);
void ssl3_cleanup_key_block(SSL *ssl);
-int ssl3_do_write(SSL *ssl, int type);
int ssl3_send_alert(SSL *ssl, int level, int desc);
-int ssl3_get_req_cert_type(SSL *ssl, uint8_t *p);
long ssl3_get_message(SSL *ssl, int msg_type,
enum ssl_hash_message_t hash_message, int *ok);
@@ -1046,20 +1029,16 @@
int ssl3_accept(SSL *ssl);
int ssl3_connect(SSL *ssl);
-int ssl3_set_handshake_header(SSL *ssl, int htype, unsigned long len);
-int ssl3_handshake_write(SSL *ssl);
+int ssl3_init_message(SSL *ssl, CBB *cbb, CBB *body, uint8_t type);
+int ssl3_finish_message(SSL *ssl, CBB *cbb);
+int ssl3_write_message(SSL *ssl);
+
void ssl3_expect_flight(SSL *ssl);
void ssl3_received_flight(SSL *ssl);
-/* dtls1_do_handshake_write writes handshake message |in| using the give epoch,
- * starting |offset| bytes into the message body. It returns one on success. On
- * error, it returns <= 0 and sets |*out_offset| to the number of bytes of body
- * which were successfully written. This may be used to retry the write
- * later. |in| must be a reassembled handshake message with the full DTLS
- * handshake header. */
-int dtls1_do_handshake_write(SSL *ssl, size_t *out_offset, const uint8_t *in,
- size_t offset, size_t len,
- enum dtls1_use_epoch_t use_epoch);
+int dtls1_init_message(SSL *ssl, CBB *cbb, CBB *body, uint8_t type);
+int dtls1_finish_message(SSL *ssl, CBB *cbb);
+int dtls1_write_message(SSL *ssl);
/* dtls1_get_record reads a new input record. On success, it places it in
* |ssl->s3->rrec| and returns one. Otherwise it returns <= 0 on error or if
@@ -1079,13 +1058,11 @@
int dtls1_send_change_cipher_spec(SSL *ssl, int a, int b);
int dtls1_send_finished(SSL *ssl, int a, int b, const char *sender, int slen);
-int dtls1_buffer_message(SSL *ssl);
int dtls1_retransmit_buffered_messages(SSL *ssl);
void dtls1_clear_record_buffer(SSL *ssl);
int dtls1_parse_fragment(CBS *cbs, struct hm_header_st *out_hdr,
CBS *out_body);
int dtls1_check_timeout_num(SSL *ssl);
-int dtls1_set_handshake_header(SSL *ssl, int type, unsigned long len);
int dtls1_handshake_write(SSL *ssl);
void dtls1_expect_flight(SSL *ssl);
void dtls1_received_flight(SSL *ssl);
diff --git a/ssl/s3_both.c b/ssl/s3_both.c
index f081066..1bdc2a2 100644
--- a/ssl/s3_both.c
+++ b/ssl/s3_both.c
@@ -117,6 +117,7 @@
#include <string.h>
#include <openssl/buf.h>
+#include <openssl/bytestring.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/mem.h>
@@ -132,61 +133,118 @@
/* ssl3_do_write sends |ssl->init_buf| in records of type 'type'
* (SSL3_RT_HANDSHAKE or SSL3_RT_CHANGE_CIPHER_SPEC). It returns 1 on success
* and <= 0 on error. */
-int ssl3_do_write(SSL *ssl, int type) {
- int ret = ssl3_write_bytes(ssl, type, ssl->init_buf->data, ssl->init_num);
+static int ssl3_do_write(SSL *ssl, int type, const uint8_t *data, size_t len) {
+ int ret = ssl3_write_bytes(ssl, type, data, len);
if (ret <= 0) {
return ret;
}
/* ssl3_write_bytes writes the data in its entirety. */
- assert(ret == ssl->init_num);
- ssl_do_msg_callback(ssl, 1 /* write */, ssl->version, type,
- ssl->init_buf->data, (size_t)ssl->init_num);
- ssl->init_num = 0;
+ assert((size_t)ret == len);
+ ssl_do_msg_callback(ssl, 1 /* write */, ssl->version, type, data, len);
+ return 1;
+}
+
+int ssl3_init_message(SSL *ssl, CBB *cbb, CBB *body, uint8_t type) {
+ CBB_zero(cbb);
+ if (ssl->s3->pending_message != NULL) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+
+ /* Pick a modest size hint to save most of the |realloc| calls. */
+ if (!CBB_init(cbb, 64) ||
+ !CBB_add_u8(cbb, type) ||
+ !CBB_add_u24_length_prefixed(cbb, body)) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+
+ return 1;
+}
+
+int ssl3_finish_message(SSL *ssl, CBB *cbb) {
+ if (ssl->s3->pending_message != NULL) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+
+ uint8_t *msg = NULL;
+ size_t len;
+ if (!CBB_finish(cbb, &msg, &len) ||
+ len > 0xffffffffu) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+ OPENSSL_free(msg);
+ return 0;
+ }
+
+ ssl3_update_handshake_hash(ssl, msg, len);
+
+ ssl->s3->pending_message = msg;
+ ssl->s3->pending_message_len = (uint32_t)len;
+ return 1;
+}
+
+int ssl3_write_message(SSL *ssl) {
+ if (ssl->s3->pending_message == NULL) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+
+ int ret = ssl3_do_write(ssl, SSL3_RT_HANDSHAKE, ssl->s3->pending_message,
+ ssl->s3->pending_message_len);
+ if (ret <= 0) {
+ return ret;
+ }
+
+ OPENSSL_free(ssl->s3->pending_message);
+ ssl->s3->pending_message = NULL;
+ ssl->s3->pending_message_len = 0;
return 1;
}
int ssl3_send_finished(SSL *ssl, int a, int b) {
- uint8_t *p;
- int n;
-
- if (ssl->state == a) {
- p = ssl_handshake_start(ssl);
-
- n = ssl->s3->enc_method->final_finish_mac(ssl, ssl->server,
- ssl->s3->tmp.finish_md);
- if (n == 0) {
- return 0;
- }
- ssl->s3->tmp.finish_md_len = n;
- memcpy(p, ssl->s3->tmp.finish_md, n);
-
- /* Log the master secret, if logging is enabled. */
- if (!ssl_log_master_secret(ssl, ssl->s3->client_random, SSL3_RANDOM_SIZE,
- ssl->session->master_key,
- ssl->session->master_key_length)) {
- return 0;
- }
-
- /* Copy the finished so we can use it for renegotiation checks */
- if (ssl->server) {
- assert(n <= EVP_MAX_MD_SIZE);
- memcpy(ssl->s3->previous_server_finished, ssl->s3->tmp.finish_md, n);
- ssl->s3->previous_server_finished_len = n;
- } else {
- assert(n <= EVP_MAX_MD_SIZE);
- memcpy(ssl->s3->previous_client_finished, ssl->s3->tmp.finish_md, n);
- ssl->s3->previous_client_finished_len = n;
- }
-
- if (!ssl_set_handshake_header(ssl, SSL3_MT_FINISHED, n)) {
- return 0;
- }
- ssl->state = b;
+ if (ssl->state == b) {
+ return ssl->method->write_message(ssl);
}
- /* SSL3_ST_SEND_xxxxxx_HELLO_B */
- return ssl_do_write(ssl);
+ int n = ssl->s3->enc_method->final_finish_mac(ssl, ssl->server,
+ ssl->s3->tmp.finish_md);
+ if (n == 0) {
+ return 0;
+ }
+ ssl->s3->tmp.finish_md_len = n;
+
+ /* Log the master secret, if logging is enabled. */
+ if (!ssl_log_master_secret(ssl, ssl->s3->client_random, SSL3_RANDOM_SIZE,
+ ssl->session->master_key,
+ ssl->session->master_key_length)) {
+ return 0;
+ }
+
+ /* Copy the finished so we can use it for renegotiation checks */
+ if (ssl->server) {
+ assert(n <= EVP_MAX_MD_SIZE);
+ memcpy(ssl->s3->previous_server_finished, ssl->s3->tmp.finish_md, n);
+ ssl->s3->previous_server_finished_len = n;
+ } else {
+ assert(n <= EVP_MAX_MD_SIZE);
+ memcpy(ssl->s3->previous_client_finished, ssl->s3->tmp.finish_md, n);
+ ssl->s3->previous_client_finished_len = n;
+ }
+
+ CBB cbb, body;
+ if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_FINISHED) ||
+ !CBB_add_bytes(&body, ssl->s3->tmp.finish_md,
+ ssl->s3->tmp.finish_md_len) ||
+ !ssl->method->finish_message(ssl, &cbb)) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+ CBB_cleanup(&cbb);
+ return -1;
+ }
+
+ ssl->state = b;
+ return ssl->method->write_message(ssl);
}
/* ssl3_take_mac calculates the Finished MAC for the handshakes messages seen
@@ -262,30 +320,24 @@
}
int ssl3_send_change_cipher_spec(SSL *ssl, int a, int b) {
- if (ssl->state == a) {
- *((uint8_t *)ssl->init_buf->data) = SSL3_MT_CCS;
- ssl->init_num = 1;
+ static const uint8_t kChangeCipherSpec[1] = {SSL3_MT_CCS};
- ssl->state = b;
- }
-
- /* SSL3_ST_CW_CHANGE_B */
- return ssl3_do_write(ssl, SSL3_RT_CHANGE_CIPHER_SPEC);
+ ssl->state = b;
+ return ssl3_do_write(ssl, SSL3_RT_CHANGE_CIPHER_SPEC, kChangeCipherSpec,
+ sizeof(kChangeCipherSpec));
}
int ssl3_output_cert_chain(SSL *ssl) {
- uint8_t *p;
- unsigned long l = 3 + SSL_HM_HEADER_LENGTH(ssl);
-
- if (!ssl_add_cert_chain(ssl, &l)) {
+ CBB cbb, body;
+ if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CERTIFICATE) ||
+ !ssl_add_cert_chain(ssl, &body) ||
+ !ssl->method->finish_message(ssl, &cbb)) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+ CBB_cleanup(&cbb);
return 0;
}
- l -= 3 + SSL_HM_HEADER_LENGTH(ssl);
- p = ssl_handshake_start(ssl);
- l2n3(l, p);
- l += 3;
- return ssl_set_handshake_header(ssl, SSL3_MT_CERTIFICATE, l);
+ return 1;
}
size_t ssl_max_handshake_message_len(const SSL *ssl) {
diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c
index f194267..3827f15 100644
--- a/ssl/s3_lib.c
+++ b/ssl/s3_lib.c
@@ -166,21 +166,6 @@
return 1;
}
-int ssl3_set_handshake_header(SSL *ssl, int htype, unsigned long len) {
- uint8_t *p = (uint8_t *)ssl->init_buf->data;
- *(p++) = htype;
- l2n3(len, p);
- ssl->init_num = (int)len + SSL3_HM_HEADER_LENGTH;
-
- /* Add the message to the handshake hash. */
- return ssl3_update_handshake_hash(ssl, (uint8_t *)ssl->init_buf->data,
- ssl->init_num);
-}
-
-int ssl3_handshake_write(SSL *ssl) {
- return ssl3_do_write(ssl, SSL3_RT_HANDSHAKE);
-}
-
void ssl3_expect_flight(SSL *ssl) {}
void ssl3_received_flight(SSL *ssl) {}
@@ -232,6 +217,7 @@
OPENSSL_free(ssl->s3->alpn_selected);
SSL_AEAD_CTX_free(ssl->s3->aead_read_ctx);
SSL_AEAD_CTX_free(ssl->s3->aead_write_ctx);
+ OPENSSL_free(ssl->s3->pending_message);
OPENSSL_cleanse(ssl->s3, sizeof *ssl->s3);
OPENSSL_free(ssl->s3);
@@ -331,40 +317,6 @@
return ret;
}
-int ssl3_get_req_cert_type(SSL *ssl, uint8_t *p) {
- int ret = 0;
- const uint8_t *sig;
- size_t i, siglen;
- int have_rsa_sign = 0;
- int have_ecdsa_sign = 0;
-
- /* get configured sigalgs */
- siglen = tls12_get_psigalgs(ssl, &sig);
- for (i = 0; i < siglen; i += 2, sig += 2) {
- switch (sig[1]) {
- case TLSEXT_signature_rsa:
- have_rsa_sign = 1;
- break;
-
- case TLSEXT_signature_ecdsa:
- have_ecdsa_sign = 1;
- break;
- }
- }
-
- if (have_rsa_sign) {
- p[ret++] = SSL3_CT_RSA_SIGN;
- }
-
- /* 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) {
- p[ret++] = TLS_CT_ECDSA_SIGN;
- }
-
- return ret;
-}
-
/* If we are using default SHA1+MD5 algorithms switch to new SHA256 PRF and
* handshake macs if required. */
uint32_t ssl_get_algorithm_prf(const SSL *ssl) {
diff --git a/ssl/s3_meth.c b/ssl/s3_meth.c
index 8370f23..715966b 100644
--- a/ssl/s3_meth.c
+++ b/ssl/s3_meth.c
@@ -70,9 +70,9 @@
ssl3_write_app_data,
ssl3_dispatch_alert,
ssl3_supports_cipher,
- SSL3_HM_HEADER_LENGTH,
- ssl3_set_handshake_header,
- ssl3_handshake_write,
+ ssl3_init_message,
+ ssl3_finish_message,
+ ssl3_write_message,
ssl3_send_change_cipher_spec,
ssl3_expect_flight,
ssl3_received_flight,
diff --git a/ssl/ssl_asn1.c b/ssl/ssl_asn1.c
index 5ec33eb..41987f8 100644
--- a/ssl/ssl_asn1.c
+++ b/ssl/ssl_asn1.c
@@ -165,22 +165,6 @@
static const int kCertChainTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 19;
-static int add_X509(CBB *cbb, X509 *x509) {
- int len = i2d_X509(x509, NULL);
- if (len < 0) {
- return 0;
- }
- uint8_t *buf;
- if (!CBB_add_space(cbb, &buf, len)) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
- return 0;
- }
- if (buf != NULL && i2d_X509(x509, &buf) < 0) {
- return 0;
- }
- return 1;
-}
-
static int SSL_SESSION_to_bytes_full(const SSL_SESSION *in, uint8_t **out_data,
size_t *out_len, int for_ticket) {
CBB cbb, session, child, child2;
@@ -229,7 +213,7 @@
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
goto err;
}
- if (!add_X509(&child, in->peer)) {
+ if (!ssl_add_cert_to_cbb(&child, in->peer)) {
goto err;
}
}
@@ -351,7 +335,7 @@
}
size_t i;
for (i = 0; i < sk_X509_num(in->cert_chain); i++) {
- if (!add_X509(&child, sk_X509_value(in->cert_chain, i))) {
+ if (!ssl_add_cert_to_cbb(&child, sk_X509_value(in->cert_chain, i))) {
goto err;
}
}
diff --git a/ssl/ssl_cert.c b/ssl/ssl_cert.c
index 0eb0d8b..b608c73 100644
--- a/ssl/ssl_cert.c
+++ b/ssl/ssl_cert.c
@@ -416,56 +416,58 @@
return add_client_CA(&ctx->client_CA, x509);
}
-/* Add a certificate to a BUF_MEM structure */
-static int ssl_add_cert_to_buf(BUF_MEM *buf, unsigned long *l, X509 *x) {
- int n;
- uint8_t *p;
-
- n = i2d_X509(x, NULL);
- if (n < 0 || !BUF_MEM_grow_clean(buf, (int)(n + (*l) + 3))) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB);
+int ssl_add_cert_to_cbb(CBB *cbb, X509 *x509) {
+ int len = i2d_X509(x509, NULL);
+ if (len < 0) {
return 0;
}
- p = (uint8_t *)&(buf->data[*l]);
- l2n3(n, p);
- n = i2d_X509(x, &p);
- if (n < 0) {
- /* This shouldn't happen. */
- OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB);
- return 0;
+ uint8_t *buf;
+ if (!CBB_add_space(cbb, &buf, len)) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+ return 0;
}
- *l += n + 3;
-
+ if (buf != NULL && i2d_X509(x509, &buf) < 0) {
+ return 0;
+ }
return 1;
}
-/* Add certificate chain to internal SSL BUF_MEM structure. */
-int ssl_add_cert_chain(SSL *ssl, unsigned long *l) {
+static int ssl_add_cert_with_length(CBB *cbb, X509 *x509) {
+ CBB child;
+ return CBB_add_u24_length_prefixed(cbb, &child) &&
+ ssl_add_cert_to_cbb(&child, x509) &&
+ CBB_flush(cbb);
+}
+
+int ssl_add_cert_chain(SSL *ssl, CBB *cbb) {
CERT *cert = ssl->cert;
- BUF_MEM *buf = ssl->init_buf;
- int no_chain = 0;
- size_t i;
-
X509 *x = cert->x509;
- STACK_OF(X509) *chain = cert->chain;
-
if (x == NULL) {
OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_SET);
return 0;
}
+ CBB child;
+ if (!CBB_add_u24_length_prefixed(cbb, &child)) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+
+ int no_chain = 0;
+ STACK_OF(X509) *chain = cert->chain;
if ((ssl->mode & SSL_MODE_NO_AUTO_CHAIN) || chain != NULL) {
no_chain = 1;
}
if (no_chain) {
- if (!ssl_add_cert_to_buf(buf, l, x)) {
+ if (!ssl_add_cert_to_cbb(&child, x)) {
return 0;
}
+ size_t i;
for (i = 0; i < sk_X509_num(chain); i++) {
x = sk_X509_value(chain, i);
- if (!ssl_add_cert_to_buf(buf, l, x)) {
+ if (!ssl_add_cert_with_length(&child, x)) {
return 0;
}
}
@@ -479,10 +481,11 @@
X509_verify_cert(&xs_ctx);
/* Don't leave errors in the queue */
ERR_clear_error();
+
+ size_t i;
for (i = 0; i < sk_X509_num(xs_ctx.chain); i++) {
x = sk_X509_value(xs_ctx.chain, i);
-
- if (!ssl_add_cert_to_buf(buf, l, x)) {
+ if (!ssl_add_cert_with_length(&child, x)) {
X509_STORE_CTX_cleanup(&xs_ctx);
return 0;
}
@@ -490,7 +493,7 @@
X509_STORE_CTX_cleanup(&xs_ctx);
}
- return 1;
+ return CBB_flush(cbb);
}
static int set_cert_store(X509_STORE **store_ptr, X509_STORE *new_store, int take_ref) {