Openssl庫握手過程中的狀態(tài)機(jī)分析

一、前言

有窮自動(dòng)機(jī) (或叫狀態(tài)機(jī)) 一文中,簡單介紹了自動(dòng)機(jī)的原理,這是人們在面對復(fù)雜問題時(shí)為了建立數(shù)學(xué)模型而對問題進(jìn)行抽象的描述。那么回歸到現(xiàn)實(shí)問題時(shí),這種抽象的描述又應(yīng)該怎么落地生根呢?本文借助分析 Openssl 庫的握手過程來探討狀態(tài)機(jī)是如何在程序中發(fā)生作用的,因此本文的重點(diǎn)分析狀態(tài)機(jī)的工作過程,對于Openssl庫中握手過程的細(xì)節(jié)不做過多深入。

二、SSL連接流程圖

單向認(rèn)證方式

注:上述圖片中橙色是普通 Socket 操作,其余為 SSL 操作,此處為單向認(rèn)證的流程,雙向認(rèn)證是在此基礎(chǔ)上 client 添加證書和密鑰校驗(yàn)等過程,詳見 參考文獻(xiàn)[2]

三、涉及的源碼

示例代碼可見 :openssl/zsk ,包含雙向認(rèn)證的兩種方式。

四、Openssl 庫中狀態(tài)機(jī)簡述

Openssl 庫中主要有兩個(gè)狀態(tài)機(jī):消息流狀態(tài)機(jī)、握手狀態(tài)機(jī)組成。消息流狀態(tài)機(jī) 控制消息的讀取和發(fā)送,包括處理 非阻塞的IO事件、刷寫B(tài)IO、處理意外消息等。同時(shí)他本身被獨(dú)立分解成兩個(gè)獨(dú)立的子狀態(tài)機(jī),用來分別控制讀取和發(fā)送消息。握手狀態(tài)機(jī) 會(huì)跟蹤當(dāng)前的 SSL/TLS 握手狀態(tài)。握手狀態(tài)的轉(zhuǎn)換是隨消息流狀態(tài)機(jī)的事件處理而變化。這些狀態(tài)機(jī)保存著握手需要的一些消息處理函數(shù)和算法函數(shù)用來解析消息和執(zhí)行加解密操作。因此正常情況下,狀態(tài)機(jī)遵循 “接收消息-處理消息-切換狀態(tài)-發(fā)送消息” 這個(gè)流程,一旦消息流狀態(tài)機(jī)不按正常的流程走,就會(huì)導(dǎo)致狀態(tài)機(jī)的異常。

這里先來看一下狀態(tài)機(jī)情況總覽:

 * ---------------------------------------------            -------------------
 * |                                           |            |                 |
 * | Message flow state machine                |            |                 |
 * |                                           |            |                 |
 * | -------------------- -------------------- | Transition | Handshake state |
 * | | MSG_FLOW_READING | | MSG_FLOW_WRITING | | Event      | machine         |
 * | | sub-state        | | sub-state        | |----------->|                 |
 * | | machine for      | | machine for      | |            |                 |
 * | | reading messages | | writing messages | |            |                 |
 * | -------------------- -------------------- |            |                 |
 * |                                           |            |                 |
 * ---------------------------------------------            -------------------

4.1 消息流狀態(tài)機(jī)

消息流狀態(tài)機(jī),共有5個(gè)狀態(tài)用于表明消息的狀態(tài)。摘取 Openssl 庫對此狀態(tài)機(jī)的描述如下:

 * The main message flow state machine. We start in the MSG_FLOW_UNINITED or
 * MSG_FLOW_FINISHED state and finish in MSG_FLOW_FINISHED. Valid states and
 * transitions are as follows:
 *
 * MSG_FLOW_UNINITED     MSG_FLOW_FINISHED
 *        |                       |
 *        +-----------------------+
 *        v
 * MSG_FLOW_WRITING <---> MSG_FLOW_READING
 *        |
 *        V
 * MSG_FLOW_FINISHED
 *        |
 *        V
 *    [SUCCESS]
 *
 * We may exit at any point due to an error or NBIO event. If an NBIO event
 * occurs then we restart at the point we left off when we are recalled.
 * MSG_FLOW_WRITING and MSG_FLOW_READING have sub-state machines associated with them.
 *
 * In addition to the above there is also the MSG_FLOW_ERROR state. We can move
 * into that state at any point in the event that an irrecoverable error occurs.
 *
 * Valid return values are:
 *   1: Success
 * <=0: NBIO or error

可以明顯看到消息流狀態(tài)機(jī)的幾個(gè)狀態(tài)的轉(zhuǎn)移過程,對于幾個(gè)狀態(tài)在此做詳細(xì)釋義:

  1. MSG_FLOW_UNINITED:握手尚未開始
  2. MSG_FLOW_ERROR:當(dāng)前連接發(fā)生錯(cuò)誤
  3. MSG_FLOW_READING:當(dāng)前正讀取消息
  4. MSG_FLOW_WRITING:當(dāng)前正寫入消息
  5. MSG_FLOW_FINISHED:握手結(jié)束

4.2 讀狀態(tài)機(jī)

讀狀態(tài)機(jī)是屬于消息流狀態(tài)機(jī)的子狀態(tài)機(jī),這里我們單獨(dú)拿出來分析

 * This function implements the sub-state machine when the message flow is in
 * MSG_FLOW_READING. The valid sub-states and transitions are:
 *
 * READ_STATE_HEADER <--+<-------------+
 *        |             |              |
 *        v             |              |
 * READ_STATE_BODY -----+-->READ_STATE_POST_PROCESS
 *        |                            |
 *        +----------------------------+
 *        v
 * [SUB_STATE_FINISHED]
 *
 * READ_STATE_HEADER has the responsibility for reading in the message header
 * and transitioning the state of the handshake state machine.
 *
 * READ_STATE_BODY reads in the rest of the message and then subsequently
 * processes it.
 *
 * READ_STATE_POST_PROCESS is an optional step that may occur if some post
 * processing activity performed on the message may block.
 *
 * Any of the above states could result in an NBIO event occurring in which case
 * control returns to the calling application. When this function is recalled we
 * will resume in the same state where we left off.

讀狀態(tài)機(jī)的狀態(tài)釋義如下:

  1. READ_STATE_HEADER:負(fù)責(zé)讀取消息頭并轉(zhuǎn)換握手狀態(tài)機(jī)的狀態(tài)
  2. READ_STATE_BODY:讀取消息的其余部分,然后隨后對其進(jìn)行處理
  3. READ_STATE_POST_PROCESS:是一個(gè)可選的步驟,如果對消息執(zhí)行的某些后處理活動(dòng)可能會(huì)被阻塞,則可能會(huì)發(fā)生該步驟

由上圖中狀態(tài)機(jī)轉(zhuǎn)移圖可知 讀狀態(tài)機(jī) 完整操作之后會(huì)轉(zhuǎn)移到 SUB_STATE_FINISHED 這個(gè)狀態(tài)。這個(gè)狀態(tài)是定義在 statem.c 中的枚舉類,用來表明子狀態(tài)機(jī)的返回值:

typedef enum {
    SUB_STATE_ERROR,    //發(fā)生錯(cuò)誤
    SUB_STATE_FINISHED,  //子狀態(tài)完成后,將轉(zhuǎn)到下一個(gè)子狀態(tài)
    SUB_STATE_END_HANDSHAKE //子狀態(tài)已完成,握手也已完成
} SUB_STATE_RETURN;

4.3 寫狀態(tài)機(jī)

寫狀態(tài)機(jī)同樣也屬于消息流狀態(tài)機(jī)的子狀態(tài)機(jī),摘錄 Openssl 中的說明如下:

 * This function implements the sub-state machine when the message flow is in
 * MSG_FLOW_WRITING. The valid sub-states and transitions are:
 *
 * +-> WRITE_STATE_TRANSITION ------> [SUB_STATE_FINISHED]
 * |             |
 * |             v
 * |      WRITE_STATE_PRE_WORK -----> [SUB_STATE_END_HANDSHAKE]
 * |             |
 * |             v
 * |       WRITE_STATE_SEND
 * |             |
 * |             v
 * |     WRITE_STATE_POST_WORK
 * |             |
 * +-------------+
 *
 * WRITE_STATE_TRANSITION transitions the state of the handshake state machine

 * WRITE_STATE_PRE_WORK performs any work necessary to prepare the later
 * sending of the message. This could result in an NBIO event occurring in
 * which case control returns to the calling application. When this function
 * is recalled we will resume in the same state where we left off.
 *
 * WRITE_STATE_SEND sends the message and performs any work to be done after
 * sending.
 *
 * WRITE_STATE_POST_WORK performs any work necessary after the sending of the
 * message has been completed. As for WRITE_STATE_PRE_WORK this could also
 * result in an NBIO event.

寫狀態(tài)機(jī)的狀態(tài)釋義如下:

  1. WRITE_STATE_TRANSITION:轉(zhuǎn)換握手狀態(tài)機(jī)的狀態(tài)
  2. WRITE_STATE_PRE_WORK:發(fā)送消息之前執(zhí)行其他的準(zhǔn)備工作
  3. WRITE_STATE_SEND:發(fā)送消息,并執(zhí)行發(fā)送后要完成的任何工作
  4. WRITE_STATE_POST_WORK:在消息發(fā)送完成后執(zhí)行其他必要的工作

4.4 握手狀態(tài)機(jī)

握手狀態(tài)機(jī)的狀態(tài)太多,此處不一一說明,因?yàn)槠渲卸x了不同協(xié)議的狀態(tài)比較繁瑣,具體可參見 第三章 ssl.h 中定義

五、狀態(tài)機(jī)工作過程

下面將從總體的角度來分析狀態(tài)機(jī)的狀態(tài)轉(zhuǎn)移,首先說明三個(gè)變量的含義:

  • s->statem.state : 消息流狀態(tài)
  • s->statem.hand_state : 握手狀態(tài)
  • st->write_state:寫狀態(tài)
  • st->read_state:讀狀態(tài)

1: 初始化 SSL_CTX_new , 代入?yún)?shù) TLS_client_method 指定使用的協(xié)議,其具體的函數(shù)的定義在 methods.c :IMPLEMENT_tls_meth_func(...)
        其中指定了連接函數(shù)是 ossl_statem_connect
        
2:初始化消息流狀態(tài)機(jī)的狀態(tài) SSL_connect -> SSL_set_connect_state -> ossl_statem_clear

        ???s->statem.state = MSG_FLOW_UNINITED;???
            ???s->statem.hand_state = TLS_ST_BEFORE;???       //握手狀態(tài)機(jī)
     
3:開始連接 SSL_do_handshake -> ossl_statem_connect -> state_machine ->
            
        ???s->statem.state = MSG_FLOW_UNINITED;???
            ???s->statem.hand_state = TLS_ST_BEFORE;??? //握手狀態(tài)機(jī)
            ???st->request_state = TLS_ST_BEFORE;???
        
        
4:SSL_do_handshake 函數(shù)中在判斷一些列條件和初始化一些參數(shù)之后開始切換 消息流狀態(tài)機(jī)的狀態(tài) 
    
        ???st->state = MSG_FLOW_WRITING;???
                ???init_write_state_machine(s);???
                ???st->write_state = WRITE_STATE_TRANSITION;???
               
5:SSL_do_handshake 函數(shù)中 消息流狀態(tài)機(jī)切換寫狀態(tài)之后,啟動(dòng)寫狀態(tài)機(jī) 
    
        ???ssret = write_state_machine(s);???
        
    ?? ssret 如果狀態(tài)為 SUB_STATE_FINISHED 則消息流狀態(tài)機(jī) 切換讀狀態(tài),
    
        ???st->state = MSG_FLOW_READING;???
                ??? init_read_state_machine(s);???
                 ???st->read_state = READ_STATE_HEADER;???
                
6:SSL_do_handshake 函數(shù)中 消息流狀態(tài)機(jī)切換讀狀態(tài)之后,啟動(dòng)讀狀態(tài)機(jī)

        ??? ssret = read_state_machine(s);???
        
    ?? ssret 如果狀態(tài)為 SUB_STATE_FINISHED 則消息流狀態(tài)機(jī) 切換寫狀態(tài),
    
        ???st->state = MSG_FLOW_WRITING;???
                ???init_write_state_machine(s);???
                ???st->write_state = WRITE_STATE_TRANSITION;???
                
  至此 第 5,6 步驟 處于不斷的循環(huán)中 消息流的狀態(tài)切換 啟動(dòng)對應(yīng)的讀寫狀態(tài)機(jī)去收發(fā)消息, 這是 消息流狀態(tài)機(jī)的 工作原理 ,那么握手狀態(tài)機(jī)是怎么工作的呢?
  
  
 
 
 
 握手狀態(tài)機(jī):
 
 
 7、消息流狀態(tài)機(jī)和握手狀態(tài)機(jī)產(chǎn)生關(guān)系是在 第5,6步驟:
        
    深入 write_state_machine(s) 來看
    
    
    write_state_machine -> 分別設(shè)定客戶端和服務(wù)端的一些處理函數(shù),這里以 clent 為例:
 
        transition = ossl_statem_client_write_transition;
        pre_work = ossl_statem_client_pre_work;
        post_work = ossl_statem_client_post_work;
        get_construct_message_f = ossl_statem_client_construct_message;
        
        -> transition(st->write_state = WRITE_STATE_TRANSITION) -> ossl_statem_client_write_transition (statem_cLnt.c)
        
            -> ???st->hand_state=TLS_ST_BEFORE -> 切換握手狀態(tài)機(jī)的狀態(tài) : 
            
                ???st->hand_state = TLS_ST_CW_CLNT_HELLO;???
                
                
           transition 返回值為 WRITE_TRAN_CONTINUE: 此時(shí)返回到 write_state_machine 函數(shù)
           
            ???st->write_state = WRITE_STATE_PRE_WORK;???   
                    ???st->write_state_work = WORK_MORE_A;???
                    
                  write_state_machine : st->write_state = WRITE_STATE_PRE_WORK -> pre_work -> ossl_statem_client_pre_work (statem_cLnt.c) 
                  
                    -> ???st->hand_state=TLS_ST_CW_CLNT_HELLO 
                    
                    ?????????[重要] -> get_construct_message_f -> ossl_statem_client_construct_message (statem_cLnt.c) 此處構(gòu)建客戶端的消息 
                    
                  pre_work 返回  WORK_FINISHED_CONTINUE: 此時(shí)返回到 write_state_machine 函數(shù)
                  
                    ???st->write_state = WRITE_STATE_SEND;???
                    
                -> ???st->write_state=WRITE_STATE_SEND -> 切換寫狀態(tài)機(jī)的狀態(tài):
                
                    ???statem_do_write???   這個(gè)函數(shù)沒弄清楚
                
                    ???st->write_state = WRITE_STATE_POST_WORK;??? 
                    
              write_state_machine : st->write_state=WRITE_STATE_POST_WORK ->  post_work -> ossl_statem_client_post_work (statem_cLnt.c) 
        
            -> ???st->hand_state=TLS_ST_CW_CLNT_HELLO :
            
          post_work 返回 WORK_FINISHED_CONTINUE :  此時(shí)返回到 write_state_machine 函數(shù)
          
            ???st->write_state = WRITE_STATE_TRANSITION;???
            
            
        -> transition(st->write_state = WRITE_STATE_TRANSITION) -> ossl_statem_client_write_transition (statem_cLnt.c)
        
               -> ???st->hand_state=TLS_ST_CW_CLNT_HELLO -> 切換握手狀態(tài)機(jī)的狀態(tài) : 
            
         transition 返回 WRITE_TRAN_FINISHED : 此時(shí)返回到 write_state_machine 函數(shù)
            
            write_state_machine 返回 SUB_STATE_FINISHED 表示寫入狀態(tài)機(jī)完成 ,此時(shí)返回到 上述第5步驟 ,以此進(jìn)入消息循環(huán) 讀狀態(tài)機(jī)相同
 

筆者本意是想使用狀態(tài)機(jī)的轉(zhuǎn)移圖或者其他的方式來清晰說明握手協(xié)議過程中狀態(tài)機(jī)的工作原理,但分析下來后發(fā)覺這部分錯(cuò)綜復(fù)雜,難以用簡單的圖示說明,所以只能采用文本簡述的方式。后續(xù)或可隨著對這部分的理解加深之后,重構(gòu)本篇。

參考

[ 1 ] OpenSSL之SSL用法
[ 2 ] 基于openssl的單向和雙向認(rèn)證的深入分析
[ 3 ] Openssl狀態(tài)機(jī)的實(shí)現(xiàn)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容