+ else
+ /* make sure it's intialized in case we exit later with an error */
+ EVP_CIPHER_CTX_init(s->enc_write_ctx);
+
+ cipher_ctx = s->enc_write_ctx;
+ mac_ctx = ssl_replace_hash(&s->write_hash, NULL);
+
+ memcpy(s->s3->write_mac_secret, mac_secret, mac_secret_len);
+ s->s3->write_mac_secret_size = mac_secret_len;
+ }
+
+ if (is_export)
+ {
+ /* In here I set both the read and write key/iv to the
+ * same value since only the correct one will be used :-).
+ */
+ const unsigned char *label;
+ unsigned label_len;
+
+ if (use_client_keys)
+ {
+ label = (const unsigned char*) TLS_MD_CLIENT_WRITE_KEY_CONST;
+ label_len = TLS_MD_CLIENT_WRITE_KEY_CONST_SIZE;
+ }
+ else
+ {
+ label = (const unsigned char*) TLS_MD_SERVER_WRITE_KEY_CONST;
+ label_len = TLS_MD_SERVER_WRITE_KEY_CONST_SIZE;
+ }
+
+ if (!tls1_PRF(ssl_get_algorithm2(s),
+ label, label_len,
+ s->s3->client_random, SSL3_RANDOM_SIZE,
+ s->s3->server_random, SSL3_RANDOM_SIZE,
+ NULL, 0, NULL, 0,
+ key /* secret */, key_len /* secret length */,
+ export_tmp1 /* output */,
+ export_tmp2 /* scratch space */,
+ EVP_CIPHER_key_length(s->s3->tmp.new_sym_enc) /* output length */))
+ return 0;
+ key = export_tmp1;
+
+ if (iv_len > 0)
+ {
+ static const unsigned char empty[] = "";
+
+ 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 /* secret */ ,0 /* secret length */,
+ export_iv1 /* output */,
+ export_iv2 /* scratch space */,
+ iv_len * 2 /* output length */))
+ return 0;
+
+ if (use_client_keys)
+ iv = export_iv1;
+ else
+ iv = &export_iv1[iv_len];
+ }
+ }
+
+ /* is_aead_cipher indicates whether the EVP_CIPHER implements an AEAD
+ * interface. This is different from the newer EVP_AEAD interface. */
+ is_aead_cipher = (EVP_CIPHER_flags(cipher) & EVP_CIPH_FLAG_AEAD_CIPHER) != 0;
+
+ if (!is_aead_cipher)
+ {
+ EVP_PKEY *mac_key =
+ EVP_PKEY_new_mac_key(s->s3->tmp.new_mac_pkey_type,
+ NULL, mac_secret, mac_secret_len);
+ if (!mac_key)
+ return 0;
+ EVP_DigestSignInit(mac_ctx, NULL, s->s3->tmp.new_hash, NULL, mac_key);
+ EVP_PKEY_free(mac_key);
+ }
+
+ if (EVP_CIPHER_mode(cipher) == EVP_CIPH_GCM_MODE)
+ {
+ EVP_CipherInit_ex(cipher_ctx, cipher, NULL /* engine */, key,
+ NULL /* iv */, !is_read);
+ EVP_CIPHER_CTX_ctrl(cipher_ctx, EVP_CTRL_GCM_SET_IV_FIXED, iv_len, (void*) iv);
+ }
+ else
+ EVP_CipherInit_ex(cipher_ctx, cipher, NULL /* engine */, key, iv, !is_read);
+
+ /* Needed for "composite" AEADs, such as RC4-HMAC-MD5 */
+ if (is_aead_cipher && mac_secret_len > 0)
+ EVP_CIPHER_CTX_ctrl(cipher_ctx, EVP_CTRL_AEAD_SET_MAC_KEY,
+ mac_secret_len, (void*) mac_secret);
+
+ if (is_export)
+ {
+ OPENSSL_cleanse(export_tmp1, sizeof(export_tmp1));
+ OPENSSL_cleanse(export_tmp2, sizeof(export_tmp1));
+ 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);
+ return 0;
+ }
+
+int tls1_change_cipher_state(SSL *s, int which)
+ {
+ /* is_read is true if we have just read a ChangeCipherSpec message -
+ * i.e. we need to update the read cipherspec. Otherwise we have just
+ * written one. */
+ const char is_read = (which & SSL3_CC_READ) != 0;
+ /* use_client_keys is true if we wish to use the keys for the "client
+ * write" direction. This is the case if we're a client sending a
+ * ChangeCipherSpec, or a server reading a client's ChangeCipherSpec. */
+ const char use_client_keys = which == SSL3_CHANGE_CIPHER_CLIENT_WRITE ||
+ which == SSL3_CHANGE_CIPHER_SERVER_READ;
+ const unsigned char *client_write_mac_secret, *server_write_mac_secret, *mac_secret;
+ const unsigned char *client_write_key, *server_write_key, *key;
+ const unsigned char *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;
+ const unsigned char *key_data;
+ const char is_export = SSL_C_IS_EXPORT(s->s3->tmp.new_cipher) != 0;
+
+ /* Update compression contexts. */
+#ifndef OPENSSL_NO_COMP
+ const SSL_COMP *comp = s->s3->tmp.new_compression;
+
+ if (is_read)
+ {