Implement all TLS ciphers with stateful AEADs.
The EVP_CIPHER codepath should no longer be used with TLS. It still exists for
DTLS and SSLv3. The AEAD construction in TLS does not allow for
variable-overhead AEADs, so stateful AEADs do not include the length in the ad
parameter. Rather the AEADs internally append the unpadded length once it is
known. EVP_aead_rc4_md5_tls is modified to account for this.
Tests are added (and RC4-MD5's regenerated) for each of the new AEADs. The
cipher tests are all moved into crypto/cipher/test because there's now a lot of
them and they clutter the directory listing.
In ssl/, the stateful AEAD logic is also modified to account for stateful AEADs
with a fixed IV component, and for AEADs which use a random nonce (for the
explicit-IV CBC mode ciphers).
The new implementation fixes a bug/quirk in stateless CBC mode ciphers where
the fixed IV portion of the keyblock was generated regardless. This is at the
end, so it's only relevant for EAP-TLS which generates a MSK from the end of
the key block.
Change-Id: I2d8b8aa11deb43bde2fd733f4f90b5d5b8cb1334
Reviewed-on: https://boringssl-review.googlesource.com/2692
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/d1_lib.c b/ssl/d1_lib.c
index e4c2a2b..5869cdf 100644
--- a/ssl/d1_lib.c
+++ b/ssl/d1_lib.c
@@ -267,8 +267,7 @@
return NULL;
}
/* TODO(davidben): EVP_AEAD does not work in DTLS yet. */
- if (ciph->algorithm2 & SSL_CIPHER_ALGORITHM2_AEAD ||
- ciph->algorithm2 & SSL_CIPHER_ALGORITHM2_STATEFUL_AEAD) {
+ if (ciph->algorithm2 & SSL_CIPHER_ALGORITHM2_AEAD) {
return NULL;
}
}
diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c
index ef4e024..913f84b 100644
--- a/ssl/s3_lib.c
+++ b/ssl/s3_lib.c
@@ -160,11 +160,6 @@
#define SSL3_NUM_CIPHERS (sizeof(ssl3_ciphers) / sizeof(SSL_CIPHER))
-/* FIXED_NONCE_LEN is a macro that results in the correct value to set the
- * fixed nonce length in SSL_CIPHER.algorithms2. It's the inverse of
- * SSL_CIPHER_AEAD_FIXED_NONCE_LEN. */
-#define FIXED_NONCE_LEN(x) ((x / 2) << 24)
-
/* list of available SSLv3 ciphers (sorted by id) */
const SSL_CIPHER ssl3_ciphers[] = {
/* The RSA ciphers */
@@ -172,8 +167,7 @@
{
1, SSL3_TXT_RSA_RC4_128_MD5, SSL3_CK_RSA_RC4_128_MD5, SSL_kRSA, SSL_aRSA,
SSL_RC4, SSL_MD5, SSL_SSLV3, SSL_MEDIUM,
- SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF | SSL_CIPHER_ALGORITHM2_STATEFUL_AEAD,
- 128, 128,
+ SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 128, 128,
},
/* Cipher 05 */
@@ -322,7 +316,6 @@
TLS1_CK_RSA_WITH_AES_128_GCM_SHA256, SSL_kRSA, SSL_aRSA, SSL_AES128GCM,
SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256 | SSL_CIPHER_ALGORITHM2_AEAD |
- FIXED_NONCE_LEN(4) |
SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD,
128, 128,
},
@@ -333,7 +326,6 @@
TLS1_CK_RSA_WITH_AES_256_GCM_SHA384, SSL_kRSA, SSL_aRSA, SSL_AES256GCM,
SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
SSL_HANDSHAKE_MAC_SHA384 | TLS1_PRF_SHA384 | SSL_CIPHER_ALGORITHM2_AEAD |
- FIXED_NONCE_LEN(4) |
SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD,
256, 256,
},
@@ -344,7 +336,6 @@
TLS1_CK_DHE_RSA_WITH_AES_128_GCM_SHA256, SSL_kEDH, SSL_aRSA, SSL_AES128GCM,
SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256 | SSL_CIPHER_ALGORITHM2_AEAD |
- FIXED_NONCE_LEN(4) |
SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD,
128, 128,
},
@@ -355,7 +346,6 @@
TLS1_CK_DHE_RSA_WITH_AES_256_GCM_SHA384, SSL_kEDH, SSL_aRSA, SSL_AES256GCM,
SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
SSL_HANDSHAKE_MAC_SHA384 | TLS1_PRF_SHA384 | SSL_CIPHER_ALGORITHM2_AEAD |
- FIXED_NONCE_LEN(4) |
SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD,
256, 256,
},
@@ -366,7 +356,6 @@
TLS1_CK_ADH_WITH_AES_128_GCM_SHA256, SSL_kEDH, SSL_aNULL, SSL_AES128GCM,
SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256 | SSL_CIPHER_ALGORITHM2_AEAD |
- FIXED_NONCE_LEN(4) |
SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD,
128, 128,
},
@@ -377,7 +366,6 @@
TLS1_CK_ADH_WITH_AES_256_GCM_SHA384, SSL_kEDH, SSL_aNULL, SSL_AES256GCM,
SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
SSL_HANDSHAKE_MAC_SHA384 | TLS1_PRF_SHA384 | SSL_CIPHER_ALGORITHM2_AEAD |
- FIXED_NONCE_LEN(4) |
SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD,
256, 256,
},
@@ -496,7 +484,6 @@
TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, SSL_kEECDH, SSL_aECDSA,
SSL_AES128GCM, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256 | SSL_CIPHER_ALGORITHM2_AEAD |
- FIXED_NONCE_LEN(4) |
SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD,
128, 128,
},
@@ -507,7 +494,6 @@
TLS1_CK_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, SSL_kEECDH, SSL_aECDSA,
SSL_AES256GCM, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
SSL_HANDSHAKE_MAC_SHA384 | TLS1_PRF_SHA384 | SSL_CIPHER_ALGORITHM2_AEAD |
- FIXED_NONCE_LEN(4) |
SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD,
256, 256,
},
@@ -518,7 +504,6 @@
TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, SSL_kEECDH, SSL_aRSA,
SSL_AES128GCM, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256 | SSL_CIPHER_ALGORITHM2_AEAD |
- FIXED_NONCE_LEN(4) |
SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD,
128, 128,
},
@@ -529,7 +514,6 @@
TLS1_CK_ECDHE_RSA_WITH_AES_256_GCM_SHA384, SSL_kEECDH, SSL_aRSA,
SSL_AES256GCM, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
SSL_HANDSHAKE_MAC_SHA384 | TLS1_PRF_SHA384 | SSL_CIPHER_ALGORITHM2_AEAD |
- FIXED_NONCE_LEN(4) |
SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD,
256, 256,
},
@@ -543,7 +527,6 @@
TLS1_CK_ECDHE_PSK_WITH_AES_128_GCM_SHA256, SSL_kEECDH, SSL_aPSK,
SSL_AES128GCM, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH,
SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256 | SSL_CIPHER_ALGORITHM2_AEAD |
- FIXED_NONCE_LEN(4) |
SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD,
128, 128,
},
@@ -552,8 +535,7 @@
1, TLS1_TXT_ECDHE_RSA_WITH_CHACHA20_POLY1305,
TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305, SSL_kEECDH, SSL_aRSA,
SSL_CHACHA20POLY1305, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH,
- SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256 | SSL_CIPHER_ALGORITHM2_AEAD |
- FIXED_NONCE_LEN(0),
+ SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256 | SSL_CIPHER_ALGORITHM2_AEAD,
256, 0,
},
@@ -561,8 +543,7 @@
1, TLS1_TXT_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305, SSL_kEECDH, SSL_aECDSA,
SSL_CHACHA20POLY1305, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH,
- SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256 | SSL_CIPHER_ALGORITHM2_AEAD |
- FIXED_NONCE_LEN(0),
+ SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256 | SSL_CIPHER_ALGORITHM2_AEAD,
256, 0,
},
@@ -570,8 +551,7 @@
1, TLS1_TXT_DHE_RSA_WITH_CHACHA20_POLY1305,
TLS1_CK_DHE_RSA_CHACHA20_POLY1305, SSL_kEDH, SSL_aRSA,
SSL_CHACHA20POLY1305, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH,
- SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256 | SSL_CIPHER_ALGORITHM2_AEAD |
- FIXED_NONCE_LEN(0),
+ SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256 | SSL_CIPHER_ALGORITHM2_AEAD,
256, 0,
},
};
diff --git a/ssl/ssl_ciph.c b/ssl/ssl_ciph.c
index fb8c047..d5f9c68 100644
--- a/ssl/ssl_ciph.c
+++ b/ssl/ssl_ciph.c
@@ -142,8 +142,10 @@
#include <assert.h>
#include <openssl/engine.h>
+#include <openssl/md5.h>
#include <openssl/mem.h>
#include <openssl/obj.h>
+#include <openssl/sha.h>
#include "ssl_locl.h"
@@ -241,49 +243,109 @@
{0, SSL_TXT_FIPS, 0, 0, 0, 0, 0, 0, SSL_FIPS, 0, 0, 0},
};
-/* ssl_cipher_get_evp_aead sets |*aead| to point to the correct EVP_AEAD object
- * for |s->cipher|. It returns 1 on success and 0 on error. */
-int ssl_cipher_get_evp_aead(const SSL_SESSION *s, const EVP_AEAD **aead) {
- const SSL_CIPHER *c = s->cipher;
+int ssl_cipher_get_evp_aead(const EVP_AEAD **out_aead,
+ size_t *out_mac_secret_len,
+ size_t *out_fixed_iv_len,
+ const SSL_CIPHER *cipher, uint16_t version) {
+ *out_aead = NULL;
+ *out_mac_secret_len = 0;
+ *out_fixed_iv_len = 0;
- *aead = NULL;
-
- if (c == NULL) {
- return 0;
- }
-
- if ((c->algorithm2 & SSL_CIPHER_ALGORITHM2_AEAD) == 0 &&
- (c->algorithm2 & SSL_CIPHER_ALGORITHM2_STATEFUL_AEAD) == 0) {
- return 0;
- }
-
- switch (c->algorithm_enc) {
+ switch (cipher->algorithm_enc) {
case SSL_AES128GCM:
- *aead = EVP_aead_aes_128_gcm();
+ *out_aead = EVP_aead_aes_128_gcm();
+ *out_fixed_iv_len = 4;
return 1;
case SSL_AES256GCM:
- *aead = EVP_aead_aes_256_gcm();
+ *out_aead = EVP_aead_aes_256_gcm();
+ *out_fixed_iv_len = 4;
return 1;
case SSL_CHACHA20POLY1305:
- *aead = EVP_aead_chacha20_poly1305();
+ *out_aead = EVP_aead_chacha20_poly1305();
+ *out_fixed_iv_len = 0;
return 1;
case SSL_RC4:
- if (c->algorithm_mac != SSL_MD5) {
- return 0;
+ switch (cipher->algorithm_mac) {
+ case SSL_MD5:
+ *out_aead = EVP_aead_rc4_md5_tls();
+ *out_mac_secret_len = MD5_DIGEST_LENGTH;
+ return 1;
+ case SSL_SHA1:
+ *out_aead = EVP_aead_rc4_sha1_tls();
+ *out_mac_secret_len = SHA_DIGEST_LENGTH;
+ return 1;
+ default:
+ return 0;
}
- *aead = EVP_aead_rc4_md5_tls();
- return 1;
- }
- return 0;
+ case SSL_AES128:
+ switch (cipher->algorithm_mac) {
+ case SSL_SHA1:
+ if (version <= TLS1_VERSION) {
+ *out_aead = EVP_aead_aes_128_cbc_sha1_tls_implicit_iv();
+ *out_fixed_iv_len = 16;
+ } else {
+ *out_aead = EVP_aead_aes_128_cbc_sha1_tls();
+ }
+ *out_mac_secret_len = SHA_DIGEST_LENGTH;
+ return 1;
+ case SSL_SHA256:
+ *out_aead = EVP_aead_aes_128_cbc_sha256_tls();
+ *out_mac_secret_len = SHA256_DIGEST_LENGTH;
+ return 1;
+ default:
+ return 0;
+ }
+
+ case SSL_AES256:
+ switch (cipher->algorithm_mac) {
+ case SSL_SHA1:
+ if (version <= TLS1_VERSION) {
+ *out_aead = EVP_aead_aes_256_cbc_sha1_tls_implicit_iv();
+ *out_fixed_iv_len = 16;
+ } else {
+ *out_aead = EVP_aead_aes_256_cbc_sha1_tls();
+ }
+ *out_mac_secret_len = SHA_DIGEST_LENGTH;
+ return 1;
+ case SSL_SHA256:
+ *out_aead = EVP_aead_aes_256_cbc_sha256_tls();
+ *out_mac_secret_len = SHA256_DIGEST_LENGTH;
+ return 1;
+ case SSL_SHA384:
+ *out_aead = EVP_aead_aes_256_cbc_sha384_tls();
+ *out_mac_secret_len = SHA384_DIGEST_LENGTH;
+ return 1;
+ default:
+ return 0;
+ }
+
+ case SSL_3DES:
+ switch (cipher->algorithm_mac) {
+ case SSL_SHA1:
+ if (version <= TLS1_VERSION) {
+ *out_aead = EVP_aead_des_ede3_cbc_sha1_tls_implicit_iv();
+ *out_fixed_iv_len = 8;
+ } else {
+ *out_aead = EVP_aead_des_ede3_cbc_sha1_tls();
+ }
+ *out_mac_secret_len = SHA_DIGEST_LENGTH;
+ return 1;
+ default:
+ return 0;
+ }
+
+ default:
+ return 0;
+ }
}
int ssl_cipher_get_evp(const SSL_SESSION *s, const EVP_CIPHER **enc,
const EVP_MD **md, int *mac_pkey_type,
- int *mac_secret_size) {
+ size_t *mac_secret_size) {
const SSL_CIPHER *c;
c = s->cipher;
@@ -347,7 +409,7 @@
}
int ssl_cipher_get_mac(const SSL_SESSION *s, const EVP_MD **md,
- int *mac_pkey_type, int *mac_secret_size) {
+ int *mac_pkey_type, size_t *mac_secret_size) {
const SSL_CIPHER *c;
c = s->cipher;
diff --git a/ssl/ssl_error.c b/ssl/ssl_error.c
index 2f06460..a0a5ac7 100644
--- a/ssl/ssl_error.c
+++ b/ssl/ssl_error.c
@@ -207,6 +207,7 @@
{ERR_PACK(ERR_LIB_SSL, SSL_F_tls1_change_cipher_state_aead, 0), "tls1_change_cipher_state_aead"},
{ERR_PACK(ERR_LIB_SSL, SSL_F_tls1_change_cipher_state_cipher, 0), "tls1_change_cipher_state_cipher"},
{ERR_PACK(ERR_LIB_SSL, SSL_F_tls1_check_duplicate_extensions, 0), "tls1_check_duplicate_extensions"},
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_tls1_enc, 0), "tls1_enc"},
{ERR_PACK(ERR_LIB_SSL, SSL_F_tls1_export_keying_material, 0), "tls1_export_keying_material"},
{ERR_PACK(ERR_LIB_SSL, SSL_F_tls1_get_server_supplemental_data, 0), "tls1_get_server_supplemental_data"},
{ERR_PACK(ERR_LIB_SSL, SSL_F_tls1_heartbeat, 0), "tls1_heartbeat"},
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 0118aa5..9e5af76 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -3165,6 +3165,27 @@
}
}
+uint16_t ssl3_version_from_wire(SSL *s, uint16_t wire_version) {
+ if (!SSL_IS_DTLS(s)) {
+ return wire_version;
+ }
+
+ uint16_t tls_version = ~wire_version;
+ uint16_t version = tls_version + 0x0201;
+ /* If either component overflowed, clamp it so comparisons still work. */
+ if ((version >> 8) < (tls_version >> 8)) {
+ version = 0xff00 | (version & 0xff);
+ }
+ if ((version & 0xff) < (tls_version & 0xff)) {
+ version = (version & 0xff00) | 0xff;
+ }
+ /* DTLS 1.0 maps to TLS 1.1, not TLS 1.0. */
+ if (version == TLS1_VERSION) {
+ version = TLS1_1_VERSION;
+ }
+ return version;
+}
+
/* Allocates new EVP_MD_CTX and sets pointer to it into given pointer vairable,
* freeing EVP_MD_CTX previously stored in that variable, if any. If EVP_MD
* pointer is passed, initializes ctx with this md Returns newly allocated
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index 0364097..4cb95ca 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -363,22 +363,12 @@
* indicates that the cipher is implemented via an EVP_AEAD. */
#define SSL_CIPHER_ALGORITHM2_AEAD (1 << 23)
-/* SSL_CIPHER_AEAD_FIXED_NONCE_LEN returns the number of bytes of fixed nonce
- * for an SSL_CIPHER* with the SSL_CIPHER_ALGORITHM2_AEAD flag. */
-#define SSL_CIPHER_AEAD_FIXED_NONCE_LEN(ssl_cipher) \
- (((ssl_cipher->algorithm2 >> 24) & 0xf) * 2)
-
/* SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD is a flag in
* SSL_CIPHER.algorithm2 which indicates that the variable part of the nonce is
* included as a prefix of the record. (AES-GCM, for example, does with with an
* 8-byte variable nonce.) */
#define SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD (1<<22)
-/* SSL_CIPHER_ALGORITHM2_STATEFUL_AEAD is a flag in SSL_CIPHER.algorithm2 which
- * indicates that the AEAD is stateful and so doesn't take an nonce. This is
- * only true of legacy cipher suites. */
-#define SSL_CIPHER_ALGORITHM2_STATEFUL_AEAD (1<<28)
-
/* Cipher strength information. */
#define SSL_MEDIUM 0x00000001L
#define SSL_HIGH 0x00000002L
@@ -643,6 +633,13 @@
/* variable_nonce_included_in_record is non-zero if the variable nonce
* for a record is included as a prefix before the ciphertext. */
char variable_nonce_included_in_record;
+ /* random_variable_nonce is non-zero if the variable nonce is
+ * randomly generated, rather than derived from the sequence
+ * number. */
+ char random_variable_nonce;
+ /* omit_length_in_ad is non-zero if the length should be omitted in the
+ * AEAD's ad parameter. */
+ char omit_length_in_ad;
};
extern const SSL_CIPHER ssl3_ciphers[];
@@ -681,12 +678,22 @@
struct ssl_cipher_preference_list_st *ssl_cipher_preference_list_from_ciphers(
STACK_OF(SSL_CIPHER) * ciphers);
struct ssl_cipher_preference_list_st *ssl_get_cipher_preferences(SSL *s);
-int ssl_cipher_get_evp_aead(const SSL_SESSION *s, const EVP_AEAD **aead);
+
+/* ssl_cipher_get_evp_aead sets |*out_aead| to point to the correct EVP_AEAD
+* object for |cipher| protocol version |version|. It sets |*out_mac_secret_len|
+* and |*out_fixed_iv_len| to the MAC key length and fixed IV length,
+* respectively. The MAC key length is zero except for legacy block and stream
+* ciphers. It returns 1 on success and 0 on error. */
+int ssl_cipher_get_evp_aead(const EVP_AEAD **out_aead,
+ size_t *out_mac_secret_len,
+ size_t *out_fixed_iv_len,
+ const SSL_CIPHER *cipher, uint16_t version);
+
int ssl_cipher_get_evp(const SSL_SESSION *s, const EVP_CIPHER **enc,
const EVP_MD **md, int *mac_pkey_type,
- int *mac_secret_size);
+ size_t *mac_secret_size);
int ssl_cipher_get_mac(const SSL_SESSION *s, const EVP_MD **md,
- int *mac_pkey_type, int *mac_secret_size);
+ int *mac_pkey_type, size_t *mac_secret_size);
int ssl_get_handshake_digest(int i, long *mask, const EVP_MD **md);
int ssl_cipher_get_cert_index(const SSL_CIPHER *c);
int ssl_cipher_has_server_public_key(const SSL_CIPHER *cipher);
@@ -999,6 +1006,15 @@
* version for |s| and zero otherwise. */
int ssl3_is_version_enabled(SSL *s, uint16_t version);
+/* ssl3_version_from_wire maps |wire_version| to a protocol version. For
+ * SSLv3/TLS, the version is returned as-is. For DTLS, the corresponding TLS
+ * version is used. Note that this mapping is not injective but preserves
+ * comparisons.
+ *
+ * TODO(davidben): To normalize some DTLS-specific code, move away from using
+ * the wire version except at API boundaries. */
+uint16_t ssl3_version_from_wire(SSL *s, uint16_t wire_version);
+
EVP_MD_CTX *ssl_replace_hash(EVP_MD_CTX **hash, const EVP_MD *md);
void ssl_clear_hash_ctx(EVP_MD_CTX **hash);
int ssl_add_serverhello_renegotiate_ext(SSL *s, uint8_t *p, int *len,
diff --git a/ssl/t1_enc.c b/ssl/t1_enc.c
index dd8ccb0..dac3fca 100644
--- a/ssl/t1_enc.c
+++ b/ssl/t1_enc.c
@@ -330,10 +330,9 @@
unsigned mac_secret_len) {
const EVP_AEAD *aead = s->s3->tmp.new_aead;
SSL_AEAD_CTX *aead_ctx;
- /* mac_key_and_key is used to merge the MAC and cipher keys for an AEAD which
- * simulates pre-AEAD cipher suites. It needs to be large enough to cope with
- * the largest pair of keys. */
- uint8_t mac_key_and_key[32 /* HMAC(SHA256) */ + 32 /* AES-256 */];
+ /* merged_key is used to merge the MAC, cipher, and IV keys for an AEAD which
+ * simulates pre-AEAD cipher suites. */
+ uint8_t merged_key[EVP_AEAD_MAX_KEY_LENGTH];
if (is_read) {
tls1_cleanup_enc_ctx(&s->enc_read_ctx);
@@ -346,15 +345,17 @@
if (mac_secret_len > 0) {
/* This is a "stateful" AEAD (for compatibility with pre-AEAD cipher
* suites). */
- if (mac_secret_len + key_len > sizeof(mac_key_and_key)) {
+ if (mac_secret_len + key_len + iv_len > sizeof(merged_key)) {
OPENSSL_PUT_ERROR(SSL, tls1_change_cipher_state_aead,
ERR_R_INTERNAL_ERROR);
return 0;
}
- memcpy(mac_key_and_key, mac_secret, mac_secret_len);
- memcpy(mac_key_and_key + mac_secret_len, key, key_len);
- key = mac_key_and_key;
+ memcpy(merged_key, mac_secret, mac_secret_len);
+ memcpy(merged_key + mac_secret_len, key, key_len);
+ memcpy(merged_key + mac_secret_len + key_len, iv, iv_len);
+ key = merged_key;
key_len += mac_secret_len;
+ key_len += iv_len;
}
if (is_read) {
@@ -381,21 +382,28 @@
return 0;
}
- if (iv_len > sizeof(aead_ctx->fixed_nonce)) {
- OPENSSL_PUT_ERROR(SSL, tls1_change_cipher_state_aead, ERR_R_INTERNAL_ERROR);
- return 0;
- }
+ if (mac_secret_len == 0) {
+ /* For a real AEAD, the IV is the fixed part of the nonce. */
+ if (iv_len > sizeof(aead_ctx->fixed_nonce)) {
+ OPENSSL_PUT_ERROR(SSL, tls1_change_cipher_state_aead, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
- memcpy(aead_ctx->fixed_nonce, iv, iv_len);
- aead_ctx->fixed_nonce_len = iv_len;
- aead_ctx->variable_nonce_len = 8; /* correct for all true AEADs so far. */
- if (s->s3->tmp.new_cipher->algorithm2 & SSL_CIPHER_ALGORITHM2_STATEFUL_AEAD) {
- aead_ctx->variable_nonce_len = 0;
- }
-
- aead_ctx->variable_nonce_included_in_record =
+ memcpy(aead_ctx->fixed_nonce, iv, iv_len);
+ aead_ctx->fixed_nonce_len = iv_len;
+ aead_ctx->variable_nonce_included_in_record =
(s->s3->tmp.new_cipher->algorithm2 &
SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD) != 0;
+ aead_ctx->random_variable_nonce = 0;
+ aead_ctx->omit_length_in_ad = 0;
+ } else {
+ aead_ctx->fixed_nonce_len = 0;
+ aead_ctx->variable_nonce_included_in_record = 1;
+ aead_ctx->random_variable_nonce = 1;
+ aead_ctx->omit_length_in_ad = 1;
+ }
+ aead_ctx->variable_nonce_len = s->s3->tmp.new_variable_iv_len;
+
if (aead_ctx->variable_nonce_len + aead_ctx->fixed_nonce_len !=
EVP_AEAD_nonce_length(aead)) {
OPENSSL_PUT_ERROR(SSL, tls1_change_cipher_state_aead, ERR_R_INTERNAL_ERROR);
@@ -524,7 +532,7 @@
const uint8_t *client_write_iv, *server_write_iv, *iv;
const EVP_CIPHER *cipher = s->s3->tmp.new_sym_enc;
const EVP_AEAD *aead = s->s3->tmp.new_aead;
- unsigned key_len, iv_len, mac_secret_len;
+ size_t key_len, iv_len, mac_secret_len;
const uint8_t *key_data;
/* Reset sequence number to zero. */
@@ -532,22 +540,23 @@
memset(is_read ? s->s3->read_sequence : s->s3->write_sequence, 0, 8);
}
- mac_secret_len = s->s3->tmp.new_mac_secret_size;
+ mac_secret_len = s->s3->tmp.new_mac_secret_len;
+ iv_len = s->s3->tmp.new_fixed_iv_len;
if (aead != NULL) {
key_len = EVP_AEAD_key_length(aead);
- /* For "stateful" AEADs (i.e. compatibility with pre-AEAD cipher suites)
- * the key length reported by |EVP_AEAD_key_length| will include the MAC
- * key bytes. */
- if (key_len < mac_secret_len) {
- OPENSSL_PUT_ERROR(SSL, tls1_change_cipher_state, ERR_R_INTERNAL_ERROR);
- return 0;
+ if (mac_secret_len > 0) {
+ /* For "stateful" AEADs (i.e. compatibility with pre-AEAD cipher suites)
+ * the key length reported by |EVP_AEAD_key_length| will include the MAC
+ * and IV key bytes. */
+ if (key_len < mac_secret_len + iv_len) {
+ OPENSSL_PUT_ERROR(SSL, tls1_change_cipher_state, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+ key_len -= mac_secret_len + iv_len;
}
- key_len -= mac_secret_len;
- iv_len = SSL_CIPHER_AEAD_FIXED_NONCE_LEN(s->s3->tmp.new_cipher);
} else {
key_len = EVP_CIPHER_key_length(cipher);
- iv_len = EVP_CIPHER_iv_length(cipher);
}
key_data = s->s3->tmp.key_block;
@@ -601,53 +610,68 @@
const EVP_MD *hash = NULL;
const EVP_AEAD *aead = NULL;
int num;
- int mac_type = NID_undef, mac_secret_size = 0;
+ int mac_type = NID_undef;
int ret = 0;
- unsigned key_len, iv_len;
+ size_t mac_secret_len, fixed_iv_len, variable_iv_len, key_len;
if (s->s3->tmp.key_block_length != 0) {
return 1;
}
- if (s->session->cipher &&
- ((s->session->cipher->algorithm2 & SSL_CIPHER_ALGORITHM2_AEAD) ||
- (s->session->cipher->algorithm2 &
- SSL_CIPHER_ALGORITHM2_STATEFUL_AEAD))) {
- if (!ssl_cipher_get_evp_aead(s->session, &aead)) {
+ if (s->session->cipher == NULL) {
+ goto cipher_unavailable_err;
+ }
+
+ /* TODO(davidben): Make DTLS record-layer code EVP_AEAD-aware. */
+ if (!SSL_IS_DTLS(s)) {
+ if (!ssl_cipher_get_evp_aead(&aead, &mac_secret_len, &fixed_iv_len,
+ s->session->cipher,
+ ssl3_version_from_wire(s, s->version))) {
goto cipher_unavailable_err;
}
key_len = EVP_AEAD_key_length(aead);
- iv_len = SSL_CIPHER_AEAD_FIXED_NONCE_LEN(s->session->cipher);
- if ((s->session->cipher->algorithm2 &
- SSL_CIPHER_ALGORITHM2_STATEFUL_AEAD) &&
- !ssl_cipher_get_mac(s->session, &hash, &mac_type, &mac_secret_size)) {
- goto cipher_unavailable_err;
+ variable_iv_len = EVP_AEAD_nonce_length(aead);
+ if (mac_secret_len > 0) {
+ /* For "stateful" AEADs (i.e. compatibility with pre-AEAD cipher suites)
+ * the key length reported by |EVP_AEAD_key_length| will include the MAC
+ * key bytes and initial implicit IV. */
+ if (key_len < mac_secret_len + fixed_iv_len) {
+ OPENSSL_PUT_ERROR(SSL, tls1_setup_key_block, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+ key_len -= mac_secret_len + fixed_iv_len;
+ } else {
+ /* The nonce is split into a fixed portion and a variable portion. */
+ if (variable_iv_len < fixed_iv_len) {
+ OPENSSL_PUT_ERROR(SSL, tls1_setup_key_block, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+ variable_iv_len -= fixed_iv_len;
}
- /* For "stateful" AEADs (i.e. compatibility with pre-AEAD cipher suites)
- * the key length reported by |EVP_AEAD_key_length| will include the MAC
- * key bytes. */
- if (key_len < (size_t)mac_secret_size) {
- OPENSSL_PUT_ERROR(SSL, tls1_change_cipher_state, ERR_R_INTERNAL_ERROR);
- return 0;
- }
- key_len -= mac_secret_size;
} else {
if (!ssl_cipher_get_evp(s->session, &c, &hash, &mac_type,
- &mac_secret_size)) {
+ &mac_secret_len)) {
goto cipher_unavailable_err;
}
key_len = EVP_CIPHER_key_length(c);
- iv_len = EVP_CIPHER_iv_length(c);
+ fixed_iv_len = EVP_CIPHER_iv_length(c);
+ variable_iv_len = 0;
}
+ assert(mac_secret_len < 256);
+ assert(fixed_iv_len < 256);
+ assert(variable_iv_len < 256);
+
s->s3->tmp.new_aead = aead;
s->s3->tmp.new_sym_enc = c;
s->s3->tmp.new_hash = hash;
s->s3->tmp.new_mac_pkey_type = mac_type;
- s->s3->tmp.new_mac_secret_size = mac_secret_size;
+ s->s3->tmp.new_mac_secret_len = (uint8_t)mac_secret_len;
+ s->s3->tmp.new_fixed_iv_len = (uint8_t)fixed_iv_len;
+ s->s3->tmp.new_variable_iv_len = (uint8_t)variable_iv_len;
- num = key_len + mac_secret_size + iv_len;
+ num = key_len + mac_secret_len + fixed_iv_len;
num *= 2;
ssl3_cleanup_key_block(s);
@@ -714,9 +738,9 @@
}
if (aead) {
- uint8_t ad[13], *seq, *in, *out, nonce[16];
+ uint8_t ad[13], *seq, *in, *out, nonce[EVP_AEAD_MAX_NONCE_LENGTH];
unsigned nonce_used;
- size_t n;
+ size_t n, ad_len;
seq = send ? s->s3->write_sequence : s->s3->read_sequence;
@@ -740,8 +764,8 @@
ad[9] = (uint8_t)(s->version >> 8);
ad[10] = (uint8_t)(s->version);
- if (aead->fixed_nonce_len + aead->variable_nonce_len > sizeof(nonce) ||
- aead->variable_nonce_len > 8) {
+ if (aead->fixed_nonce_len + aead->variable_nonce_len > sizeof(nonce)) {
+ OPENSSL_PUT_ERROR(SSL, tls1_enc, ERR_R_INTERNAL_ERROR);
return -1; /* internal error - should never happen. */
}
@@ -754,12 +778,21 @@
in = rec->input;
out = rec->data;
- /* When sending we use the sequence number as the variable part of the
- * nonce. */
- if (aead->variable_nonce_len > 8) {
- return -1;
+ uint8_t *variable_nonce = nonce + nonce_used;
+ if (aead->random_variable_nonce) {
+ assert(aead->variable_nonce_included_in_record);
+ if (!RAND_bytes(nonce + nonce_used, aead->variable_nonce_len)) {
+ return -1;
+ }
+ } else {
+ /* When sending we use the sequence number as the variable part of the
+ * nonce. */
+ if (aead->variable_nonce_len != 8) {
+ OPENSSL_PUT_ERROR(SSL, tls1_enc, ERR_R_INTERNAL_ERROR);
+ return -1;
+ }
+ memcpy(nonce + nonce_used, ad, aead->variable_nonce_len);
}
- memcpy(nonce + nonce_used, ad, aead->variable_nonce_len);
nonce_used += aead->variable_nonce_len;
/* in do_ssl3_write, rec->input is moved forward by variable_nonce_len in
@@ -767,17 +800,21 @@
* sequence number bytes into place without overwriting any of the
* plaintext. */
if (aead->variable_nonce_included_in_record) {
- memcpy(out, ad, aead->variable_nonce_len);
+ memcpy(out, variable_nonce, aead->variable_nonce_len);
len -= aead->variable_nonce_len;
eivlen = aead->variable_nonce_len;
}
- ad[11] = len >> 8;
- ad[12] = len & 0xff;
+ if (aead->omit_length_in_ad) {
+ ad_len = 11;
+ } else {
+ ad[11] = len >> 8;
+ ad[12] = len & 0xff;
+ ad_len = 13;
+ }
if (!EVP_AEAD_CTX_seal(&aead->ctx, out + eivlen, &n, len + aead->tag_len,
- nonce, nonce_used, in + eivlen, len, ad,
- sizeof(ad))) {
+ nonce, nonce_used, in + eivlen, len, ad, ad_len)) {
return -1;
}
@@ -789,6 +826,7 @@
size_t len = rec->length;
if (rec->data != rec->input) {
+ OPENSSL_PUT_ERROR(SSL, tls1_enc, ERR_R_INTERNAL_ERROR);
return -1; /* internal error - should never happen. */
}
out = in = rec->input;
@@ -807,16 +845,21 @@
out += aead->variable_nonce_len;
}
- if (len < aead->tag_len) {
- return 0;
+ if (aead->omit_length_in_ad) {
+ ad_len = 11;
+ } else {
+ if (len < aead->tag_len) {
+ return 0;
+ }
+ size_t plaintext_len = len - aead->tag_len;
+
+ ad[11] = plaintext_len >> 8;
+ ad[12] = plaintext_len & 0xff;
+ ad_len = 13;
}
- len -= aead->tag_len;
- ad[11] = len >> 8;
- ad[12] = len & 0xff;
-
- if (!EVP_AEAD_CTX_open(&aead->ctx, out, &n, len, nonce, nonce_used, in,
- len + aead->tag_len, ad, sizeof(ad))) {
+ if (!EVP_AEAD_CTX_open(&aead->ctx, out, &n, rec->length, nonce, nonce_used, in,
+ len, ad, ad_len)) {
return -1;
}