Rustls 源码分析总结(一) - V2EX
anhkgg
V2EX    SSL

Rustls 源码分析总结(一)

  •  
  •   anhkgg 2017-11-17 13:48:15 +08:00 2302 次点击
    这是一个创建于 2933 天前的主题,其中的信息可能已经有所发展或是发生改变。
    **由于不知道 V2EX 支持的表格格式是什么样的?解析不对,所以直接放原样 md 了**

    # Rustls 源码分析总结

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


    rustls 已经支持 tls1.3,但是测试分析中使用的 tls1.2,所以后面分析主要集中在 tls1.2。

    主要分析的源码内容:
    1. client 和 server 的握手协议流程
    2. rustls 是如何进行数据传输的
    3. 数据传输是如何加密解密的

    ## 源码结构

    **分为 client 和 server 两部分**

    ### 公共接口

    **session.rs**定义了 SessionCommon,包括了数据传输、数据加密、包处理相关接口。

    **主要字段**

    ```
    pub struct SessionCommon {
    pub negotiated_version: Option<ProtocolVersion>, //协商好的协议版本
    pub is_client: bool, //是客户端 true,是服务端 false
    message_encrypter: Box<MessageEncrypter>, //数据加密接口
    message_decrypter: Box<MessageDecrypter>, //数据解密接口
    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, // 默认 false,握手完成字段为 true
    pub want_write_key_update: bool,
    pub message_deframer: MessageDeframer, //消息帧处理对象,保存所有 Message 包
    pub handshake_joiner: HandshakeJoiner,
    pub message_fragmenter: MessageFragmenter,
    received_plaintext: ChunkVecBuffer, //缓存接收到的数据明文
    sendable_plaintext: ChunkVecBuffer,//缓存握手后需要传输的数据明文
    pub sendable_tls: ChunkVecBuffer, //缓存握手数据包
    }
    ```

    **主要接口**

    | 函数名 | 说明
    | --- | ---
    | `read_tls` | 接收底层连接数据
    | `write_tls` | 通过底层连接发送数据
    | `process_new_packets` | 每次调用 read_tls 之后都需要调用该函数主动触发消息处理
    | `wants_read/wants_write` | 是否有数据需要接收发送
    | `encrypt_outgoing` | 加密要发送的数据,在握手完成之后需要
    | `decrypt_incoming` | 解密要接收的数据,在握手完成之后需要
    | `send_msg_encrypt` | 发送加密数据
    | `send_appdata_encrypt` | 发送握手之后的数据,加密
    | `send_some_plaintext` | 发送明文数据,握手之后会被加密发送
    | `start_traffic` | 握手完成之后调用,设置传输标志,发送缓存的数据明文
    | `send_msg` | 发送 TLS 消息,根据是否加密走不通发送方式
    | `take_received_plaintext` | 握手完成之后,收到数据会被调用,参数已经是明文 Message
    | `set_message_encrypter` | 设置消息加密接口,`start_encryption_tls12`中调用
    | `set_message_decrypter` | 设置消息解密接口,`start_encryption_tls12`中调用
    | `start_encryption_tls12` | TLS1.2 设置加解密接口,在 ExpectTLS12ServerDone::handle/ExpectTLS12ClientKX::handle 调用

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

    `MessageEncrypter`,`MessageDecrypter`,具体使用加解密方法在握手过程中 ExpectTLS12ServerDone::handle/ExpectTLS12ClientKX::handle 设置。

    ```
    //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 导出 ClientSession 接口,外部使用
    src/client/hs.rs tls 协议中所有包处理,包括握手和传输
    src/client/
    ```

    `ClientSession`内部由`ClientSessionImpl`实现。

    ```
    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>>, // 保存握手过程中的交互状态,握手中处理对象都实现 State 接口
    pub server_cert_chain: CertificatePayload, // 服务端证书链
    }
    ```

    **握手,准备第一个数据包**。

    `ClientSessionImpl::new`内部就会准备握手要发送的第一个数据包。

    ```
    cs.state = Some(hs::start_handshake(&mut cs, hostname));
    //cs.state 保存下一次将处理数据对象
    ---> //进入 hs.rs
    InitialState::emit_initial_client_hello
    --->
    emit_client_hello_for_retry
    ---> //构造发送的数据包
    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,
    }),
    };
    ```

    然后,收到返回数据之后,会在`ClientSessionImpl::process_main_protocol`调用`state.handle`来处理收到的数据,然后返回新的 state,用于下次处理,如此循环,知道握手完成。

    ```
    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
    })?;
    //处理本次数据,返回下次需要处理的数据对象
    self.state = Some(state.handle(self, msg)?);
    Ok(())
    }
    ```

    消息处理调用流程如下:

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

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

    ```
    ExpectServerHelloOrHelloRetryRequest:handle
    ExpectServerHello:handle // 处理 serverhello
    ExpectTLS12Certificate: handle //验证证书
    ExpectTLS12ServerKX: handle // 密钥交换
    ExpectTLS12ServerDoneOrCertReq: handle
    ExpectTLS12ServerDone: handle
    emit_clientkx
    ExpectTLS12CCS:handle //通知使用加密方式发送报文,sess.common.peer_now_encrypting();设置后面数据会加密的状态
    emit_finished
    ExpectTLS12Finished:handle // 握手结束
    ```

    在`ExpectTLS12Finished::handle`中,会保存`session`,开始传输数据,以及返回下次的`state`,**此时握手协议已经完成**。

    ```
    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(); //发送数据
    Ok(st.into_expect_tls12_traffic(fin)) // 下次需要 ExpectTLS12Traffic
    ```

    后面数据传输的所有流程都会进入`ExpectTLS12Traffic::handle`,也就是开始**传输协议**。

    ```
    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,所以以后都会进入这里
    }
    }
    ```

    **传输数据的处理**。

    **接收数据**

    调用`take_received_plaintext`将获取到的明文 Message 传给内部处理,存入`SessionCommon`的`received_plaintext`,等待用户的提取。

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

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

    在`process_msg`中会判断`peer_encrypting`状态为真则将数据解密,而该状态是在握手中`ExpectTLS12CCS::handle` 被设置为 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)?; //解密数据
    msg = dm;
    }

    //self.common.peer_encrypting
    pub fn peer_now_encrypting(&mut self) {
    self.peer_encrypting = true;
    }
    ```

    **发送数据**

    **握手过程中**,发送数据包使用`sess.common.send_msg(ch, false)`。`send_msg`内部根据是否加密状态(`must_encrypt`)进行不同处理,直接缓存或者调用`send_msg_encrypt`加密之后缓存。

    ```
    send_msg_encrypt->send_single_fragment->encrypt_outgoing(加密)
    ```

    最后都是通过`queue_tls_message`将数据先缓存,然后在调用`write_tls`之后将数据发送。

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

    **握手完成后**,通过`ClientSession`实现的`io::write`(或者`write_all`)接口发送明文数据。

    ```
    impl io::Write for ClientSession {
    //先缓存数据
    fn write(&mut self, buf: &[u8]) -> io::Result<usize>{
    self.imp.common.send_some_plaintext(buf)
    }
    //flush 时才发送数据
    fn flush(&mut self) -> io::Result<()> {
    self.imp.common.flush_plaintext();
    Ok(())
    }
    }
    ```

    `send_some_plaintext`在根据是否握手完成有不同的操作,握手未完成时,先缓存明文到`sendable_plaintext`,握手完成后,直接调用`send_appdata_encrypt`缓存密文(进入`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);
    }
    //握手完成,直接缓存加密数据
    Ok(self.send_appdata_encrypt(data, limit))
    }
    ```

    握手完成时,之前缓存的明文数据通过`start_traffic`实际将数据加密缓存到 sendable_tls,最后也是通过 write_tls 发送出去。

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

    握手完成之后调用的`send_some_plaintext`是直接将数据加密缓存,在 write_tls 后发送出去。


    ### server 详解

    ```
    src/server/mod.rs 导出 ServerSession 接口,外部使用
    src/server/hs.rs tls 协议中所有包处理,包括握手和传输
    src/client/
    ```

    公开外部使用的借口 ServerSession,内部由 ServerSessionImpl 实现。

    ```
    pub struct ServerSessionImpl {
    pub config: Arc<ServerConfig>, //证书、密钥等配置
    pub secrets: Option<SessionSecrets>, //会话密钥
    pub common: SessionCommon, // 实际握手传输数据处理对象
    sni: Option<webpki::DNSName>, //SNI(Server Name Indication) ,解决一个服务器使用多个域名和证书的 SSL/TLS 扩展
    pub alpn_protocol: Option<String>,
    pub error: Option<TLSError>,
    pub state: Option<Box<hs::State + Send>>, //握手和传输中处理数据包的状态,每个状态的数据包处理对象
    pub client_cert_chain: Option<Vec<key::Certificate>>, //client 证书链
    }
    ```

    **接口基本和 ClientSession 类似,不再详述**

    **握手流程**

    server 和 client 处理握手的方式都一样,每个握手包处理对象都会实现 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`中调用对应握手包对象的 handle 函数,并且会返回握手期望处理的下次数据包对象给 state,以便下次收到消息继续处理。

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

    握手流程:

    ```
    -----ExpectClientHello::handle
    -----ExpectTLS12Certificate::handle //如果需要验证 client 的证书,有这步
    -----ExpectTLS12ClientKX::handle //密钥交换
    -----ExpectTLS12CertificateVerify::handle //验证 client 证书
    -----ExpectTLS12CCS::handle //通知使用加密方式发送报文
    -----ExpectTLS12Finished::handle //握手完成
    -----ExpectTLS12Traffic:: handle //开发传输数据
    ```

    **消息传输**

    同样,握手完成后,server 在`ExpectTLS12Traffic::handle`中处理后续的传输协议中的消息。

    ```
    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)
    }
    }
    ```

    数据加密和解密流程基本和 client 类似,不再详述。

    **另外,client 和 server 握手中需要发送的数据包构造都在 hs.rs::emit_xxx 函数中**

    ### 消息相关

    该部分存在单独的 msgs 目录下,包含了握手过程中各种消息类型的定义,消息传输具体设计的`fragment/deframe`等。

    所有消息统一的结构`Message `,`Message `也定义了一下方便获取字段和数据的借口,这里不再详述。

    ```
    pub struct Message {
    pub typ: ContentType,
    pub version: ProtocolVersion,
    pub payload: MessagePayload,
    }
    ```

    ```
    //msgs/message.rs
    MessagePayload
    BorrowMessage

    //msgs/handshake.rs
    包含握手过程中,证书、密钥交换的一些数据结构

    //msgs/deframe.rs
    定义了 MessageDeframer,管理 Message 数据,read/deframe_one

    //msgs/hsjoiner.rs
    HandshakeJoiner,重建握手数据,验证数据等定义

    //msgs/enums.rs
    各种版本号,算法类型号,握手包类型序号等等的 enum 定义

    //msgs/ccs.rs
    密钥交换相关定义

    ```


    ## 其他

    |文件 | 说明 |
    | : | : |
    |key.rs | 密钥、证书结构定义 |
    |pemfile.rs | PEM 文件解析生成密钥相关接口 |
    |verify.rs | 证书验证相关 |
    |suites.rs | 加密套件、密钥交换相关 |
    |sign.rs | 签名相关 |
    |vecbuf.rs | 所有消息数据最底层存储结构,vec 构成 |
    |webpki | 三方库,完成证书验证 |
    |ring | 三方库,完成加密算法相关能力 |

    **下篇在根据示例代码分析一下 rustls 库具体的使用**

    博客原文:[https://anhkgg.github.io/rustls-source-code-analyze/]( https://anhkgg.github.io/rustls-source-code-analyze/)
    目前尚无回复
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     941 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 24ms UTC 19:05 PVG 03:05 LAX 11:05 JFK 14:05
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86