summaryrefslogtreecommitdiff
path: root/lib/libssl/t1_enc.c
diff options
context:
space:
mode:
authorJoel Sing <jsing@cvs.openbsd.org>2014-06-08 14:13:45 +0000
committerJoel Sing <jsing@cvs.openbsd.org>2014-06-08 14:13:45 +0000
commitb7bb88f7f63eb95c1c484f6cca0bfa8e451c5f1b (patch)
tree58df4ee049bf2da795606c29d2081ba9b244a0b9 /lib/libssl/t1_enc.c
parent5ae906149f2a16708ba7948adbe01663316db6ab (diff)
Factor out the part of tls1_change_cipher_state() that is specific to
switching cipher states using an EVP_CIPHER. This will facilitate the addition of cipher state changes for EVP_AEAD. No functional change. Based on Adam Langley's chromium patches.
Diffstat (limited to 'lib/libssl/t1_enc.c')
-rw-r--r--lib/libssl/t1_enc.c279
1 files changed, 153 insertions, 126 deletions
diff --git a/lib/libssl/t1_enc.c b/lib/libssl/t1_enc.c
index 55f4d720738..5f5c8e3488b 100644
--- a/lib/libssl/t1_enc.c
+++ b/lib/libssl/t1_enc.c
@@ -297,38 +297,176 @@ tls1_generate_key_block(SSL *s, unsigned char *km, unsigned char *tmp, int num)
return ret;
}
-int
-tls1_change_cipher_state(SSL *s, int which)
+/*
+ * tls1_change_cipher_state_cipher performs the work needed to switch cipher
+ * states when using EVP_CIPHER. The argument is_read is true iff this function
+ * is being called due to reading, as opposed to writing, a ChangeCipherSpec
+ * message. In order to support export ciphersuites, use_client_keys indicates
+ * whether the key material provided is in the "client write" direction.
+ */
+static int
+tls1_change_cipher_state_cipher(SSL *s, char is_read, char use_client_keys,
+ const unsigned char *mac_secret, unsigned mac_secret_size,
+ const unsigned char *key, unsigned key_len, const unsigned char *iv,
+ unsigned iv_len)
{
static const unsigned char empty[] = "";
unsigned char export_tmp1[EVP_MAX_KEY_LENGTH];
unsigned char export_tmp2[EVP_MAX_KEY_LENGTH];
unsigned char export_iv1[EVP_MAX_IV_LENGTH * 2];
unsigned char export_iv2[EVP_MAX_IV_LENGTH * 2];
+ unsigned char *exp_label;
+ int exp_label_len;
+ EVP_CIPHER_CTX *cipher_ctx;
+ const EVP_CIPHER *cipher;
+ EVP_MD_CTX *mac_ctx;
+ const EVP_MD *mac;
+ EVP_PKEY *mac_key;
+ int mac_type;
+ int is_export;
+
+ is_export = SSL_C_IS_EXPORT(s->s3->tmp.new_cipher);
+ cipher = s->s3->tmp.new_sym_enc;
+ mac = s->s3->tmp.new_hash;
+ mac_type = s->s3->tmp.new_mac_pkey_type;
+
+ if (is_read) {
+ if (s->s3->tmp.new_cipher->algorithm2 & TLS1_STREAM_MAC)
+ s->mac_flags |= SSL_MAC_FLAG_READ_MAC_STREAM;
+ else
+ s->mac_flags &= ~SSL_MAC_FLAG_READ_MAC_STREAM;
+
+ EVP_CIPHER_CTX_free(s->enc_read_ctx);
+ s->enc_read_ctx = NULL;
+ EVP_MD_CTX_destroy(s->read_hash);
+ s->read_hash = NULL;
+
+ if ((cipher_ctx = EVP_CIPHER_CTX_new()) == NULL)
+ goto err;
+ s->enc_read_ctx = cipher_ctx;
+ if ((mac_ctx = EVP_MD_CTX_create()) == NULL)
+ goto err;
+ s->read_hash = mac_ctx;
+ } else {
+ if (s->s3->tmp.new_cipher->algorithm2 & TLS1_STREAM_MAC)
+ s->mac_flags |= SSL_MAC_FLAG_WRITE_MAC_STREAM;
+ else
+ s->mac_flags &= ~SSL_MAC_FLAG_WRITE_MAC_STREAM;
+
+ /*
+ * DTLS fragments retain a pointer to the compression, cipher
+ * and hash contexts, so that it can restore state in order
+ * to perform retransmissions. As such, we cannot free write
+ * contexts that are used for DTLS - these are instead freed
+ * by DTLS when its frees a ChangeCipherSpec fragment.
+ */
+ if (!SSL_IS_DTLS(s)) {
+ EVP_CIPHER_CTX_free(s->enc_write_ctx);
+ s->enc_write_ctx = NULL;
+ EVP_MD_CTX_destroy(s->write_hash);
+ s->write_hash = NULL;
+ }
+ if ((cipher_ctx = EVP_CIPHER_CTX_new()) == NULL)
+ goto err;
+ s->enc_write_ctx = cipher_ctx;
+ if ((mac_ctx = EVP_MD_CTX_create()) == NULL)
+ goto err;
+ s->write_hash = mac_ctx;
+ }
+
+ if (!(EVP_CIPHER_flags(cipher) & EVP_CIPH_FLAG_AEAD_CIPHER)) {
+ mac_key = EVP_PKEY_new_mac_key(mac_type, NULL,
+ mac_secret, mac_secret_size);
+ if (mac_key == NULL)
+ goto err;
+ EVP_DigestSignInit(mac_ctx, NULL, mac, NULL, mac_key);
+ EVP_PKEY_free(mac_key);
+ }
+ if (is_export) {
+ /*
+ * Both the read and write key/iv are set to the same value
+ * since only the correct one will be used :-).
+ */
+ if (use_client_keys) {
+ exp_label = TLS_MD_CLIENT_WRITE_KEY_CONST;
+ exp_label_len = TLS_MD_CLIENT_WRITE_KEY_CONST_SIZE;
+ } else {
+ exp_label = TLS_MD_SERVER_WRITE_KEY_CONST;
+ exp_label_len = TLS_MD_SERVER_WRITE_KEY_CONST_SIZE;
+ }
+ if (!tls1_PRF(ssl_get_algorithm2(s),
+ exp_label, exp_label_len,
+ s->s3->client_random, SSL3_RANDOM_SIZE,
+ s->s3->server_random, SSL3_RANDOM_SIZE,
+ NULL, 0, NULL, 0, key, key_len, export_tmp1, export_tmp2,
+ EVP_CIPHER_key_length(cipher)))
+ goto err2;
+ key = export_tmp1;
+
+ if (iv_len > 0) {
+ if (!tls1_PRF(ssl_get_algorithm2(s),
+ TLS_MD_IV_BLOCK_CONST, TLS_MD_IV_BLOCK_CONST_SIZE,
+ s->s3->client_random, SSL3_RANDOM_SIZE,
+ s->s3->server_random, SSL3_RANDOM_SIZE,
+ NULL, 0, NULL, 0, empty, 0,
+ export_iv1, export_iv2, iv_len * 2))
+ goto err2;
+ if (use_client_keys)
+ iv = export_iv1;
+ else
+ iv = &(export_iv1[iv_len]);
+ }
+ }
+
+ if (EVP_CIPHER_mode(cipher) == EVP_CIPH_GCM_MODE) {
+ EVP_CipherInit_ex(cipher_ctx, cipher, NULL, key, NULL,
+ !is_read);
+ EVP_CIPHER_CTX_ctrl(cipher_ctx, EVP_CTRL_GCM_SET_IV_FIXED,
+ iv_len, (unsigned char *)iv);
+ } else
+ EVP_CipherInit_ex(cipher_ctx, cipher, NULL, key, iv, !is_read);
+
+ /* Needed for "composite" AEADs, such as RC4-HMAC-MD5 */
+ if ((EVP_CIPHER_flags(cipher) & EVP_CIPH_FLAG_AEAD_CIPHER) &&
+ mac_secret_size)
+ EVP_CIPHER_CTX_ctrl(cipher_ctx, EVP_CTRL_AEAD_SET_MAC_KEY,
+ mac_secret_size, (unsigned char *)mac_secret);
+
+ if (is_export) {
+ OPENSSL_cleanse(export_tmp1, sizeof(export_tmp1));
+ OPENSSL_cleanse(export_tmp2, sizeof(export_tmp2));
+ OPENSSL_cleanse(export_iv1, sizeof(export_iv1));
+ OPENSSL_cleanse(export_iv2, sizeof(export_iv2));
+ }
+
+ return (1);
+
+err:
+ SSLerr(SSL_F_TLS1_CHANGE_CIPHER_STATE_CIPHER, ERR_R_MALLOC_FAILURE);
+err2:
+ return (0);
+}
+
+int
+tls1_change_cipher_state(SSL *s, int which)
+{
const unsigned char *client_write_mac_secret, *server_write_mac_secret;
const unsigned char *client_write_key, *server_write_key;
const unsigned char *client_write_iv, *server_write_iv;
const unsigned char *mac_secret, *key, *iv;
int mac_secret_size, key_len, iv_len;
- unsigned char *key_block, *exp_label, *seq;
-
- EVP_CIPHER_CTX *cipher_ctx;
+ unsigned char *key_block, *seq;
const EVP_CIPHER *cipher;
+ char is_read, use_client_keys;
+ int is_export;
+
#ifndef OPENSSL_NO_COMP
const SSL_COMP *comp;
#endif
- const EVP_MD *mac;
- int mac_type;
- EVP_MD_CTX *mac_ctx;
- EVP_PKEY *mac_key;
- int is_export, exp_label_len;
- char is_read, use_client_keys;
is_export = SSL_C_IS_EXPORT(s->s3->tmp.new_cipher);
cipher = s->s3->tmp.new_sym_enc;
- mac = s->s3->tmp.new_hash;
- mac_type = s->s3->tmp.new_mac_pkey_type;
/*
* is_read is true if we have just read a ChangeCipherSpec message,
@@ -381,51 +519,6 @@ tls1_change_cipher_state(SSL *s, int which)
}
#endif
- if (is_read) {
- if (s->s3->tmp.new_cipher->algorithm2 & TLS1_STREAM_MAC)
- s->mac_flags |= SSL_MAC_FLAG_READ_MAC_STREAM;
- else
- s->mac_flags &= ~SSL_MAC_FLAG_READ_MAC_STREAM;
-
- EVP_CIPHER_CTX_free(s->enc_read_ctx);
- s->enc_read_ctx = NULL;
- EVP_MD_CTX_destroy(s->read_hash);
- s->read_hash = NULL;
-
- if ((cipher_ctx = EVP_CIPHER_CTX_new()) == NULL)
- goto err;
- s->enc_read_ctx = cipher_ctx;
- if ((mac_ctx = EVP_MD_CTX_create()) == NULL)
- goto err;
- s->read_hash = mac_ctx;
- } else {
- if (s->s3->tmp.new_cipher->algorithm2 & TLS1_STREAM_MAC)
- s->mac_flags |= SSL_MAC_FLAG_WRITE_MAC_STREAM;
- else
- s->mac_flags &= ~SSL_MAC_FLAG_WRITE_MAC_STREAM;
-
- /*
- * DTLS fragments retain a pointer to the compression, cipher
- * and hash contexts, so that it can restore state in order
- * to perform retransmissions. As such, we cannot free write
- * contexts that are used for DTLS - these are instead freed
- * by DTLS when its frees a ChangeCipherSpec fragment.
- */
- if (!SSL_IS_DTLS(s)) {
- EVP_CIPHER_CTX_free(s->enc_write_ctx);
- s->enc_write_ctx = NULL;
- EVP_MD_CTX_destroy(s->write_hash);
- s->write_hash = NULL;
- }
- if ((cipher_ctx = EVP_CIPHER_CTX_new()) == NULL)
- goto err;
- s->enc_write_ctx = cipher_ctx;
- if ((mac_ctx = EVP_MD_CTX_create()) == NULL)
- goto err;
- s->write_hash = mac_ctx;
-
- }
-
/*
* Reset sequence number to zero - for DTLS this is handled in
* dtls1_reset_seq_numbers().
@@ -486,74 +579,8 @@ tls1_change_cipher_state(SSL *s, int which)
s->s3->write_mac_secret_size = mac_secret_size;
}
- if (!(EVP_CIPHER_flags(cipher) & EVP_CIPH_FLAG_AEAD_CIPHER)) {
- mac_key = EVP_PKEY_new_mac_key(mac_type, NULL,
- mac_secret, mac_secret_size);
- if (mac_key == NULL)
- goto err;
- EVP_DigestSignInit(mac_ctx, NULL, mac, NULL, mac_key);
- EVP_PKEY_free(mac_key);
- }
-
- if (is_export) {
- /*
- * Both the read and write key/iv are set to the same value
- * since only the correct one will be used :-).
- */
- if (use_client_keys) {
- exp_label = TLS_MD_CLIENT_WRITE_KEY_CONST;
- exp_label_len = TLS_MD_CLIENT_WRITE_KEY_CONST_SIZE;
- } else {
- exp_label = TLS_MD_SERVER_WRITE_KEY_CONST;
- exp_label_len = TLS_MD_SERVER_WRITE_KEY_CONST_SIZE;
- }
-
- if (!tls1_PRF(ssl_get_algorithm2(s),
- exp_label, exp_label_len,
- s->s3->client_random, SSL3_RANDOM_SIZE,
- s->s3->server_random, SSL3_RANDOM_SIZE,
- NULL, 0, NULL, 0, key, key_len, export_tmp1, export_tmp2,
- EVP_CIPHER_key_length(cipher)))
- goto err2;
- key = export_tmp1;
-
- if (iv_len > 0) {
- if (!tls1_PRF(ssl_get_algorithm2(s),
- TLS_MD_IV_BLOCK_CONST, TLS_MD_IV_BLOCK_CONST_SIZE,
- s->s3->client_random, SSL3_RANDOM_SIZE,
- s->s3->server_random, SSL3_RANDOM_SIZE,
- NULL, 0, NULL, 0, empty, 0,
- export_iv1, export_iv2, iv_len * 2))
- goto err2;
- if (use_client_keys)
- iv = export_iv1;
- else
- iv = &(export_iv1[iv_len]);
- }
- }
-
- if (EVP_CIPHER_mode(cipher) == EVP_CIPH_GCM_MODE) {
- EVP_CipherInit_ex(cipher_ctx, cipher, NULL, key, NULL,
- !is_read);
- EVP_CIPHER_CTX_ctrl(cipher_ctx, EVP_CTRL_GCM_SET_IV_FIXED,
- iv_len, (unsigned char *)iv);
- } else
- EVP_CipherInit_ex(cipher_ctx, cipher, NULL, key, iv, !is_read);
-
- /* Needed for "composite" AEADs, such as RC4-HMAC-MD5 */
- if ((EVP_CIPHER_flags(cipher) & EVP_CIPH_FLAG_AEAD_CIPHER) &&
- mac_secret_size)
- EVP_CIPHER_CTX_ctrl(cipher_ctx, EVP_CTRL_AEAD_SET_MAC_KEY,
- mac_secret_size, (unsigned char *)mac_secret);
-
- if (is_export) {
- OPENSSL_cleanse(export_tmp1, sizeof(export_tmp1));
- OPENSSL_cleanse(export_tmp2, sizeof(export_tmp2));
- OPENSSL_cleanse(export_iv1, sizeof(export_iv1));
- OPENSSL_cleanse(export_iv2, sizeof(export_iv2));
- }
-
- return (1);
+ return tls1_change_cipher_state_cipher(s, is_read, use_client_keys,
+ mac_secret, mac_secret_size, key, key_len, iv, iv_len);
err:
SSLerr(SSL_F_TLS1_CHANGE_CIPHER_STATE, ERR_R_MALLOC_FAILURE);