OPENSSL源码阅读(8)

读取客户端Finished报文

Posted by 大狗 on September 13, 2020

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_finishedtls_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_secretderive_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这块还有啥不明白的直接告诉我就成了 狗头的赞赏码.jpg