blob: 4764ea1962e4bf8b6abd6280f8cbde4cff98475a [file] [log] [blame]
/** @file
*
* QUIC Handshake Protocol (TLS to Secure QUIC)
*
* @section license License
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "QUICTLS.h"
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <openssl/bio.h>
#include "QUICPacketProtectionKeyInfo.h"
#include "QUICDebugNames.h"
constexpr static char tag[] = "quic_tls";
static const char *
content_type_str(int type)
{
switch (type) {
case SSL3_RT_CHANGE_CIPHER_SPEC:
return "CHANGE_CIPHER_SPEC";
case SSL3_RT_ALERT:
return "ALERT";
case SSL3_RT_HANDSHAKE:
return "HANDSHAKE";
case SSL3_RT_APPLICATION_DATA:
return "APPLICATION_DATA";
case SSL3_RT_HEADER:
// The buf contains the record header bytes only
return "HEADER";
default:
return "UNKNOWN";
}
}
static const char *
hs_type_str(int type)
{
switch (type) {
case SSL3_MT_CLIENT_HELLO:
return "CLIENT_HELLO";
case SSL3_MT_SERVER_HELLO:
return "SERVER_HELLO";
case SSL3_MT_NEWSESSION_TICKET:
return "NEWSESSION_TICKET";
case SSL3_MT_END_OF_EARLY_DATA:
return "END_OF_EARLY_DATA";
case SSL3_MT_ENCRYPTED_EXTENSIONS:
return "ENCRYPTED_EXTENSIONS";
case SSL3_MT_CERTIFICATE:
return "CERTIFICATE";
case SSL3_MT_CERTIFICATE_VERIFY:
return "CERTIFICATE_VERIFY";
case SSL3_MT_FINISHED:
return "FINISHED";
case SSL3_MT_KEY_UPDATE:
return "KEY_UPDATE";
case SSL3_MT_MESSAGE_HASH:
return "MESSAGE_HASH";
default:
return "UNKNOWN";
}
}
SSL *
QUICTLS::ssl_handle()
{
return this->_ssl;
}
std::shared_ptr<const QUICTransportParameters>
QUICTLS::local_transport_parameters()
{
return this->_local_transport_parameters;
}
std::shared_ptr<const QUICTransportParameters>
QUICTLS::remote_transport_parameters()
{
return this->_remote_transport_parameters;
}
void
QUICTLS::set_remote_transport_parameters(std::shared_ptr<const QUICTransportParameters> tp)
{
this->_remote_transport_parameters = tp;
}
const char *
QUICTLS::session_file() const
{
return this->_session_file;
}
const char *
QUICTLS::keylog_file() const
{
return this->_keylog_file;
}
QUICTLS::~QUICTLS()
{
SSL_free(this->_ssl);
}
int
QUICTLS::handshake(QUICHandshakeMsgs **out, const QUICHandshakeMsgs *in)
{
if (this->is_handshake_finished()) {
if (in != nullptr && in->offsets[4] != 0) {
return this->_process_post_handshake_messages(*out, in);
}
return 1;
}
return this->_handshake(out, in);
}
void
QUICTLS::reset()
{
SSL_clear(this->_ssl);
}
uint64_t
QUICTLS::convert_to_quic_trans_error_code(uint8_t alert)
{
return static_cast<uint64_t>(QUICTransErrorCode::CRYPTO_ERROR) + alert;
}
bool
QUICTLS::is_handshake_finished() const
{
return SSL_is_init_finished(this->_ssl);
}
bool
QUICTLS::is_ready_to_derive() const
{
if (this->_netvc_context == NET_VCONNECTION_IN) {
return SSL_get_current_cipher(this->_ssl) != nullptr;
} else {
return this->is_handshake_finished();
}
}
int
QUICTLS::initialize_key_materials(QUICConnectionId cid)
{
this->_pp_key_info.set_cipher_initial(EVP_aes_128_gcm());
this->_pp_key_info.set_cipher_for_hp_initial(EVP_aes_128_ecb());
// Generate keys
if (is_debug_tag_set(tag)) {
Debug(tag, "Generating %s keys with cid %s", QUICDebugNames::key_phase(QUICKeyPhase::INITIAL), cid.hex().c_str());
}
uint8_t *client_key_for_hp;
uint8_t *client_key;
uint8_t *client_iv;
size_t client_key_for_hp_len;
size_t client_key_len;
size_t *client_iv_len;
uint8_t *server_key_for_hp;
uint8_t *server_key;
uint8_t *server_iv;
size_t server_key_for_hp_len;
size_t server_key_len;
size_t *server_iv_len;
if (this->_netvc_context == NET_VCONNECTION_IN) {
client_key_for_hp = this->_pp_key_info.decryption_key_for_hp(QUICKeyPhase::INITIAL);
client_key_for_hp_len = this->_pp_key_info.decryption_key_for_hp_len(QUICKeyPhase::INITIAL);
client_key = this->_pp_key_info.decryption_key(QUICKeyPhase::INITIAL);
client_key_len = this->_pp_key_info.decryption_key_len(QUICKeyPhase::INITIAL);
client_iv = this->_pp_key_info.decryption_iv(QUICKeyPhase::INITIAL);
client_iv_len = this->_pp_key_info.decryption_iv_len(QUICKeyPhase::INITIAL);
server_key_for_hp = this->_pp_key_info.encryption_key_for_hp(QUICKeyPhase::INITIAL);
server_key_for_hp_len = this->_pp_key_info.encryption_key_for_hp_len(QUICKeyPhase::INITIAL);
server_key = this->_pp_key_info.encryption_key(QUICKeyPhase::INITIAL);
server_key_len = this->_pp_key_info.encryption_key_len(QUICKeyPhase::INITIAL);
server_iv = this->_pp_key_info.encryption_iv(QUICKeyPhase::INITIAL);
server_iv_len = this->_pp_key_info.encryption_iv_len(QUICKeyPhase::INITIAL);
} else {
client_key_for_hp = this->_pp_key_info.encryption_key_for_hp(QUICKeyPhase::INITIAL);
client_key_for_hp_len = this->_pp_key_info.encryption_key_for_hp_len(QUICKeyPhase::INITIAL);
client_key = this->_pp_key_info.encryption_key(QUICKeyPhase::INITIAL);
client_key_len = this->_pp_key_info.encryption_key_len(QUICKeyPhase::INITIAL);
client_iv = this->_pp_key_info.encryption_iv(QUICKeyPhase::INITIAL);
client_iv_len = this->_pp_key_info.encryption_iv_len(QUICKeyPhase::INITIAL);
server_key_for_hp = this->_pp_key_info.decryption_key_for_hp(QUICKeyPhase::INITIAL);
server_key_for_hp_len = this->_pp_key_info.decryption_key_for_hp_len(QUICKeyPhase::INITIAL);
server_key = this->_pp_key_info.decryption_key(QUICKeyPhase::INITIAL);
server_key_len = this->_pp_key_info.decryption_key_len(QUICKeyPhase::INITIAL);
server_iv = this->_pp_key_info.decryption_iv(QUICKeyPhase::INITIAL);
server_iv_len = this->_pp_key_info.decryption_iv_len(QUICKeyPhase::INITIAL);
}
this->_keygen_for_client.generate(client_key_for_hp, client_key, client_iv, client_iv_len, cid);
this->_keygen_for_server.generate(server_key_for_hp, server_key, server_iv, server_iv_len, cid);
this->_pp_key_info.set_decryption_key_available(QUICKeyPhase::INITIAL);
this->_pp_key_info.set_encryption_key_available(QUICKeyPhase::INITIAL);
this->_print_km("initial - server", server_key_for_hp, server_key_for_hp_len, server_key, server_key_len, server_iv,
*server_iv_len);
this->_print_km("initial - client", client_key_for_hp, client_key_for_hp_len, client_key, client_key_len, client_iv,
*client_iv_len);
return 1;
}
void
QUICTLS::update_negotiated_cipher()
{
this->_store_negotiated_cipher();
this->_store_negotiated_cipher_for_hp();
}
void
QUICTLS::update_key_materials_for_read(QUICEncryptionLevel level, const uint8_t *secret, size_t secret_len)
{
if (this->_state == HandshakeState::ABORTED) {
return;
}
QUICKeyPhase phase;
const EVP_CIPHER *cipher;
switch (level) {
case QUICEncryptionLevel::ZERO_RTT:
phase = QUICKeyPhase::ZERO_RTT;
cipher = this->_pp_key_info.get_cipher(phase);
break;
case QUICEncryptionLevel::HANDSHAKE:
this->_update_encryption_level(QUICEncryptionLevel::HANDSHAKE);
phase = QUICKeyPhase::HANDSHAKE;
break;
case QUICEncryptionLevel::ONE_RTT:
this->_update_encryption_level(QUICEncryptionLevel::ONE_RTT);
// TODO Support Key Update
phase = QUICKeyPhase::PHASE_0;
break;
default:
phase = QUICKeyPhase::INITIAL;
break;
}
QUICHKDF hkdf(this->_get_handshake_digest());
uint8_t *key_for_hp;
uint8_t *key;
uint8_t *iv;
size_t key_for_hp_len;
size_t key_len;
size_t *iv_len;
cipher = this->_pp_key_info.get_cipher(phase);
key_for_hp = this->_pp_key_info.decryption_key_for_hp(phase);
key_for_hp_len = this->_pp_key_info.decryption_key_for_hp_len(phase);
key = this->_pp_key_info.decryption_key(phase);
key_len = this->_pp_key_info.decryption_key_len(phase);
iv = this->_pp_key_info.decryption_iv(phase);
iv_len = this->_pp_key_info.decryption_iv_len(phase);
if (this->_netvc_context == NET_VCONNECTION_IN) {
this->_keygen_for_client.regenerate(key_for_hp, key, iv, iv_len, secret, secret_len, cipher, hkdf);
this->_print_km("update - client", key_for_hp, key_for_hp_len, key, key_len, iv, *iv_len, secret, secret_len, phase);
} else {
this->_keygen_for_server.regenerate(key_for_hp, key, iv, iv_len, secret, secret_len, cipher, hkdf);
this->_print_km("update - server", key_for_hp, key_for_hp_len, key, key_len, iv, *iv_len, secret, secret_len, phase);
}
this->_pp_key_info.set_decryption_key_available(phase);
}
void
QUICTLS::update_key_materials_for_write(QUICEncryptionLevel level, const uint8_t *secret, size_t secret_len)
{
if (this->_state == HandshakeState::ABORTED) {
return;
}
QUICKeyPhase phase;
const EVP_CIPHER *cipher = nullptr;
switch (level) {
case QUICEncryptionLevel::ZERO_RTT:
phase = QUICKeyPhase::ZERO_RTT;
cipher = this->_pp_key_info.get_cipher(phase);
break;
case QUICEncryptionLevel::HANDSHAKE:
this->_update_encryption_level(QUICEncryptionLevel::HANDSHAKE);
phase = QUICKeyPhase::HANDSHAKE;
cipher = this->_pp_key_info.get_cipher(phase);
break;
case QUICEncryptionLevel::ONE_RTT:
this->_update_encryption_level(QUICEncryptionLevel::ONE_RTT);
phase = QUICKeyPhase::PHASE_0;
cipher = this->_pp_key_info.get_cipher(phase);
break;
default:
phase = QUICKeyPhase::INITIAL;
break;
}
QUICHKDF hkdf(this->_get_handshake_digest());
uint8_t *key_for_hp;
uint8_t *key;
uint8_t *iv;
size_t key_for_hp_len;
size_t key_len;
size_t *iv_len;
key_for_hp = this->_pp_key_info.encryption_key_for_hp(phase);
key_for_hp_len = this->_pp_key_info.encryption_key_for_hp_len(phase);
key = this->_pp_key_info.encryption_key(phase);
key_len = this->_pp_key_info.encryption_key_len(phase);
iv = this->_pp_key_info.encryption_iv(phase);
iv_len = this->_pp_key_info.encryption_iv_len(phase);
if (this->_netvc_context == NET_VCONNECTION_IN) {
this->_keygen_for_server.regenerate(key_for_hp, key, iv, iv_len, secret, secret_len, cipher, hkdf);
this->_print_km("update - server", key_for_hp, key_for_hp_len, key, key_len, iv, *iv_len, secret, secret_len, phase);
} else {
this->_keygen_for_client.regenerate(key_for_hp, key, iv, iv_len, secret, secret_len, cipher, hkdf);
this->_print_km("update - client", key_for_hp, key_for_hp_len, key, key_len, iv, *iv_len, secret, secret_len, phase);
}
this->_pp_key_info.set_encryption_key_available(phase);
}
const char *
QUICTLS::negotiated_cipher_suite() const
{
return SSL_get_cipher_name(this->_ssl);
}
void
QUICTLS::negotiated_application_name(const uint8_t **name, unsigned int *len) const
{
SSL_get0_alpn_selected(this->_ssl, name, len);
}
QUICEncryptionLevel
QUICTLS::current_encryption_level() const
{
return this->_current_level;
}
void
QUICTLS::abort_handshake()
{
this->_state = HandshakeState::ABORTED;
return;
}
void
QUICTLS::set_ready_for_write()
{
this->_should_flush = true;
}
void
QUICTLS::on_handshake_data_generated(QUICEncryptionLevel level, const uint8_t *data, size_t len)
{
int index = static_cast<int>(level);
int next_index = index + 1;
size_t offset = this->_out.offsets[next_index];
size_t next_level_offset = offset + len;
memcpy(this->_out.buf + offset, data, len);
for (int i = next_index; i < 5; ++i) {
this->_out.offsets[i] = next_level_offset;
}
}
void
QUICTLS::on_tls_alert(uint8_t alert)
{
this->_has_crypto_error = true;
this->_crypto_error = QUICTLS::convert_to_quic_trans_error_code(alert);
}
bool
QUICTLS::has_crypto_error() const
{
return this->_has_crypto_error;
}
uint64_t
QUICTLS::crypto_error() const
{
return this->_crypto_error;
}
int
QUICTLS::_handshake(QUICHandshakeMsgs **out, const QUICHandshakeMsgs *in)
{
ink_assert(this->_ssl != nullptr);
if (this->_state == HandshakeState::ABORTED) {
return 0;
}
int err = SSL_ERROR_NONE;
ERR_clear_error();
int ret = 0;
SSL_set_msg_callback(this->_ssl, QUICTLS::_msg_cb);
this->_out.offsets[0] = 0;
this->_out.offsets[1] = 0;
this->_out.offsets[2] = 0;
this->_out.offsets[3] = 0;
this->_out.offsets[4] = 0;
if (in) {
this->_pass_quic_data_to_ssl_impl(*in);
}
if (this->_netvc_context == NET_VCONNECTION_IN) {
if (!this->_early_data_processed) {
if (auto ret = this->_read_early_data(); ret == 0) {
this->_early_data_processed = true;
} else if (ret < 0) {
return 0;
} else {
// Early data is not arrived yet -- can be multiple initial packets
}
}
ret = SSL_accept(this->_ssl);
} else {
if (!this->_early_data_processed) {
if (this->_write_early_data()) {
this->_early_data_processed = true;
}
}
ret = SSL_connect(this->_ssl);
}
if (ret <= 0) {
err = SSL_get_error(this->_ssl, ret);
switch (err) {
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
break;
default:
char err_buf[256] = {0};
ERR_error_string_n(ERR_get_error(), err_buf, sizeof(err_buf));
Debug(tag, "Handshake: %s", err_buf);
return ret;
}
}
if (this->_should_flush) {
this->_should_flush = false;
*out = &this->_out;
} else {
*out = nullptr;
}
return 1;
}
void
QUICTLS::_update_encryption_level(QUICEncryptionLevel level)
{
if (this->_current_level < level) {
this->_current_level = level;
}
return;
}
void
QUICTLS::_print_km(const char *header, const uint8_t *key_for_hp, size_t key_for_hp_len, const uint8_t *key, size_t key_len,
const uint8_t *iv, size_t iv_len, const uint8_t *secret, size_t secret_len, QUICKeyPhase phase)
{
if (is_debug_tag_set("vv_quic_crypto")) {
Debug("vv_quic_crypto", "%s - %s", header, QUICDebugNames::key_phase(phase));
uint8_t print_buf[128];
if (secret) {
QUICDebug::to_hex(print_buf, static_cast<const uint8_t *>(secret), secret_len);
Debug("vv_quic_crypto", "secret=%s", print_buf);
}
QUICDebug::to_hex(print_buf, key, key_len);
Debug("vv_quic_crypto", "key=%s", print_buf);
QUICDebug::to_hex(print_buf, iv, iv_len);
Debug("vv_quic_crypto", "iv=%s", print_buf);
QUICDebug::to_hex(print_buf, key_for_hp, key_for_hp_len);
Debug("vv_quic_crypto", "hp=%s", print_buf);
}
}
void
QUICTLS::_print_hs_message(int content_type, const void *buf, size_t len)
{
if ((content_type == SSL3_RT_HANDSHAKE || content_type == SSL3_RT_ALERT)) {
int msg_type = reinterpret_cast<const uint8_t *>(buf)[0];
Debug(tag, "%s (%d), %s (%d) len=%zu", content_type_str(content_type), content_type, hs_type_str(msg_type), msg_type, len);
}
}