Support EVP_AEAD in DTLS.
This CL removes the last of the EVP_CIPHER codepath in ssl/. The dead code is
intentionally not pruned for ease of review, except in DTLS-only code where
adding new logic to support both, only to remove half, would be cumbersome.
Fixes made:
- dtls1_retransmit_state is taught to retain aead_write_ctx rather than
enc_write_ctx.
- d1_pkt.c reserves space for the variable-length nonce when echoed into the
packet.
- dtls1_do_write sizes the MTU based on EVP_AEAD max overhead.
- tls1_change_cipher_state_cipher should not free AEAD write contexts in DTLS.
This matches the (rather confused) ownership for the EVP_CIPHER contexts.
I've added a TODO to resolve this craziness.
A follow-up CL will remove all the resultant dead code.
Change-Id: I644557f4db53bbfb182950823ab96d5e4c908866
Reviewed-on: https://boringssl-review.googlesource.com/2699
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/include/openssl/dtls1.h b/include/openssl/dtls1.h
index 57337f6..0fc3ae6 100644
--- a/include/openssl/dtls1.h
+++ b/include/openssl/dtls1.h
@@ -111,8 +111,7 @@
struct dtls1_retransmit_state
{
- EVP_CIPHER_CTX *enc_write_ctx; /* cryptographic state */
- EVP_MD_CTX *write_hash; /* used for mac generation */
+ SSL_AEAD_CTX *aead_write_ctx;
SSL_SESSION *session;
unsigned short epoch;
};
diff --git a/ssl/d1_both.c b/ssl/d1_both.c
index 9081e4b..8d1dbf9 100644
--- a/ssl/d1_both.c
+++ b/ssl/d1_both.c
@@ -214,8 +214,14 @@
void dtls1_hm_fragment_free(hm_fragment *frag) {
if (frag->msg_header.is_ccs) {
- EVP_CIPHER_CTX_free(frag->msg_header.saved_retransmit_state.enc_write_ctx);
- EVP_MD_CTX_destroy(frag->msg_header.saved_retransmit_state.write_hash);
+ /* TODO(davidben): Simplify aead_write_ctx ownership, probably by just
+ * forbidding DTLS renego. */
+ SSL_AEAD_CTX *aead_write_ctx =
+ frag->msg_header.saved_retransmit_state.aead_write_ctx;
+ if (aead_write_ctx) {
+ EVP_AEAD_CTX_cleanup(&aead_write_ctx->ctx);
+ OPENSSL_free(aead_write_ctx);
+ }
}
if (frag->fragment) {
OPENSSL_free(frag->fragment);
@@ -231,7 +237,8 @@
int dtls1_do_write(SSL *s, int type) {
int ret;
int curr_mtu;
- unsigned int len, frag_off, mac_size = 0, blocksize = 0;
+ unsigned int len, frag_off;
+ size_t max_overhead = 0;
/* AHA! Figure out the MTU, and stick to the right size */
if (s->d1->mtu < dtls1_min_mtu() &&
@@ -255,21 +262,18 @@
(int)s->d1->w_msg_hdr.msg_len + DTLS1_HM_HEADER_LENGTH);
}
- if (s->write_hash &&
- (s->enc_write_ctx == NULL ||
- EVP_CIPHER_CTX_mode(s->enc_write_ctx) != EVP_CIPH_GCM_MODE)) {
- mac_size = EVP_MD_CTX_size(s->write_hash);
- }
-
- if (s->enc_write_ctx &&
- (EVP_CIPHER_CTX_mode(s->enc_write_ctx) == EVP_CIPH_CBC_MODE)) {
- blocksize = 2 * EVP_CIPHER_block_size(s->enc_write_ctx->cipher);
+ /* Determine the maximum overhead of the current cipher. */
+ if (s->aead_write_ctx != NULL) {
+ max_overhead = EVP_AEAD_max_overhead(s->aead_write_ctx->ctx.aead);
+ if (s->aead_write_ctx->variable_nonce_included_in_record) {
+ max_overhead += s->aead_write_ctx->variable_nonce_len;
+ }
}
frag_off = 0;
while (s->init_num) {
curr_mtu = s->d1->mtu - BIO_wpending(SSL_get_wbio(s)) -
- DTLS1_RT_HEADER_LENGTH - mac_size - blocksize;
+ DTLS1_RT_HEADER_LENGTH - max_overhead;
if (curr_mtu <= DTLS1_HM_HEADER_LENGTH) {
/* grr.. we could get an error if MTU picked was wrong */
@@ -277,7 +281,7 @@
if (ret <= 0) {
return ret;
}
- curr_mtu = s->d1->mtu - DTLS1_RT_HEADER_LENGTH - mac_size - blocksize;
+ curr_mtu = s->d1->mtu - DTLS1_RT_HEADER_LENGTH - max_overhead;
}
if (s->init_num > curr_mtu) {
@@ -978,8 +982,7 @@
frag->msg_header.is_ccs = is_ccs;
/* save current state*/
- frag->msg_header.saved_retransmit_state.enc_write_ctx = s->enc_write_ctx;
- frag->msg_header.saved_retransmit_state.write_hash = s->write_hash;
+ frag->msg_header.saved_retransmit_state.aead_write_ctx = s->aead_write_ctx;
frag->msg_header.saved_retransmit_state.session = s->session;
frag->msg_header.saved_retransmit_state.epoch = s->d1->w_epoch;
@@ -1044,14 +1047,12 @@
0, frag->msg_header.frag_len);
/* save current state */
- saved_state.enc_write_ctx = s->enc_write_ctx;
- saved_state.write_hash = s->write_hash;
+ saved_state.aead_write_ctx = s->aead_write_ctx;
saved_state.session = s->session;
saved_state.epoch = s->d1->w_epoch;
/* restore state in which the message was originally sent */
- s->enc_write_ctx = frag->msg_header.saved_retransmit_state.enc_write_ctx;
- s->write_hash = frag->msg_header.saved_retransmit_state.write_hash;
+ s->aead_write_ctx = frag->msg_header.saved_retransmit_state.aead_write_ctx;
s->session = frag->msg_header.saved_retransmit_state.session;
s->d1->w_epoch = frag->msg_header.saved_retransmit_state.epoch;
@@ -1066,8 +1067,7 @@
: SSL3_RT_HANDSHAKE);
/* restore current state */
- s->enc_write_ctx = saved_state.enc_write_ctx;
- s->write_hash = saved_state.write_hash;
+ s->aead_write_ctx = saved_state.aead_write_ctx;
s->session = saved_state.session;
s->d1->w_epoch = saved_state.epoch;
diff --git a/ssl/d1_lib.c b/ssl/d1_lib.c
index 7e76905..e4928a5 100644
--- a/ssl/d1_lib.c
+++ b/ssl/d1_lib.c
@@ -256,22 +256,11 @@
return ret;
}
-/* As it's impossible to use stream ciphers in "datagram" mode, this
- * simple filter is designed to disengage them in DTLS. Unfortunately
- * there is no universal way to identify stream SSL_CIPHER, so we have
- * to explicitly list their SSL_* codes. Currently RC4 is the only one
- * available, but if new ones emerge, they will have to be added... */
const SSL_CIPHER *dtls1_get_cipher(unsigned int u) {
const SSL_CIPHER *ciph = ssl3_get_cipher(u);
-
- if (ciph != NULL) {
- if (ciph->algorithm_enc == SSL_RC4) {
- return NULL;
- }
- /* TODO(davidben): EVP_AEAD does not work in DTLS yet. */
- if (ciph->algorithm2 & SSL_CIPHER_ALGORITHM2_AEAD) {
- return NULL;
- }
+ /* DTLS does not support stream ciphers. */
+ if (ciph == NULL || ciph->algorithm_enc == SSL_RC4) {
+ return NULL;
}
return ciph;
diff --git a/ssl/d1_pkt.c b/ssl/d1_pkt.c
index 67b06a5..85bd305 100644
--- a/ssl/d1_pkt.c
+++ b/ssl/d1_pkt.c
@@ -1213,17 +1213,15 @@
p += 10;
/* Explicit IV length, block ciphers appropriate version flag */
- if (s->enc_write_ctx) {
- int mode = EVP_CIPHER_CTX_mode(s->enc_write_ctx);
- if (mode == EVP_CIPH_CBC_MODE) {
- eivlen = EVP_CIPHER_CTX_iv_length(s->enc_write_ctx);
- if (eivlen <= 1) {
- eivlen = 0;
- }
- } else if (mode == EVP_CIPH_GCM_MODE) {
- /* Need explicit part of IV for GCM mode */
- eivlen = EVP_GCM_TLS_EXPLICIT_IV_LEN;
+ if (s->enc_write_ctx && SSL_USE_EXPLICIT_IV(s) &&
+ EVP_CIPHER_CTX_mode(s->enc_write_ctx) == EVP_CIPH_CBC_MODE) {
+ eivlen = EVP_CIPHER_CTX_iv_length(s->enc_write_ctx);
+ if (eivlen <= 1) {
+ eivlen = 0;
}
+ } else if (s->aead_write_ctx != NULL &&
+ s->aead_write_ctx->variable_nonce_included_in_record) {
+ eivlen = s->aead_write_ctx->variable_nonce_len;
}
/* lets setup the record stuff. */
diff --git a/ssl/d1_srvr.c b/ssl/d1_srvr.c
index 6f2e844..e4c57c3 100644
--- a/ssl/d1_srvr.c
+++ b/ssl/d1_srvr.c
@@ -294,9 +294,7 @@
case SSL3_ST_SW_CERT_A:
case SSL3_ST_SW_CERT_B:
- /* Check if it is anon DH or normal PSK */
- if (!(s->s3->tmp.new_cipher->algorithm_auth & SSL_aNULL) &&
- !(s->s3->tmp.new_cipher->algorithm_mkey & SSL_kPSK)) {
+ if (ssl_cipher_has_server_public_key(s->s3->tmp.new_cipher)) {
dtls1_start_timer(s);
ret = ssl3_send_server_certificate(s);
if (ret <= 0) {
diff --git a/ssl/t1_enc.c b/ssl/t1_enc.c
index 48079ff..e4e9907 100644
--- a/ssl/t1_enc.c
+++ b/ssl/t1_enc.c
@@ -362,6 +362,15 @@
}
aead_ctx = s->aead_read_ctx;
} else {
+ /* When updating the cipher state for DTLS, we do not wish to overwrite the
+ * old ones because DTLS stores pointers to them in order to implement
+ * retransmission. See dtls1_hm_fragment_free.
+ *
+ * TODO(davidben): Simplify aead_write_ctx ownership, probably by just
+ * forbidding DTLS renego. */
+ if (SSL_IS_DTLS(s)) {
+ s->aead_write_ctx = NULL;
+ }
if (!tls1_aead_ctx_init(&s->aead_write_ctx)) {
return 0;
}
@@ -440,7 +449,17 @@
if (is_read) {
tls1_cleanup_aead_ctx(&s->aead_read_ctx);
} else {
- tls1_cleanup_aead_ctx(&s->aead_write_ctx);
+ /* When updating the cipher state for DTLS, we do not wish to free the old
+ * ones because DTLS stores pointers to them in order to implement
+ * retransmission. See dtls1_hm_fragment_free.
+ *
+ * TODO(davidben): Simplify aead_write_ctx ownership, probably by just
+ * forbidding DTLS renego. */
+ if (!SSL_IS_DTLS(s)) {
+ tls1_cleanup_aead_ctx(&s->aead_write_ctx);
+ } else {
+ s->aead_write_ctx = NULL;
+ }
}
if (is_read) {
@@ -622,8 +641,8 @@
goto cipher_unavailable_err;
}
- /* TODO(davidben): Make DTLS record-layer code EVP_AEAD-aware. */
- if (!SSL_IS_DTLS(s)) {
+ /* TODO(davidben): Prune away dead code. To be done in follow-up commit. */
+ if (1) {
if (!ssl_cipher_get_evp_aead(&aead, &mac_secret_len, &fixed_iv_len,
s->session->cipher,
ssl3_version_from_wire(s, s->version))) {
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index d76bdf7..2342682 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -998,10 +998,7 @@
}
func isDTLSCipher(suiteName string) bool {
- // TODO(davidben): AES-GCM exists in DTLS 1.2 but is currently
- // broken because DTLS is not EVP_AEAD-aware.
- return !hasComponent(suiteName, "RC4") &&
- !hasComponent(suiteName, "GCM")
+ return !hasComponent(suiteName, "RC4")
}
func addCipherSuiteTests() {