int tls1_change_cipher_state(SSL *ssl, int which) { /* Ensure the key block is set up. */ if (!tls1_setup_key_block(ssl)) { return 0; } /* 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; size_t mac_secret_len = ssl->s3->tmp.new_mac_secret_len; size_t key_len = ssl->s3->tmp.new_key_len; size_t iv_len = ssl->s3->tmp.new_fixed_iv_len; assert((mac_secret_len + key_len + iv_len) * 2 == ssl->s3->tmp.key_block_length); const uint8_t *key_data = ssl->s3->tmp.key_block; const uint8_t *client_write_mac_secret = key_data; key_data += mac_secret_len; const uint8_t *server_write_mac_secret = key_data; key_data += mac_secret_len; const uint8_t *client_write_key = key_data; key_data += key_len; const uint8_t *server_write_key = key_data; key_data += key_len; const uint8_t *client_write_iv = key_data; key_data += iv_len; const uint8_t *server_write_iv = key_data; key_data += iv_len; const uint8_t *mac_secret, *key, *iv; if (use_client_keys) { mac_secret = client_write_mac_secret; key = client_write_key; iv = client_write_iv; } else { mac_secret = server_write_mac_secret; key = server_write_key; iv = server_write_iv; } SSL_AEAD_CTX *aead_ctx = SSL_AEAD_CTX_new(is_read ? evp_aead_open : evp_aead_seal, ssl3_protocol_version(ssl), ssl->s3->tmp.new_cipher, key, key_len, mac_secret, mac_secret_len, iv, iv_len); if (aead_ctx == NULL) { return 0; } if (is_read) { return ssl->method->set_read_state(ssl, aead_ctx); } return ssl->method->set_write_state(ssl, aead_ctx); }
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 uint8_t *client_write_mac_secret, *server_write_mac_secret, *mac_secret; const uint8_t *client_write_key, *server_write_key, *key; const uint8_t *client_write_iv, *server_write_iv, *iv; const EVP_AEAD *aead = s->s3->tmp.new_aead; size_t key_len, iv_len, mac_secret_len; const uint8_t *key_data; /* Reset sequence number to zero. */ if (!SSL_IS_DTLS(s)) { memset(is_read ? s->s3->read_sequence : s->s3->write_sequence, 0, 8); } mac_secret_len = s->s3->tmp.new_mac_secret_len; iv_len = s->s3->tmp.new_fixed_iv_len; if (aead == NULL) { OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return 0; } key_len = EVP_AEAD_key_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 and IV key bytes. */ if (key_len < mac_secret_len + iv_len) { OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return 0; } key_len -= mac_secret_len + iv_len; } key_data = s->s3->tmp.key_block; client_write_mac_secret = key_data; key_data += mac_secret_len; server_write_mac_secret = key_data; key_data += mac_secret_len; client_write_key = key_data; key_data += key_len; server_write_key = key_data; key_data += key_len; client_write_iv = key_data; key_data += iv_len; server_write_iv = key_data; key_data += iv_len; if (use_client_keys) { mac_secret = client_write_mac_secret; key = client_write_key; iv = client_write_iv; } else { mac_secret = server_write_mac_secret; key = server_write_key; iv = server_write_iv; } if (key_data - s->s3->tmp.key_block != s->s3->tmp.key_block_length) { OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return 0; } if (is_read) { SSL_AEAD_CTX_free(s->aead_read_ctx); s->aead_read_ctx = SSL_AEAD_CTX_new( evp_aead_open, ssl3_version_from_wire(s, s->version), s->s3->tmp.new_cipher, key, key_len, mac_secret, mac_secret_len, iv, iv_len); return s->aead_read_ctx != NULL; } SSL_AEAD_CTX_free(s->aead_write_ctx); s->aead_write_ctx = SSL_AEAD_CTX_new( evp_aead_seal, ssl3_version_from_wire(s, s->version), s->s3->tmp.new_cipher, key, key_len, mac_secret, mac_secret_len, iv, iv_len); if (s->aead_write_ctx == NULL) { return 0; } s->s3->need_record_splitting = 0; if (!SSL_USE_EXPLICIT_IV(s) && (s->mode & SSL_MODE_CBC_RECORD_SPLITTING) != 0 && SSL_CIPHER_is_block_cipher(s->s3->tmp.new_cipher)) { /* Enable 1/n-1 record-splitting to randomize the IV. See * https://www.openssl.org/~bodo/tls-cbc.txt and the BEAST attack. */ s->s3->need_record_splitting = 1; } return 1; }
int tls13_set_traffic_key(SSL *ssl, enum tls_record_type_t type, enum evp_aead_direction_t direction, const uint8_t *traffic_secret, size_t traffic_secret_len) { if (traffic_secret_len > 0xff) { OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW); return 0; } const char *phase; switch (type) { case type_early_handshake: phase = "early handshake key expansion, "; break; case type_early_data: phase = "early application data key expansion, "; break; case type_handshake: phase = "handshake key expansion, "; break; case type_data: phase = "application data key expansion, "; break; default: return 0; } size_t phase_len = strlen(phase); const char *purpose = "client write key"; if ((ssl->server && direction == evp_aead_seal) || (!ssl->server && direction == evp_aead_open)) { purpose = "server write key"; } size_t purpose_len = strlen(purpose); /* The longest label has length 38 (type_early_data) + 16 (either purpose * value). */ uint8_t label[38 + 16]; size_t label_len = phase_len + purpose_len; if (label_len > sizeof(label)) { assert(0); return 0; } memcpy(label, phase, phase_len); memcpy(label + phase_len, purpose, purpose_len); /* Look up cipher suite properties. */ const EVP_AEAD *aead; const EVP_MD *digest = ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl)); size_t mac_secret_len, fixed_iv_len; if (!ssl_cipher_get_evp_aead(&aead, &mac_secret_len, &fixed_iv_len, ssl->session->cipher, ssl3_protocol_version(ssl))) { return 0; } /* Derive the key. */ size_t key_len = EVP_AEAD_key_length(aead); uint8_t key[EVP_AEAD_MAX_KEY_LENGTH]; if (!hkdf_expand_label(key, digest, traffic_secret, traffic_secret_len, label, label_len, NULL, 0, key_len)) { return 0; } /* The IV's label ends in "iv" instead of "key". */ if (label_len < 3) { assert(0); return 0; } label_len--; label[label_len - 2] = 'i'; label[label_len - 1] = 'v'; /* Derive the IV. */ size_t iv_len = EVP_AEAD_nonce_length(aead); uint8_t iv[EVP_AEAD_MAX_NONCE_LENGTH]; if (!hkdf_expand_label(iv, digest, traffic_secret, traffic_secret_len, label, label_len, NULL, 0, iv_len)) { return 0; } SSL_AEAD_CTX *traffic_aead = SSL_AEAD_CTX_new(direction, ssl3_protocol_version(ssl), ssl->session->cipher, key, key_len, NULL, 0, iv, iv_len); if (traffic_aead == NULL) { return 0; } if (direction == evp_aead_open) { if (!ssl->method->set_read_state(ssl, traffic_aead)) { return 0; } } else { if (!ssl->method->set_write_state(ssl, traffic_aead)) { return 0; } } /* Save the traffic secret. */ if (direction == evp_aead_open) { memcpy(ssl->s3->read_traffic_secret, traffic_secret, traffic_secret_len); ssl->s3->read_traffic_secret_len = traffic_secret_len; } else { memcpy(ssl->s3->write_traffic_secret, traffic_secret, traffic_secret_len); ssl->s3->write_traffic_secret_len = traffic_secret_len; } return 1; }