OPENSSL源码阅读(5)

非复用状态下的发送EncryptedExtension

Posted by 大狗 on September 11, 2020

OPENSSL源码阅读(5)

前言

上一回说了非复用状态/完整握手状态下发送ChangeCipherSpec消息时自动机的变化,这次我们来说说完整握手情况下发送EncrypotedExtension时发生了什么。

从ChangeCipherSpec发送完毕说起

我们上次讲了,发送ChangeCipherSpec之后post_work函数时计算完毕写ctx和读ctx的流程,这次就要发送EncryptedExteions函数了,因为上次最后修改写自动机状态为写初始状态st->write_state = WRITE_STATE_TRANSITION,所以还要从while循环说起。

写自动机transition函数流程

此次进入transition函数,当前的握手状态为st->hand_state == TLS_ST_SW_CHANGE,因此,新的状态是”服务端写EncryptedExtension”阶段,即st->hand_state = TLS_ST_SW_ENCRYPTED_EXTENSIONS;。返回之后,修改写自动机的写状态st->write_state = WRITE_STATE_PRE_WORK;继续向下执行,状态转变的代码为:

	static WRITE_TRAN ossl_statem_server13_write_transition(SSL *s)
{
    OSSL_STATEM *st = &s->statem;

    /*
     * No case for TLS_ST_BEFORE, because at that stage we have not negotiated
     * TLSv1.3 yet, so that is handled by ossl_statem_server_write_transition()
     */

    switch (st->hand_state) {

...

    case TLS_ST_SW_CHANGE:
        if (s->hello_retry_request == SSL_HRR_PENDING)
            st->hand_state = TLS_ST_EARLY_DATA;
        else
            st->hand_state = TLS_ST_SW_ENCRYPTED_EXTENSIONS;
        return WRITE_TRAN_CONTINUE;

写自动机pre_work函数流程和构建EncryptedExtension流程

进入pre_work函数之后实际上啥都没做就返回了,直接返回,继续执行。pre_work函数的代码为:

	WORK_STATE ossl_statem_server_pre_work(SSL *s, WORK_STATE wst)
{
    OSSL_STATEM *st = &s->statem;

    switch (st->hand_state) {
    default:
        /* No pre work to be done */
        break;

...
	}
	return WORK_FINISHED_CONTINUE;

构建EncryptedExtension流程就比较有趣了,ossl_statem_server_construct_message指定调用的函数tls_construct_encrypted_extensions函数之后,调用的函数还是tls_construct_extensions,即前几部发送ServerHello报文时写拓展的函数。只不过context == SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS。 分析tls_construct_extensions函数,实际上还是对全局结构ext_defs的for循环调用,只不过检查下EncryptedExtension函数要不要写,没什么好说的,返回后代码继续执行。

	int tls_construct_extensions(SSL *s, WPACKET *pkt, unsigned int context,
                             X509 *x, size_t chainidx)
{
    size_t i;
    int min_version, max_version = 0, reason;
    const EXTENSION_DEFINITION *thisexd;

    ...
    if (!custom_ext_add(s, context, pkt, x, chainidx, max_version)) {
        /* SSLfatal() already called */
        return 0;
    }

    for (i = 0, thisexd = ext_defs; i < OSSL_NELEM(ext_defs); i++, thisexd++) {
        EXT_RETURN (*construct)(SSL *s, WPACKET *pkt, unsigned int context,
                                X509 *x, size_t chainidx);
        EXT_RETURN ret;

        /* Skip if not relevant for our context */
        if (!should_add_extension(s, thisexd->context, context, max_version))
            continue;

        construct = s->server ? thisexd->construct_stoc
                              : thisexd->construct_ctos;

        if (construct == NULL)
            continue;

        ret = construct(s, pkt, context, x, chainidx);
        if (ret == EXT_RETURN_FAIL) {
            /* SSLfatal() already called */
            return 0;
        }
        ...
    }

    if (!WPACKET_close(pkt)) {
        SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_EXTENSIONS,
                 ERR_R_INTERNAL_ERROR);
        return 0;
    }

    return 1;
}

写自动机发送阶段

没啥好说的,写CTX上一步初始化好了,直接发送出去。

写自动机post_work阶段

post_work函数实际上也啥都没做,直接返回,并将写自动机状态改为写初始状态st->write_state = WRITE_STATE_TRANSITION;。直接蹦到下个阶段,发送Certificate阶段。

结语

这次实际上没咋写,写CTX初始化好了,EncryptedExtension构建好了直接发出去,比较简单,后一步发送Certificate也是一样的流程,pre_work和post_work啥都没干,construct_func函数构建Certificate消息。等到certificateVerify和Finished报文才比较有趣,所以不写Certificate发送阶段了,直接写certificateVerify和finieshed消息了。

结尾的闲言碎语

写到这里差不多就可以结束了,就不多说了。TLS这块还有啥不明白的直接告诉我就成了 狗头的赞赏码.jpg