ソースを参照

Separate CCS and handshake writing in DTLS.

They run through completely different logic as only handshake is fragmented.
This'll make it easier to rewrite the handshake logic in a follow-up.

Change-Id: I9515feafc06bf069b261073873966e72fcbe13cb
Reviewed-on: https://boringssl-review.googlesource.com/6420
Reviewed-by: Adam Langley <agl@google.com>
kris/onging/CECPQ3_patch15
David Benjamin 9年前
committed by Adam Langley
コミット
a97b737fb0
4個のファイルの変更160行の追加133行の削除
  1. +9
    -2
      include/openssl/ssl.h
  2. +144
    -125
      ssl/d1_both.c
  3. +2
    -2
      ssl/d1_lib.c
  4. +5
    -4
      ssl/internal.h

+ 9
- 2
include/openssl/ssl.h ファイルの表示

@@ -3750,8 +3750,15 @@ struct ssl_st {

BIO *rbio; /* used by SSL_read */
BIO *wbio; /* used by SSL_write */
BIO *bbio; /* used during session-id reuse to concatenate
* messages */

/* bbio, if non-NULL, is a buffer placed in front of |wbio| to pack handshake
* messages within one flight into a single |BIO_write|.
*
* TODO(davidben): This does not work right for DTLS. It assumes the MTU is
* smaller than the buffer size so that the buffer's internal flushing never
* kicks in. It also doesn't kick in for DTLS retransmission. Replace this
* with a better mechanism. */
BIO *bbio;

int (*handshake_func)(SSL *);



+ 144
- 125
ssl/d1_both.c ファイルの表示

@@ -251,29 +251,63 @@ static void dtls1_hm_fragment_mark(hm_fragment *frag, size_t start,
frag->reassembly = NULL;
}

/* send s->init_buf in records of type 'type' (SSL3_RT_HANDSHAKE or
* SSL3_RT_CHANGE_CIPHER_SPEC) */
int dtls1_do_write(SSL *s, int type, enum dtls1_use_epoch_t use_epoch) {
int ret;
int curr_mtu;
unsigned int len, frag_off;

/* AHA! Figure out the MTU, and stick to the right size */
if (s->d1->mtu < dtls1_min_mtu() &&
!(SSL_get_options(s) & SSL_OP_NO_QUERY_MTU)) {
long mtu = BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_QUERY_MTU, 0, NULL);
static void dtls1_update_mtu(SSL *ssl) {
/* TODO(davidben): What is this code doing and do we need it? */
if (ssl->d1->mtu < dtls1_min_mtu() &&
!(SSL_get_options(ssl) & SSL_OP_NO_QUERY_MTU)) {
long mtu = BIO_ctrl(SSL_get_wbio(ssl), BIO_CTRL_DGRAM_QUERY_MTU, 0, NULL);
if (mtu >= 0 && mtu <= (1 << 30) && (unsigned)mtu >= dtls1_min_mtu()) {
s->d1->mtu = (unsigned)mtu;
ssl->d1->mtu = (unsigned)mtu;
} else {
s->d1->mtu = kDefaultMTU;
BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SET_MTU, s->d1->mtu, NULL);
ssl->d1->mtu = kDefaultMTU;
BIO_ctrl(SSL_get_wbio(ssl), BIO_CTRL_DGRAM_SET_MTU, ssl->d1->mtu, NULL);
}
}

/* should have something reasonable now */
assert(s->d1->mtu >= dtls1_min_mtu());
/* The MTU should be above the minimum now. */
assert(ssl->d1->mtu >= dtls1_min_mtu());
}

static int dtls1_write_change_cipher_spec(SSL *ssl,
enum dtls1_use_epoch_t use_epoch) {
dtls1_update_mtu(ssl);

if (s->init_off == 0 && type == SSL3_RT_HANDSHAKE) {
/* During the handshake, wbio is buffered to pack messages together. Flush the
* buffer if the ChangeCipherSpec would not fit in a packet. */
if (BIO_wpending(SSL_get_wbio(ssl)) + ssl_max_seal_overhead(ssl) + 1 >
ssl->d1->mtu) {
ssl->rwstate = SSL_WRITING;
int ret = BIO_flush(SSL_get_wbio(ssl));
if (ret <= 0) {
return ret;
}
ssl->rwstate = SSL_NOTHING;
}

static const uint8_t kChangeCipherSpec[1] = {SSL3_MT_CCS};
int ret = dtls1_write_bytes(ssl, SSL3_RT_CHANGE_CIPHER_SPEC, kChangeCipherSpec,
sizeof(kChangeCipherSpec), use_epoch);
if (ret <= 0) {
return ret;
}

if (ssl->msg_callback != NULL) {
ssl->msg_callback(1 /* write */, ssl->version, SSL3_RT_CHANGE_CIPHER_SPEC,
kChangeCipherSpec, sizeof(kChangeCipherSpec), ssl,
ssl->msg_callback_arg);
}

return 1;
}

int dtls1_do_handshake_write(SSL *s, enum dtls1_use_epoch_t use_epoch) {
int ret;
int curr_mtu;
unsigned int len, frag_off;

dtls1_update_mtu(s);

if (s->init_off == 0) {
assert(s->init_num ==
(int)s->d1->w_msg_hdr.msg_len + DTLS1_HM_HEADER_LENGTH);
}
@@ -307,45 +341,34 @@ int dtls1_do_write(SSL *s, int type, enum dtls1_use_epoch_t use_epoch) {
curr_mtu = s->d1->mtu - DTLS1_RT_HEADER_LENGTH - max_overhead;
}

/* XDTLS: this function is too long. split out the CCS part */
if (type == SSL3_RT_HANDSHAKE) {
/* If this isn't the first fragment, reserve space to prepend a new
* fragment header. This will override the body of a previous fragment. */
if (s->init_off != 0) {
assert(s->init_off > DTLS1_HM_HEADER_LENGTH);
s->init_off -= DTLS1_HM_HEADER_LENGTH;
s->init_num += DTLS1_HM_HEADER_LENGTH;
}

if (curr_mtu <= DTLS1_HM_HEADER_LENGTH) {
/* To make forward progress, the MTU must, at minimum, fit the handshake
* header and one byte of handshake body. */
OPENSSL_PUT_ERROR(SSL, SSL_R_MTU_TOO_SMALL);
return -1;
}
/* If this isn't the first fragment, reserve space to prepend a new fragment
* header. This will override the body of a previous fragment. */
if (s->init_off != 0) {
assert(s->init_off > DTLS1_HM_HEADER_LENGTH);
s->init_off -= DTLS1_HM_HEADER_LENGTH;
s->init_num += DTLS1_HM_HEADER_LENGTH;
}

if (s->init_num > curr_mtu) {
len = curr_mtu;
} else {
len = s->init_num;
}
assert(len >= DTLS1_HM_HEADER_LENGTH);
if (curr_mtu <= DTLS1_HM_HEADER_LENGTH) {
/* To make forward progress, the MTU must, at minimum, fit the handshake
* header and one byte of handshake body. */
OPENSSL_PUT_ERROR(SSL, SSL_R_MTU_TOO_SMALL);
return -1;
}

dtls1_fix_message_header(s, frag_off, len - DTLS1_HM_HEADER_LENGTH);
dtls1_write_message_header(
s, (uint8_t *)&s->init_buf->data[s->init_off]);
if (s->init_num > curr_mtu) {
len = curr_mtu;
} else {
assert(type == SSL3_RT_CHANGE_CIPHER_SPEC);
/* ChangeCipherSpec cannot be fragmented. */
if (s->init_num > curr_mtu) {
OPENSSL_PUT_ERROR(SSL, SSL_R_MTU_TOO_SMALL);
return -1;
}
len = s->init_num;
}
assert(len >= DTLS1_HM_HEADER_LENGTH);

ret = dtls1_write_bytes(s, type, &s->init_buf->data[s->init_off], len,
use_epoch);
dtls1_fix_message_header(s, frag_off, len - DTLS1_HM_HEADER_LENGTH);
dtls1_write_message_header(
s, (uint8_t *)&s->init_buf->data[s->init_off]);

ret = dtls1_write_bytes(s, SSL3_RT_HANDSHAKE,
&s->init_buf->data[s->init_off], len, use_epoch);
if (ret < 0) {
return -1;
}
@@ -356,7 +379,9 @@ int dtls1_do_write(SSL *s, int type, enum dtls1_use_epoch_t use_epoch) {

if (ret == s->init_num) {
if (s->msg_callback) {
s->msg_callback(1, s->version, type, s->init_buf->data,
/* TODO(davidben): At this point, |s->init_buf->data| has been clobbered
* already. */
s->msg_callback(1, s->version, SSL3_RT_HANDSHAKE, s->init_buf->data,
(size_t)(s->init_off + s->init_num), s,
s->msg_callback_arg);
}
@@ -644,37 +669,6 @@ err:
return -1;
}

/* for these 2 messages, we need to
* ssl->enc_read_ctx re-init
* ssl->s3->read_sequence zero
* ssl->s3->read_mac_secret re-init
* ssl->session->read_sym_enc assign
* ssl->session->read_compression assign
* ssl->session->read_hash assign */
int dtls1_send_change_cipher_spec(SSL *s, int a, int b) {
uint8_t *p;

if (s->state == a) {
p = (uint8_t *)s->init_buf->data;
*p++ = SSL3_MT_CCS;
s->d1->handshake_write_seq = s->d1->next_handshake_write_seq;
s->init_num = DTLS1_CCS_HEADER_LENGTH;

s->init_off = 0;

dtls1_set_message_header(s, SSL3_MT_CCS, 0, s->d1->handshake_write_seq, 0,
0);

/* buffer the message to handle re-xmits */
dtls1_buffer_message(s, 1);

s->state = b;
}

/* SSL3_ST_CW_CHANGE_B */
return dtls1_do_write(s, SSL3_RT_CHANGE_CIPHER_SPEC, dtls1_use_current_epoch);
}

int dtls1_read_failed(SSL *s, int code) {
if (code > 0) {
assert(0);
@@ -696,7 +690,9 @@ int dtls1_read_failed(SSL *s, int code) {
return DTLSv1_handle_timeout(s);
}

int dtls1_get_queue_priority(unsigned short seq, int is_ccs) {
static uint16_t dtls1_get_queue_priority(uint16_t seq, int is_ccs) {
assert(seq * 2 >= seq);

/* The index of the retransmission queue actually is the message sequence
* number, since the queue only contains messages of a single handshake.
* However, the ChangeCipherSpec has no message sequence number and so using
@@ -709,27 +705,6 @@ int dtls1_get_queue_priority(unsigned short seq, int is_ccs) {
}

static int dtls1_retransmit_message(SSL *s, hm_fragment *frag) {
int ret;
/* XDTLS: for now assuming that read/writes are blocking */
unsigned long header_length;

/* assert(s->init_num == 0);
assert(s->init_off == 0); */

if (frag->msg_header.is_ccs) {
header_length = DTLS1_CCS_HEADER_LENGTH;
} else {
header_length = DTLS1_HM_HEADER_LENGTH;
}

memcpy(s->init_buf->data, frag->fragment,
frag->msg_header.msg_len + header_length);
s->init_num = frag->msg_header.msg_len + header_length;

dtls1_set_message_header(s, frag->msg_header.type,
frag->msg_header.msg_len, frag->msg_header.seq,
0, frag->msg_header.frag_len);

/* DTLS renegotiation is unsupported, so only epochs 0 (NULL cipher) and 1
* (negotiated cipher) exist. */
assert(s->d1->w_epoch == 0 || s->d1->w_epoch == 1);
@@ -739,10 +714,24 @@ static int dtls1_retransmit_message(SSL *s, hm_fragment *frag) {
use_epoch = dtls1_use_previous_epoch;
}

ret = dtls1_do_write(s, frag->msg_header.is_ccs ? SSL3_RT_CHANGE_CIPHER_SPEC
: SSL3_RT_HANDSHAKE,
use_epoch);
/* TODO(davidben): This cannot handle non-blocking writes. */
int ret;
if (frag->msg_header.is_ccs) {
ret = dtls1_write_change_cipher_spec(s, use_epoch);
} else {
/* Restore the message body.
* TODO(davidben): Make this less stateful. */
memcpy(s->init_buf->data, frag->fragment,
frag->msg_header.msg_len + DTLS1_HM_HEADER_LENGTH);
s->init_num = frag->msg_header.msg_len + DTLS1_HM_HEADER_LENGTH;

dtls1_set_message_header(s, frag->msg_header.type,
frag->msg_header.msg_len, frag->msg_header.seq,
0, frag->msg_header.frag_len);
ret = dtls1_do_handshake_write(s, use_epoch);
}

/* TODO(davidben): Check return value? */
(void)BIO_flush(SSL_get_wbio(s));
return ret;
}
@@ -763,46 +752,65 @@ int dtls1_retransmit_buffered_messages(SSL *s) {
return 1;
}

int dtls1_buffer_message(SSL *s, int is_ccs) {
pitem *item;
hm_fragment *frag;
/* dtls1_buffer_change_cipher_spec adds a ChangeCipherSpec to the current
* handshake flight, ordered just before the handshake message numbered
* |seq|. */
static int dtls1_buffer_change_cipher_spec(SSL *ssl, uint16_t seq) {
hm_fragment *frag = dtls1_hm_fragment_new(0 /* frag_len */,
0 /* no reassembly */);
if (frag == NULL) {
return 0;
}
frag->msg_header.is_ccs = 1;
frag->msg_header.epoch = ssl->d1->w_epoch;

uint16_t priority = dtls1_get_queue_priority(seq, 1 /* is_ccs */);
uint8_t seq64be[8];
memset(seq64be, 0, sizeof(seq64be));
seq64be[6] = (uint8_t)(priority >> 8);
seq64be[7] = (uint8_t)priority;

pitem *item = pitem_new(seq64be, frag);
if (item == NULL) {
dtls1_hm_fragment_free(frag);
return 0;
}

pqueue_insert(ssl->d1->sent_messages, item);
return 1;
}

int dtls1_buffer_message(SSL *s) {
/* this function is called immediately after a message has
* been serialized */
assert(s->init_off == 0);

frag = dtls1_hm_fragment_new(s->init_num, 0);
hm_fragment *frag = dtls1_hm_fragment_new(s->init_num, 0);
if (!frag) {
return 0;
}

memcpy(frag->fragment, s->init_buf->data, s->init_num);

if (is_ccs) {
assert(s->d1->w_msg_hdr.msg_len + DTLS1_CCS_HEADER_LENGTH ==
(unsigned int)s->init_num);
} else {
assert(s->d1->w_msg_hdr.msg_len + DTLS1_HM_HEADER_LENGTH ==
(unsigned int)s->init_num);
}
assert(s->d1->w_msg_hdr.msg_len + DTLS1_HM_HEADER_LENGTH ==
(unsigned int)s->init_num);

frag->msg_header.msg_len = s->d1->w_msg_hdr.msg_len;
frag->msg_header.seq = s->d1->w_msg_hdr.seq;
frag->msg_header.type = s->d1->w_msg_hdr.type;
frag->msg_header.frag_off = 0;
frag->msg_header.frag_len = s->d1->w_msg_hdr.msg_len;
frag->msg_header.is_ccs = is_ccs;
frag->msg_header.is_ccs = 0;
frag->msg_header.epoch = s->d1->w_epoch;

uint16_t priority = dtls1_get_queue_priority(frag->msg_header.seq,
0 /* handshake */);
uint8_t seq64be[8];
memset(seq64be, 0, sizeof(seq64be));
seq64be[6] = (uint8_t)(
dtls1_get_queue_priority(frag->msg_header.seq, frag->msg_header.is_ccs) >>
8);
seq64be[7] = (uint8_t)(
dtls1_get_queue_priority(frag->msg_header.seq, frag->msg_header.is_ccs));
seq64be[6] = (uint8_t)(priority >> 8);
seq64be[7] = (uint8_t)priority;

item = pitem_new(seq64be, frag);
pitem *item = pitem_new(seq64be, frag);
if (item == NULL) {
dtls1_hm_fragment_free(frag);
return 0;
@@ -812,6 +820,17 @@ int dtls1_buffer_message(SSL *s, int is_ccs) {
return 1;
}

int dtls1_send_change_cipher_spec(SSL *s, int a, int b) {
if (s->state == a) {
/* Buffer the message to handle retransmits. */
s->d1->handshake_write_seq = s->d1->next_handshake_write_seq;
dtls1_buffer_change_cipher_spec(s, s->d1->handshake_write_seq);
s->state = b;
}

return dtls1_write_change_cipher_spec(s, dtls1_use_current_epoch);
}

/* call this function when the buffered messages are no longer needed */
void dtls1_clear_record_buffer(SSL *s) {
pitem *item;


+ 2
- 2
ssl/d1_lib.c ファイルの表示

@@ -321,7 +321,7 @@ int dtls1_set_handshake_header(SSL *s, int htype, unsigned long len) {
s->init_off = 0;

/* Buffer the message to handle re-xmits */
dtls1_buffer_message(s, 0);
dtls1_buffer_message(s);

/* Add the new message to the handshake hash. Serialize the message
* header as if it were a single fragment. */
@@ -336,5 +336,5 @@ int dtls1_set_handshake_header(SSL *s, int htype, unsigned long len) {
}

int dtls1_handshake_write(SSL *s) {
return dtls1_do_write(s, SSL3_RT_HANDSHAKE, dtls1_use_current_epoch);
return dtls1_do_handshake_write(s, dtls1_use_current_epoch);
}

+ 5
- 4
ssl/internal.h ファイルの表示

@@ -912,7 +912,9 @@ typedef struct dtls1_state_st {
/* records being received in the current epoch */
DTLS1_BITMAP bitmap;

/* handshake message numbers */
/* handshake message numbers.
* TODO(davidben): It doesn't make much sense to store both of these. Only
* store one. */
uint16_t handshake_write_seq;
uint16_t next_handshake_write_seq;

@@ -1075,7 +1077,7 @@ int ssl3_do_change_cipher_spec(SSL *ssl);
int ssl3_set_handshake_header(SSL *s, int htype, unsigned long len);
int ssl3_handshake_write(SSL *s);

int dtls1_do_write(SSL *s, int type, enum dtls1_use_epoch_t use_epoch);
int dtls1_do_handshake_write(SSL *s, enum dtls1_use_epoch_t use_epoch);
int dtls1_read_app_data(SSL *ssl, uint8_t *buf, int len, int peek);
void dtls1_read_close_notify(SSL *ssl);
int dtls1_read_bytes(SSL *s, int type, uint8_t *buf, int len, int peek);
@@ -1090,8 +1092,7 @@ int dtls1_write_bytes(SSL *s, int type, const void *buf, int len,
int dtls1_send_change_cipher_spec(SSL *s, int a, int b);
int dtls1_send_finished(SSL *s, int a, int b, const char *sender, int slen);
int dtls1_read_failed(SSL *s, int code);
int dtls1_buffer_message(SSL *s, int ccs);
int dtls1_get_queue_priority(unsigned short seq, int is_ccs);
int dtls1_buffer_message(SSL *s);
int dtls1_retransmit_buffered_messages(SSL *s);
void dtls1_clear_record_buffer(SSL *s);
void dtls1_get_message_header(uint8_t *data, struct hm_header_st *msg_hdr);


読み込み中…
キャンセル
保存