- 作者: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.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庫具體的使用