美文网首页Rust语言Rust编程集程序员
Rustls之源码分析总结(一)

Rustls之源码分析总结(一)

作者: anhkgg | 来源:发表于2017-11-17 14:16 被阅读21次

    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传给内部处理,存入SessionCommonreceived_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等。

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

    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库具体的使用

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

    公众号:汉客儿

    相关文章

      网友评论

        本文标题:Rustls之源码分析总结(一)

        本文链接:https://www.haomeiwen.com/subject/kfljvxtx.html