继续分析SSL协议,上篇介绍的是 客户端握手协议状态机的子状态机“写状态机”,
这篇文章将介绍“读状态机”。 因为“读状态机”就是“写状态机”的下一个跳转状态。
再来看看我画的协议状态机简图:
从上篇文章结尾处开始继续分析, write_state_machine() 函数(在statem_lib.c文件中)返回
SUB_STATE_FINISHED ,修改state 为 MSG_FLOW_READING 并调用 init_read_state_machine()
函数初始化读取状态机的一些标志:
初始化完成后,进入 “读状态机”,简图如下:
让我们看看 read_state_machine() 代码大致是啥样子的:
很容易就发现其实和 write_state_machine() 函数是同一个模样,先根据运行的是
客户端代码还是服务端代码初始化函数指针,然后进入“读状态机”。在初始化中我们
得知 read_state 被赋值为 READ_STATE_HEADER ,现在开始分析这个代码块:
判断是否为DTLS,我用的是TLS ,所以分析 tls_get_message_header() 函数:
//*****************************************************************************
读取一字节的消息类型: SSL3_RT_HANDSHAKE 或者 SSL3_RT_CHANGE_CIPHER_SPEC;
其他的类型都是错误的 握手包。 可以在 WireShark 抓包中看到,只要是握手包协议,开头第一字节
都是 0x16 ,即 SSL3_RT_HANDSHAKE 的值。
其实它是在一个循环内找标志数据包类型的标记,识别出数据包类型后循环就退出了。
赋值消息类型:
循环退出后,根据接收的数据包协议版本,做不同的指针初始化:
ok,返回到 read_state_machine() 函数继续分析。如果有回调函数则调用之。然后调用
transition() 指针函数,即 ossl_statem_client_read_transition() 函数。
//*****************************************************************************
这个函数根据从服务器读取的信息,修改握手状态,接收的数据包类型在 mt 参数中,
当前 握手状态 在 s->statem.hand_state 中。
根据 当前握手状态,我们分析 TLS_ST_CW_CLNT_HELLO 基本块。
判断消息类型是否为 SSL3_MT_SERVER_HELLO ,是则修改 握手状态为
TLS_ST_CR_SRVR_HELLO 。正常情况下,客户端发送 ClientHello ,服务器都
会先响应一个 SSL3_MT_SERVER_HELLO 类型的握手数据包。所以在这里我就返回
分析了。
接着就是修改 read_state ,进入 READ_STATE_BODY 基本块分析。
//*****************************************************************************
进入 tls_get_message_body() 函数,开始分析:
如果消息类型是 修改密码套件 ,则直接返回。
否则不断的读取数据到缓存中。
如果数据包结束了,就计算消息的 MAC 值,并返回。好了,返回分析到“读状态机”。
初始化缓存,并调用 process_message() 指针函数,即 ossl_statem_client_process_message()函数。
//*****************************************************************************
进入 ossl_statem_client_process_message() 函数分析:
根据 hand_state 调用不同的处理过程,即不同的握手状态(Hello,证书验证,密钥交换,Finish) ,
调用不同的函数。当前的握手状态是 ServerHello,ok,进入 tls_process_server_hello() 函数分析。
//*****************************************************************************
读取两个字节的版本信息,读取服务端随机数,读取会话ID,读取加密方式,压缩方式,
以及扩展信息。可以从 WireShark 解析的协议中,清楚的看见以上信息:
有了服务器发送的加密方式,就设置客户端的加密方式:
然后解析扩展信息:
最后根据TLS版本调用一些函数,然后返回:
返回值有多个,根据不同返回值进行不同处理:
在 READ_STATE_POST_PROCESS 块中,发送客户端的证书:
有了以上的分析,就可以很快理解读取状态机了,最关键的就是判断读取的数据包
是什么类型的握手包(ServerHello,Certificate,KeyExchange,Alert),然后根据
握手包类型,调用不同的处理函数。需要分析的就是 ossl_statem_client_process_message
函数。