OPENSSL源码阅读(8)
前言
上一回说了非复用状态/完整握手状态下发送Finised消息时自动机的变化,这次我们来说说服务端读取客户端Finished时发生了什么。有一点需要注意,这里并不区分客户端是不是复用连接,对于完整握手和复用连接服务端读取finished消息的处理流程都一致。
从Finished发送完毕说起
我们上次讲了,发送Finished之后重新进入写自动机调用transition
函数,因为当前的握手状态为st->hand_state == TLS_ST_SW_FINISHED;
,所以进入新的状态为 st->hand_state = TLS_ST_EARLY_DATA;
。进入pre_work
函数之后返回WORK_FINISHED_CONTINUE
,而get_construct_message_f
会什么都不做直接到post_work
函数,返回WORK_FINISHED_CONTINUE
,又转到写自动机的transition
,因为当前状态是st->hand_state == TLS_ST_EARLY_DATA;
。直接返回WRITE_TRAN_FINISHED;
。写自动机返回后,返回SUB_STATE_FINISHED
,写自动机的小状态就暂时结束了。重新初始化读自动机,因此这次读客户端Finished消息,需要分析读自动机。
读自动机transition函数及之前流程
此次进入transition
函数之前,先读取tls_get_message_header
函数,函数会获取当前消息种类为Finished消息。因为当前的握手状态为st->hand_state == TLS_ST_EARLY_DATA;
,我们这里虽然不区分复用或完整的ECDHE握手,但是我们假设是没有HelloRetryRequest请求,也不接受EarlyData的,所以代码会执行到改变当前握手状态为服务端读Finished消息阶段st->hand_state = TLS_ST_SR_FINISHED;
。代码如下:
static int ossl_statem_server13_read_transition(SSL *s, int mt)
{
OSSL_STATEM *st = &s->statem;
/*
* Note: There is no case for TLS_ST_BEFORE because at that stage we have
* not negotiated TLSv1.3 yet, so that case is handled by
* ossl_statem_server_read_transition()
*/
switch (st->hand_state) {
default:
break;
case TLS_ST_EARLY_DATA:
if (s->hello_retry_request == SSL_HRR_PENDING) {
if (mt == SSL3_MT_CLIENT_HELLO) {
st->hand_state = TLS_ST_SR_CLNT_HELLO;
return 1;
}
break;
} else if (s->ext.early_data == SSL_EARLY_DATA_ACCEPTED) {
if (mt == SSL3_MT_END_OF_EARLY_DATA) {
st->hand_state = TLS_ST_SR_END_OF_EARLY_DATA;
return 1;
}
break;
}
/* Fall through */
case TLS_ST_SR_END_OF_EARLY_DATA:
case TLS_ST_SW_FINISHED:
if (s->s3->tmp.cert_request) {
if (mt == SSL3_MT_CERTIFICATE) {
st->hand_state = TLS_ST_SR_CERT;
return 1;
}
} else {
if (mt == SSL3_MT_FINISHED) {
st->hand_state = TLS_ST_SR_FINISHED;
return 1;
}
}
break;
读自动机tls_get_message_body函数流程和process_message流程
我们首先看看tls_get_message_body
函数,因为消息类型是finished消息,tls_get_message_body
函数不断尝试读完并解密客户端finished消息,之后会调用ssl3_take_mac(s)
函数。ssl3_take_mac(s)
函数这次要计算的是客户端的finished消息,因此选择client_finished_label
,即 sender = s->method->ssl3_enc->client_finished_label;
,之后调用final_finish_mac
函数计算客户端finished消息。后面的代码是用于将握手消息都保存,NST报文和KEY UPDATE报文不会保存。
int tls_get_message_body(SSL *s, size_t *len)
{
size_t n, readbytes;
unsigned char *p;
int i;
if (s->s3->tmp.message_type == SSL3_MT_CHANGE_CIPHER_SPEC) {
...
}
p = s->init_msg;
n = s->s3->tmp.message_size - s->init_num;
while (n > 0) {
i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE, NULL,
&p[s->init_num], n, 0, &readbytes);
if (i <= 0) {
s->rwstate = SSL_READING;
*len = 0;
return 0;
}
s->init_num += readbytes;
n -= readbytes;
}
/*
* If receiving Finished, record MAC of prior handshake messages for
* Finished verification.
*/
if (*(s->init_buf->data) == SSL3_MT_FINISHED && !ssl3_take_mac(s)) {
/* SSLfatal() already called */
*len = 0;
return 0;
}
/* Feed this message into MAC computation. */
if (RECORD_LAYER_is_sslv2_record(&s->rlayer)) {
...
} else {
/*
* We defer feeding in the HRR until later. We'll do it as part of
* processing the message
* The TLsv1.3 handshake transcript stops at the ClientFinished
* message.
*/
#define SERVER_HELLO_RANDOM_OFFSET (SSL3_HM_HEADER_LENGTH + 2)
/* KeyUpdate and NewSessionTicket do not need to be added */
if (!SSL_IS_TLS13(s) || (s->s3->tmp.message_type != SSL3_MT_NEWSESSION_TICKET
&& s->s3->tmp.message_type != SSL3_MT_KEY_UPDATE)) {
if (s->s3->tmp.message_type != SSL3_MT_SERVER_HELLO
|| s->init_num < SERVER_HELLO_RANDOM_OFFSET + SSL3_RANDOM_SIZE
|| memcmp(hrrrandom,
s->init_buf->data + SERVER_HELLO_RANDOM_OFFSET,
SSL3_RANDOM_SIZE) != 0) {
if (!ssl3_finish_mac(s, (unsigned char *)s->init_buf->data,
s->init_num + SSL3_HM_HEADER_LENGTH)) {
/* SSLfatal() already called */
*len = 0;
return 0;
}
}
}
if (s->msg_callback)
s->msg_callback(0, s->version, SSL3_RT_HANDSHAKE, s->init_buf->data,
(size_t)s->init_num + SSL3_HM_HEADER_LENGTH, s,
s->msg_callback_arg);
}
*len = s->init_num;
return 1;
}
这里的final_finish_mac
对应于tls13_final_finish_mac
,上次分析过一次,我们下面再分析一遍。首先,对从ClientHello,ServerHello到客户端Finished的消息做transcript-hash,代码为ssl_handshake_hash(s, hash, sizeof(hash), &hashlen)
。因为 sender == s->method->ssl3_enc->client_finished_label;
,所以会指定客户端的finished_secret计算客户端finished消息,即key = EVP_PKEY_new_raw_private_key(EVP_PKEY_HMAC, NULL, s->client_finished_secret, hashlen);
。最后使用初始化完毕的hmac ctx计算finished消息。并返回。
size_t tls13_final_finish_mac(SSL *s, const char *str, size_t slen,
unsigned char *out)
{
const EVP_MD *md = ssl_handshake_md(s);
unsigned char hash[EVP_MAX_MD_SIZE];
size_t hashlen, ret = 0;
EVP_PKEY *key = NULL;
EVP_MD_CTX *ctx = EVP_MD_CTX_new();
if (!ssl_handshake_hash(s, hash, sizeof(hash), &hashlen)) {
/* SSLfatal() already called */
goto err;
}
if (str == s->method->ssl3_enc->server_finished_label) {
...
} else if (SSL_IS_FIRST_HANDSHAKE(s)) {
key = EVP_PKEY_new_raw_private_key(EVP_PKEY_HMAC, NULL,
s->client_finished_secret, hashlen);
} else {
...
}
if (key == NULL
|| ctx == NULL
|| EVP_DigestSignInit(ctx, NULL, md, NULL, key) <= 0
|| EVP_DigestSignUpdate(ctx, hash, hashlen) <= 0
|| EVP_DigestSignFinal(ctx, out, &hashlen) <= 0) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS13_FINAL_FINISH_MAC,
ERR_R_INTERNAL_ERROR);
goto err;
}
ret = hashlen;
err:
EVP_PKEY_free(key);
EVP_MD_CTX_free(ctx);
return ret;
}
上面tls_get_message_body
函数计算结束,进入process_message
函数,process_message
函数包裹tls_process_finished
,tls_process_finished
返回以后,读自动机结束返回SUB_STATE_FINISHED
.握手阶段就结束了。我们看看tls_process_finished
函数流程:
- 处于计算pha的缘故,需要保存握手摘要,代码为
SSL_IS_TLS13(s) && !tls13_save_handshake_digest_for_pha(s)
- 分别比较finished消息的长度和finished消息的内容,判断客户端是否计算正确finished消息。代码分别为
if (md_len != PACKET_remaining(pkt))
与if (CRYPTO_memcmp(PACKET_data(pkt), s->s3->tmp.peer_finish_md, md_len) != 0)
- 拷贝finished消息,方便将来使用
- 因为需要读取客户端的应用数据,需要初始化服务端应用数据读环境,代码为
s->method->ssl3_enc->change_cipher_state(s,SSL3_CC_APPLICATION | SSL3_CHANGE_CIPHER_SERVER_READ)
,函数返回MSG_PROCESS_FINISHED_READING
MSG_PROCESS_RETURN tls_process_finished(SSL *s, PACKET *pkt)
{
size_t md_len;
/* This is a real handshake so make sure we clean it up at the end */
if (s->server) {
/*
* To get this far we must have read encrypted data from the client. We
* no longer tolerate unencrypted alerts. This value is ignored if less
* than TLSv1.3
*/
s->statem.enc_read_state = ENC_READ_STATE_VALID;
if (s->post_handshake_auth != SSL_PHA_REQUESTED)
s->statem.cleanuphand = 1;
if (SSL_IS_TLS13(s) && !tls13_save_handshake_digest_for_pha(s)) {
/* SSLfatal() already called */
return MSG_PROCESS_ERROR;
}
}
/*
* In TLSv1.3 a Finished message signals a key change so the end of the
* message must be on a record boundary.
*/
if (SSL_IS_TLS13(s) && RECORD_LAYER_processed_read_pending(&s->rlayer)) {
SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, SSL_F_TLS_PROCESS_FINISHED,
SSL_R_NOT_ON_RECORD_BOUNDARY);
return MSG_PROCESS_ERROR;
}
/* If this occurs, we have missed a message */
if (!SSL_IS_TLS13(s) && !s->s3->change_cipher_spec) {
SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, SSL_F_TLS_PROCESS_FINISHED,
SSL_R_GOT_A_FIN_BEFORE_A_CCS);
return MSG_PROCESS_ERROR;
}
s->s3->change_cipher_spec = 0;
md_len = s->s3->tmp.peer_finish_md_len;
if (md_len != PACKET_remaining(pkt)) {
SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_TLS_PROCESS_FINISHED,
SSL_R_BAD_DIGEST_LENGTH);
return MSG_PROCESS_ERROR;
}
if (CRYPTO_memcmp(PACKET_data(pkt), s->s3->tmp.peer_finish_md,
md_len) != 0) {
SSLfatal(s, SSL_AD_DECRYPT_ERROR, SSL_F_TLS_PROCESS_FINISHED,
SSL_R_DIGEST_CHECK_FAILED);
return MSG_PROCESS_ERROR;
}
/*
* Copy the finished so we can use it for renegotiation checks
*/
if (!ossl_assert(md_len <= EVP_MAX_MD_SIZE)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_PROCESS_FINISHED,
ERR_R_INTERNAL_ERROR);
return MSG_PROCESS_ERROR;
}
if (s->server) {
memcpy(s->s3->previous_client_finished, s->s3->tmp.peer_finish_md,
md_len);
s->s3->previous_client_finished_len = md_len;
} else {
memcpy(s->s3->previous_server_finished, s->s3->tmp.peer_finish_md,
md_len);
s->s3->previous_server_finished_len = md_len;
}
/*
* In TLS1.3 we also have to change cipher state and do any final processing
* of the initial server flight (if we are a client)
*/
if (SSL_IS_TLS13(s)) {
if (s->server) {
if (s->post_handshake_auth != SSL_PHA_REQUESTED &&
!s->method->ssl3_enc->change_cipher_state(s,
SSL3_CC_APPLICATION | SSL3_CHANGE_CIPHER_SERVER_READ)) {
/* SSLfatal() already called */
return MSG_PROCESS_ERROR;
}
} else {
...
}
}
return MSG_PROCESS_FINISHED_READING;
}
我们看看change_cipher_state(s, SSL3_CC_APPLICATION | SSL3_CHANGE_CIPHER_SERVER_READ)
函数内部:
- 进入
if (which & SSL3_CC_READ)
,初始化读ctx - 进入
if (((which & SSL3_CC_CLIENT) && (which & SSL3_CC_WRITE)) || ((which & SSL3_CC_SERVER) && (which & SSL3_CC_READ)))
,分支继续向里走,不会进入if (which & SSL3_CC_EARLY)
,不会进入if(which & SSL3_CC_HANDSHAKE)
,因此选择利用主密钥计算客户端应用数据写秘钥,也就是服务端应用数据读秘钥,选择label为客户端应用数据label,即static const unsigned char client_application_traffic[] = "c ap traffic";
。 - 调用
ssl_handshake_hash(s, hashval, sizeof(hashval), &hashlen)
,对从ClientHello,ServerHello到服务端Finished,客户端Finished消息做hash。 - 因为lable为
client_application_traffic
,所以此时需要计算复用主密钥,对上一步的hash做一次hkdf_expand,代码为tls13_hkdf_expand(s, ssl_handshake_md(s), insecret,resumption_master_secret,sizeof(resumption_master_secret) - 1,hashval, hashlen, s->resumption_master_secret,hashlen, 1)
- 对主密钥
s->master_secret
做derive_secret_key_and_iv
,得到服务端应用数据读key和服务端应用数据读iv。
int tls13_change_cipher_state(SSL *s, int which)
{
static const unsigned char client_early_traffic[] = "c e traffic";
static const unsigned char client_handshake_traffic[] = "c hs traffic";
static const unsigned char client_application_traffic[] = "c ap traffic";
static const unsigned char server_handshake_traffic[] = "s hs traffic";
static const unsigned char server_application_traffic[] = "s ap traffic";
static const unsigned char exporter_master_secret[] = "exp master";
static const unsigned char resumption_master_secret[] = "res master";
static const unsigned char early_exporter_master_secret[] = "e exp master";
unsigned char *iv;
unsigned char secret[EVP_MAX_MD_SIZE];
unsigned char hashval[EVP_MAX_MD_SIZE];
unsigned char *hash = hashval;
unsigned char *insecret;
unsigned char *finsecret = NULL;
const char *log_label = NULL;
EVP_CIPHER_CTX *ciph_ctx;
size_t finsecretlen = 0;
const unsigned char *label;
size_t labellen, hashlen = 0;
int ret = 0;
const EVP_MD *md = NULL;
const EVP_CIPHER *cipher = NULL;
if (which & SSL3_CC_READ) {
if (s->enc_read_ctx != NULL) {
EVP_CIPHER_CTX_reset(s->enc_read_ctx);
} else {
s->enc_read_ctx = EVP_CIPHER_CTX_new();
if (s->enc_read_ctx == NULL) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR,
SSL_F_TLS13_CHANGE_CIPHER_STATE, ERR_R_MALLOC_FAILURE);
goto err;
}
}
ciph_ctx = s->enc_read_ctx;
iv = s->read_iv;
RECORD_LAYER_reset_read_sequence(&s->rlayer);
} else {
...
}
if (((which & SSL3_CC_CLIENT) && (which & SSL3_CC_WRITE))
|| ((which & SSL3_CC_SERVER) && (which & SSL3_CC_READ))) {
if (which & SSL3_CC_EARLY) {
...
} else if (which & SSL3_CC_HANDSHAKE) {
...
} else {
insecret = s->master_secret;
label = client_application_traffic;
labellen = sizeof(client_application_traffic) - 1;
log_label = CLIENT_APPLICATION_LABEL;
/*
* For this we only use the handshake hashes up until the server
* Finished hash. We do not include the client's Finished, which is
* what ssl_handshake_hash() would give us. Instead we use the
* previously saved value.
*/
hash = s->server_finished_hash;
}
} else {
...
}
if (!(which & SSL3_CC_EARLY)) {
md = ssl_handshake_md(s);
cipher = s->s3->tmp.new_sym_enc;
if (!ssl3_digest_cached_records(s, 1)
|| !ssl_handshake_hash(s, hashval, sizeof(hashval), &hashlen)) {
/* SSLfatal() already called */;
goto err;
}
}
...
if (label == client_application_traffic) {
/*
* We also create the resumption master secret, but this time use the
* hash for the whole handshake including the Client Finished
*/
if (!tls13_hkdf_expand(s, ssl_handshake_md(s), insecret,
resumption_master_secret,
sizeof(resumption_master_secret) - 1,
hashval, hashlen, s->resumption_master_secret,
hashlen, 1)) {
/* SSLfatal() already called */
goto err;
}
}
if (!derive_secret_key_and_iv(s, which & SSL3_CC_WRITE, md, cipher,
insecret, hash, label, labellen, secret, iv,
ciph_ctx)) {
/* SSLfatal() already called */
goto err;
}
if (label == server_application_traffic) {
memcpy(s->server_app_traffic_secret, secret, hashlen);
/* Now we create the exporter master secret */
if (!tls13_hkdf_expand(s, ssl_handshake_md(s), insecret,
exporter_master_secret,
sizeof(exporter_master_secret) - 1,
hash, hashlen, s->exporter_master_secret,
hashlen, 1)) {
/* SSLfatal() already called */
goto err;
}
if (!ssl_log_secret(s, EXPORTER_SECRET_LABEL, s->exporter_master_secret,
hashlen)) {
/* SSLfatal() already called */
goto err;
}
} else if (label == client_application_traffic)
memcpy(s->client_app_traffic_secret, secret, hashlen);
if (!ssl_log_secret(s, log_label, secret, hashlen)) {
/* SSLfatal() already called */
goto err;
}
...
ret = 1;
err:
OPENSSL_cleanse(secret, sizeof(secret));
return ret;
}
结语
客户端finished消息发送结束之后,服务端计算应用数据读密钥,握手就结束了。
结尾的闲言碎语
写到这里差不多就可以结束了,就不多说了。TLS这块还有啥不明白的直接告诉我就成了