SSL 协议分析:HandShake客户端角度分析(一)

      继续分析SSL协议,上次写的 ClientHello 过程只是整个 SSL 协议握手过程的一部分而已。

下面是我画的一个简单的客户端握手过程,协议状态机简图:

        根据上篇文章,可以知道客户端握手过程调用的函数是 SSL_connect 。在SSL_connect 函数内部调用中,

实现了客户端的协议状态机。协议状态机的上层代码在:..\ssl\statem\statem.c 文件中的 state_machine 函数

中实现。下面是 state_machine 函数的部分代码,这部分代码就是上面图示的状态机。根据代码和状态机简图,

应该很容易就明白这个客户端握手协议状态机的原理了吧。

      因为 write_state_machine()SSL_connect 的第一个状态,一步一个脚印,依次分析。

我又画了一个丑陋的 “写状态机” 简图: 帮助你大致了解这个子状态机是怎么运转的。

So,现在分析 “写状态机” 是怎么实现的。

      进入“写状态机”前,要进行一些简单的 状态标志 初始化。有: state , write_state , hand_state ,

request_state 。初始化的代码为:

//在 statem.c 的 state_machine 函数中初始化            
st->hand_state = TLS_ST_BEFORE;
st->request_state = TLS_ST_BEFORE;
st->state = MSG_FLOW_WRITING;

//在 statem.c 的 init_write_state_machine 函数中初始化
st->write_state = WRITE_START_TRANSITION;
            

//*****************************************************************************

      OK,正式进入 write_state_machine() ,分析“写状态机”的代码。

      从下图可以看到,根据当前执行的是客户端代码还是服务端代码,对一些关键 函数指针 初始化。

    因为本文是基于客户端分析HandShake,所以我暂时只分析客户端的HandShake函数。

经过上面的 函数指针 初始化,开始新的 “写状态机” 循环,如下图所示。

    根据 write_state ,执行不同基本块。根据前面的初始化,我们知道先执行的是

WRITE_STATE_TRANSITION  基本块。

    先判断是否注册了回调函数,有则调用回调函数;接着调用 transition() 指针函数

根据前面初始化 transition 函数指针的代码,分析 ossl_statem_client_write_transition()

函数。

//*****************************************************************************

    根据官方注释可以知道,这个函数判断握手状态应该迁移到哪个状态。函数根据 hand_state

执行不同基本块。前面 hand_state 被赋值为 TLS_ST_BEFORE 。让我们看看这个基本块做了啥。

 

就是修改了 hand_state ,并返回 WRITE_TRAN_CONTINUE 。 Good , 现在我们重返分析调用

指针函数 transition() 处后面的 switch 代码。根据指针函数的返回值,执行:

case WRITE_TRAN_CONTINUE:
                st->write_state = WRITE_STATE_PRE_WORK;
                st->write_state_work = WORK_MORE_A;
                break;

//*****************************************************************************

OK,返回到 while 循环层的 switch 语句,执行 WRITE_STATE_PRE_WORK 基本块。

 调用 pre_wrok() 指针函数,并根据返回值做别的操作……分析 pre_work() 函数。

    官方注释提示说这是客户端发送服务端消息前的一些必要的预处理。根据 hand_state 执行。

前面 hand_state 被赋值为 TLS_ST_CW_CLNT_HELLO 。

 

  哇,这个基本块也太简单了,就不说是干啥的了。不过要注意了,我分析的是TLS,不说DTLS,

所以赋值完成后就直接返回 WORK_FINISHED_CONTINUE 。返回到 write_state_machine() 继续分析:

修改 write_state 为 WRITE_STATE_SEND ,接着调用 get_construct_message_f() 指针函数,

ossl_statem_client_construct_message() 函数。

//*****************************************************************************

    官方says:获取 消息构造函数 以及 消息的类型。 我已经高亮了根据前面的分析,应该

执行的代码块。OK返回到 write_state_machine() 继续分析:

//*****************************************************************************

调用 confunc() 函数指针,即 tls_construct_client_hello() 函数。

这个函数略长,我就不把整个函数截图放出来了,如下所示是一些关键的基本块,根据注释就知道在这里写入

会话ID,客户端支持的密码套件,压缩方式,以及TLS扩展信息。 正如我上篇文章中写的

如出一辙。

执行完 ssl_close_construct_packet() 后,就返回到循环语句,执行 switch(st->write_state) ,

根据最新的 write_state 标志,我们执行:


调用 statem_do_write() 函数,即 发送预构造的消息给伙伴(服务器)。

因为 hand_state 的值为 TLS_ST_CW_CLNT_SEND ,所以调用 ssl_do_write() 函数。

 

这个函数最后到底调用哪个函数是根据客户端初始的 TLS版本决定的 ,暂时就不分析了。

回到上面,分析 statem_do_write() 函数返回后执行的代码,修改了 write_state 和 write_state_work。

进入 WRITE_STATE_POST_WORK 基本块,调用 post_work() 指针函数。

//*****************************************************************************

post_work() ossl_statem_client_post_work() 函数:

执行一些发送消息的善后函数。这里我就不展开说明了。函数返回 WORK_FINISHED_CONTINUE。

write_state 又回到了 WRITE_STATE_TRANSITION 状态?这是个死循环?不,因为 hand_state

不同了,所以 transition() 指针函数 将返回 WRITE_TRAN_FINISHED ,结束 write_state_machine()

函数的死循环,回到 state_machine() 函数!

      因为返回值是 SUB_STATE_FINISHED ,所以将调用 init_read_state_machine() 函数,

并修改 state 为 MSG_FLOW_READING 进入“读取状态机”。

      好了,以上就是 握手状态机 的 “写状态机”的分析了。写了好几个小时……不过之前也分析了好久,

诶,还好,虽然慢,但至少我还是往前走的吧!

猜你喜欢

转载自blog.csdn.net/cwg2552298/article/details/83020289