Rustls之源碼分析總結(jié)(一)

Rustls源碼分析總結(jié)

  • 作者:anhkgg
  • 日期:2017-11-16

rustls已經(jīng)支持tls1.3,但是測試分析中使用的tls1.2,所以后面分析主要集中在tls1.2。

主要分析的源碼內(nèi)容:

  1. client和server的握手協(xié)議流程
  2. rustls是如何進(jìn)行數(shù)據(jù)傳輸?shù)?/li>
  3. 數(shù)據(jù)傳輸是如何加密解密的

源碼結(jié)構(gòu)

分為client和server兩部分

公共接口

session.rs定義了SessionCommon,包括了數(shù)據(jù)傳輸、數(shù)據(jù)加密、包處理相關(guān)接口。

主要字段

pub struct SessionCommon {
    pub negotiated_version: Option<ProtocolVersion>, //協(xié)商好的協(xié)議版本
    pub is_client: bool, //是客戶端true,是服務(wù)端false
    message_encrypter: Box<MessageEncrypter>, //數(shù)據(jù)加密接口
    message_decrypter: Box<MessageDecrypter>, //數(shù)據(jù)解密接口
    key_schedule: Option<KeySchedule>,
    suite: Option<&'static SupportedCipherSuite>,
    write_seq: u64,
    read_seq: u64,
    peer_eof: bool,
    pub peer_encrypting: bool,
    pub we_encrypting: bool,
    pub traffic: bool, // 默認(rèn)false,握手完成字段為true
    pub want_write_key_update: bool,
    pub message_deframer: MessageDeframer, //消息幀處理對象,保存所有Message包
    pub handshake_joiner: HandshakeJoiner,
    pub message_fragmenter: MessageFragmenter,
    received_plaintext: ChunkVecBuffer, //緩存接收到的數(shù)據(jù)明文
    sendable_plaintext: ChunkVecBuffer,//緩存握手后需要傳輸?shù)臄?shù)據(jù)明文
    pub sendable_tls: ChunkVecBuffer, //緩存握手?jǐn)?shù)據(jù)包
}

主要接口

函數(shù)名 說明
read_tls 接收底層連接數(shù)據(jù)
write_tls 通過底層連接發(fā)送數(shù)據(jù)
process_new_packets 每次調(diào)用read_tls之后都需要調(diào)用該函數(shù)主動觸發(fā)消息處理
wants_read/wants_write 是否有數(shù)據(jù)需要接收發(fā)送
encrypt_outgoing 加密要發(fā)送的數(shù)據(jù),在握手完成之后需要
decrypt_incoming 解密要接收的數(shù)據(jù),在握手完成之后需要
send_msg_encrypt 發(fā)送加密數(shù)據(jù)
send_appdata_encrypt 發(fā)送握手之后的數(shù)據(jù),加密
send_some_plaintext 發(fā)送明文數(shù)據(jù),握手之后會被加密發(fā)送
start_traffic 握手完成之后調(diào)用,設(shè)置傳輸標(biāo)志,發(fā)送緩存的數(shù)據(jù)明文
send_msg 發(fā)送TLS消息,根據(jù)是否加密走不通發(fā)送方式
take_received_plaintext 握手完成之后,收到數(shù)據(jù)會被調(diào)用,參數(shù)已經(jīng)是明文Message
set_message_encrypter 設(shè)置消息加密接口,start_encryption_tls12中調(diào)用
set_message_decrypter 設(shè)置消息解密接口,start_encryption_tls12中調(diào)用
start_encryption_tls12 TLS1.2設(shè)置加解密接口,在ExpectTLS12ServerDone::handle/ExpectTLS12ClientKX::handle調(diào)用

ciper.rs定義了加密解密的接口。

MessageEncrypter,MessageDecrypter,具體使用加解密方法在握手過程中ExpectTLS12ServerDone::handle/ExpectTLS12ClientKX::handle設(shè)置。

//client端
// 5e. Now commit secrets.
let hashalg = sess.common.get_suite().get_hash();
if st.handshake.using_ems {
    sess.secrets = Some(SessionSecrets::new_ems(&st.handshake.randoms,
                                                &handshake_hash,
                                                hashalg,
                                                &kxd.premaster_secret));
} else {
    sess.secrets = Some(SessionSecrets::new(&st.handshake.randoms,
                                            hashalg,
                                            &kxd.premaster_secret));
}
sess.start_encryption_tls12();
//----------
pub fn start_encryption_tls12(&mut self, secrets: &SessionSecrets) {
        let (dec, enc) = cipher::new_tls12(self.get_suite(), secrets);
        self.message_encrypter = enc;
        self.message_decrypter = dec;
    }

client詳解

src/client/mod.rs 導(dǎo)出ClientSession接口,外部使用
src/client/hs.rs tls協(xié)議中所有包處理,包括握手和傳輸
src/client/

ClientSession內(nèi)部由ClientSessionImpl實(shí)現(xiàn)。

pub struct ClientSessionImpl {
    pub config: Arc<ClientConfig>, //保存client端的證書,密鑰配置等信息
    pub secrets: Option<SessionSecrets>, //保存握手后的會話密鑰
    pub alpn_protocol: Option<String>,
    pub common: SessionCommon, // 完成具體消息傳輸、加解密等
    pub error: Option<TLSError>,
    pub state: Option<Box<hs::State + Send>>, // 保存握手過程中的交互狀態(tài),握手中處理對象都實(shí)現(xiàn)State接口
    pub server_cert_chain: CertificatePayload, // 服務(wù)端證書鏈
}

握手,準(zhǔn)備第一個數(shù)據(jù)包。

ClientSessionImpl::new內(nèi)部就會準(zhǔn)備握手要發(fā)送的第一個數(shù)據(jù)包。

cs.state = Some(hs::start_handshake(&mut cs, hostname));
//cs.state保存下一次將處理數(shù)據(jù)對象
---> //進(jìn)入hs.rs
InitialState::emit_initial_client_hello
--->
emit_client_hello_for_retry
---> //構(gòu)造發(fā)送的數(shù)據(jù)包
let mut chp = HandshakeMessagePayload {
        typ: HandshakeType::ClientHello,
        payload: HandshakePayload::ClientHello(ClientHelloPayload {
            client_version: ProtocolVersion::TLSv1_2,
            random: Random::from_slice(&handshake.randoms.client),
            session_id: session_id,
            cipher_suites: sess.get_cipher_suites(),
            compression_methods: vec![Compression::Null],
            extensions: exts,
        }),
    };

然后,收到返回?cái)?shù)據(jù)之后,會在ClientSessionImpl::process_main_protocol調(diào)用state.handle來處理收到的數(shù)據(jù),然后返回新的state,用于下次處理,如此循環(huán),知道握手完成。

fn process_main_protocol(&mut self, msg: Message) -> Result<(), TLSError> {
    //檢查消息是否合法
    let state = self.state.take().unwrap();
    state
        .check_message(&msg)
        .map_err(|err| {
            self.queue_unexpected_alert();
            err
        })?;
    //處理本次數(shù)據(jù),返回下次需要處理的數(shù)據(jù)對象
    self.state = Some(state.handle(self, msg)?);
    Ok(())
}

消息處理調(diào)用流程如下:

//ClientSessionImpl
process_new_packets->process_msg->process_main_protocol->state.handle

下面直接列出client端握手處理流程:

ExpectServerHelloOrHelloRetryRequest:handle 
ExpectServerHello:handle // 處理serverhello
ExpectTLS12Certificate: handle //驗(yàn)證證書
ExpectTLS12ServerKX: handle  // 密鑰交換
ExpectTLS12ServerDoneOrCertReq: handle
ExpectTLS12ServerDone: handle
emit_clientkx
emit_ccs
ExpectTLS12CCS:handle //通知使用加密方式發(fā)送報(bào)文,sess.common.peer_now_encrypting();設(shè)置后面數(shù)據(jù)會加密的狀態(tài)
emit_finished
ExpectTLS12Finished:handle // 握手結(jié)束

ExpectTLS12Finished::handle中,會保存session,開始傳輸數(shù)據(jù),以及返回下次的state,此時握手協(xié)議已經(jīng)完成

save_session(&mut st.handshake,
             &mut st.ticket,
             sess);

if st.resuming {
    emit_ccs(sess);
    emit_finished(&mut st.handshake, sess);
}

sess.common.we_now_encrypting();
sess.common.start_traffic(); //發(fā)送數(shù)據(jù)
Ok(st.into_expect_tls12_traffic(fin)) // 下次需要ExpectTLS12Traffic

后面數(shù)據(jù)傳輸?shù)乃辛鞒潭紩M(jìn)入ExpectTLS12Traffic::handle,也就是開始傳輸協(xié)議。

impl State for ExpectTLS12Traffic {
    fn handle(self: Box<Self>, sess: &mut ClientSessionImpl, mut m: Message) -> StateResult {
   sess.common.take_received_plaintext(m.take_opaque_payload().unwrap());
        Ok(self) //返回的依然是ExpectTLS12Traffic給state,所以以后都會進(jìn)入這里
    }
}

傳輸數(shù)據(jù)的處理

接收數(shù)據(jù)

調(diào)用take_received_plaintext將獲取到的明文Message傳給內(nèi)部處理,存入SessionCommonreceived_plaintext,等待用戶的提取。

那明文Message是怎么來的呢?是在前面說到的消息處理流程中,到handle之前。

process_new_packets->process_msg->process_main_protocol->state.handle

process_msg中會判斷peer_encrypting狀態(tài)為真則將數(shù)據(jù)解密,而該狀態(tài)是在握手中ExpectTLS12CCS::handle 被設(shè)置為true的。

pub fn process_msg(&mut self, mut msg: Message) -> Result<(), TLSError> {
    // Decrypt if demanded by current state.
    if self.common.peer_encrypting {
        let dm = self.common.decrypt_incoming(msg)?; //解密數(shù)據(jù)
        msg = dm;
    }
        
//self.common.peer_encrypting
pub fn peer_now_encrypting(&mut self) {
    self.peer_encrypting = true;
}

發(fā)送數(shù)據(jù)

握手過程中,發(fā)送數(shù)據(jù)包使用sess.common.send_msg(ch, false)。send_msg內(nèi)部根據(jù)是否加密狀態(tài)(must_encrypt)進(jìn)行不同處理,直接緩存或者調(diào)用send_msg_encrypt加密之后緩存。

send_msg_encrypt->send_single_fragment->encrypt_outgoing(加密)

最后都是通過queue_tls_message將數(shù)據(jù)先緩存,然后在調(diào)用write_tls之后將數(shù)據(jù)發(fā)送。

pub fn write_tls(&mut self, wr: &mut Write) -> io::Result<usize> {
    self.sendable_tls.write_to(wr)
}

握手完成后,通過ClientSession實(shí)現(xiàn)的io::write(或者write_all)接口發(fā)送明文數(shù)據(jù)。

impl io::Write for ClientSession {
    //先緩存數(shù)據(jù)
    fn write(&mut self, buf: &[u8]) -> io::Result<usize>{
            self.imp.common.send_some_plaintext(buf)
    }
    //flush時才發(fā)送數(shù)據(jù)
    fn flush(&mut self) -> io::Result<()> {
        self.imp.common.flush_plaintext();
        Ok(())
    }
}

send_some_plaintext在根據(jù)是否握手完成有不同的操作,握手未完成時,先緩存明文到sendable_plaintext,握手完成后,直接調(diào)用send_appdata_encrypt緩存密文(進(jìn)入send_single_fragment過程加密)。

pub fn send_some_plaintext(&mut self, data: &[u8]) -> io::Result<usize> {
    self.send_plain(data, Limit::Yes)
}

fn send_plain(&mut self, data: &[u8], limit: Limit) -> io::Result<usize> {
    if !self.traffic { //握手未完成
        let len = match limit { //緩存明文
            Limit::Yes => self.sendable_plaintext.append_limited_copy(data),
            Limit::No => self.sendable_plaintext.append(data.to_vec())
        };
        return Ok(len);
    }
    //握手完成,直接緩存加密數(shù)據(jù)
    Ok(self.send_appdata_encrypt(data, limit))
}

握手完成時,之前緩存的明文數(shù)據(jù)通過start_traffic實(shí)際將數(shù)據(jù)加密緩存到sendable_tls,最后也是通過write_tls發(fā)送出去。

pub fn start_traffic(&mut self) {
        self.traffic = true;
        self.flush_plaintext();
    }
->
flush_plaintext->send_plain->send_appdata_encrypt->send_single_fragment-> encrypt_outgoing(加密)

握手完成之后調(diào)用的send_some_plaintext是直接將數(shù)據(jù)加密緩存,在write_tls后發(fā)送出去。

server詳解

src/server/mod.rs 導(dǎo)出ServerSession接口,外部使用
src/server/hs.rs tls協(xié)議中所有包處理,包括握手和傳輸
src/client/

公開外部使用的借口ServerSession,內(nèi)部由ServerSessionImpl實(shí)現(xiàn)。

pub struct ServerSessionImpl {
    pub config: Arc<ServerConfig>, //證書、密鑰等配置
    pub secrets: Option<SessionSecrets>, //會話密鑰
    pub common: SessionCommon, // 實(shí)際握手傳輸數(shù)據(jù)處理對象
    sni: Option<webpki::DNSName>, //SNI(Server Name Indication) ,解決一個服務(wù)器使用多個域名和證書的SSL/TLS擴(kuò)展
    pub alpn_protocol: Option<String>,
    pub error: Option<TLSError>,
    pub state: Option<Box<hs::State + Send>>, //握手和傳輸中處理數(shù)據(jù)包的狀態(tài),每個狀態(tài)的數(shù)據(jù)包處理對象
    pub client_cert_chain: Option<Vec<key::Certificate>>, //client證書鏈
}

接口基本和ClientSession類似,不再詳述

握手流程

server和client處理握手的方式都一樣,每個握手包處理對象都會實(shí)現(xiàn)State接口。

pub trait State {
    fn check_message(&self, m: &Message) -> CheckResult;
    fn handle(self: Box<Self>, sess: &mut ServerSessionImpl, m: Message) -> StateResult;
}

然后在收到client消息之后,在process_main_protocol中調(diào)用對應(yīng)握手包對象的handle函數(shù),并且會返回握手期望處理的下次數(shù)據(jù)包對象給state,以便下次收到消息繼續(xù)處理。

//process_main_protocol
self.state = Some(st.handle(self, msg)?);

握手流程:

-----ExpectClientHello::handle
-----ExpectTLS12Certificate::handle //如果需要驗(yàn)證client的證書,有這步
-----ExpectTLS12ClientKX::handle //密鑰交換
-----ExpectTLS12CertificateVerify::handle //驗(yàn)證client證書
-----ExpectTLS12CCS::handle //通知使用加密方式發(fā)送報(bào)文
-----ExpectTLS12Finished::handle //握手完成
-----ExpectTLS12Traffic:: handle //開發(fā)傳輸數(shù)據(jù)

消息傳輸

同樣,握手完成后,server在ExpectTLS12Traffic::handle中處理后續(xù)的傳輸協(xié)議中的消息。

impl State for ExpectTLS12Traffic {
    fn handle(self: Box<Self>, sess: &mut ServerSessionImpl, mut m: Message) -> StateResult {
        println!("-----ExpectTLS12Traffic::handle");
        sess.common.take_received_plaintext(m.take_opaque_payload().unwrap());
        Ok(self)
    }
}

數(shù)據(jù)加密和解密流程基本和client類似,不再詳述。

另外,client和server握手中需要發(fā)送的數(shù)據(jù)包構(gòu)造都在hs.rs::emit_xxx函數(shù)中

消息相關(guān)

該部分存在單獨(dú)的msgs目錄下,包含了握手過程中各種消息類型的定義,消息傳輸具體設(shè)計(jì)的fragment/deframe等。

所有消息統(tǒng)一的結(jié)構(gòu)MessageMessage也定義了一下方便獲取字段和數(shù)據(jù)的借口,這里不再詳述。

pub struct Message {
    pub typ: ContentType,
    pub version: ProtocolVersion,
    pub payload: MessagePayload,
}
//msgs/message.rs
MessagePayload
BorrowMessage

//msgs/handshake.rs
包含握手過程中,證書、密鑰交換的一些數(shù)據(jù)結(jié)構(gòu)

//msgs/deframe.rs
定義了MessageDeframer,管理Message數(shù)據(jù),read/deframe_one

//msgs/hsjoiner.rs
HandshakeJoiner,重建握手?jǐn)?shù)據(jù),驗(yàn)證數(shù)據(jù)等定義

//msgs/enums.rs
各種版本號,算法類型號,握手包類型序號等等的enum定義

//msgs/ccs.rs
密鑰交換相關(guān)定義

其他

文件 說明
key.rs 密鑰、證書結(jié)構(gòu)定義
pemfile.rs PEM文件解析生成密鑰相關(guān)接口
verify.rs 證書驗(yàn)證相關(guān)
suites.rs 加密套件、密鑰交換相關(guān)
sign.rs 簽名相關(guān)
vecbuf.rs 所有消息數(shù)據(jù)最底層存儲結(jié)構(gòu),vec構(gòu)成
webpki 三方庫,完成證書驗(yàn)證
ring 三方庫,完成加密算法相關(guān)能力

下篇在根據(jù)示例代碼分析一下rustls庫具體的使用

博客原文:http://anhkgg.com/rustls-source-code-analyze/

公眾號:漢客兒

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

相關(guān)閱讀更多精彩內(nèi)容

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