V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
anhkgg
V2EX  ›  SSL

Rustls 源码分析总结(一)

  •  
  •   anhkgg · 2017-11-17 13:48:15 +08:00 · 2039 次点击
    这是一个创建于 2562 天前的主题,其中的信息可能已经有所发展或是发生改变。
    **由于不知道 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
    emit_ccs
    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   ·   实用小工具   ·   4903 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 09:41 · PVG 17:41 · LAX 01:41 · JFK 04:41
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.