Rustls之源码分析总结(一)
来源:互联网 发布:始作俑者其无后乎 编辑:程序博客网 时间:2024/06/07 13:13
- 作者: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协议中所有包处理,包括握手和传输
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.rsInitialState::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(())}
消息处理调用流程如下:
//ClientSessionImplprocess_new_packets->process_msg->process_main_protocol->state.handle
下面直接列出client端握手处理流程:
ExpectServerHelloOrHelloRetryRequest:handle ExpectServerHello:handle // 处理serverhelloExpectTLS12Certificate: handle //验证证书ExpectTLS12ServerKX: handle // 密钥交换ExpectTLS12ServerDoneOrCertReq: handleExpectTLS12ServerDone: handleemit_clientkxemit_ccsExpectTLS12CCS:handle //通知使用加密方式发送报文,sess.common.peer_now_encrypting();设置后面数据会加密的状态emit_finishedExpectTLS12Finished: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_encryptingpub 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_protocolself.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.rsMessagePayloadBorrowMessage//msgs/handshake.rs包含握手过程中,证书、密钥交换的一些数据结构//msgs/deframe.rs定义了MessageDeframer,管理Message数据,read/deframe_one//msgs/hsjoiner.rsHandshakeJoiner,重建握手数据,验证数据等定义//msgs/enums.rs各种版本号,算法类型号,握手包类型序号等等的enum定义//msgs/ccs.rs密钥交换相关定义
其他
下篇在根据示例代码分析一下rustls库具体的使用
博客原文:https://anhkgg.github.io/rustls-source-code-analyze/
- Rustls之源码分析总结(一)
- Memcached源码分析之状态机(一)
- 源码分析之tinyhttpd(一)
- XUtils源码分析之HttpUtils(一)
- HDFS源码分析之UnderReplicatedBlocks(一)
- MapReduce源码分析之JobSubmitter(一)
- PackageManagerService源码分析之入门(一)
- MapReduce源码分析之JobSubmitter(一)
- 【Zookeeper】源码分析之服务器(一)
- kubenetes源码分析之DNS(一)
- kubernetes 源码分析之kubeadm(一)
- kubernetes 源码分析之ingress(一)
- Spring源码分析之AOP(一)
- SpringMVC源码分析(一)之DispatcherServlet
- JDK源码分析之ArrayList(一)
- Mupdf源码分析之MuPDFActivity类分析(一)
- 源码分析(一)
- contiki 源码分析之platform(一)(platform / cc2530dk)
- C++存储数据结构之四map
- 恢复Reflector反编译后资源文件的办法
- jquery-1.4.2.min.js等jquery库-----引入到eclipse工程中显示红叉,但是不影响运行----解决方法
- CSDN-markdown编辑器使用
- cinder添加一个新功能流程
- Rustls之源码分析总结(一)
- 写代码的日常——判断素数
- Android中单击空白区域隐藏键盘
- js自定义一个Map类
- Pycharm中_init_.py文件的作用
- 基于c++的统计输入字符串中单词个数程序
- 基础概念
- nginx配置文件说明
- Win7开启IPV6总结