QUIC学习笔记之 如何做到0RTT加密传输
簡單介紹下什么是QUIC,這是從Google官方文檔抄錄的一段話
QUIC (Quick UDP Internet Connections) is a new transport protocol for the internet, developed by Google.
QUIC solves a number of transport-layer and application-layer problems experienced by modern web applications, while requiring little or no change from application writers. QUIC is very similar to TCP+TLS+HTTP2, but implemented on top of UDP. Having QUIC as a self-contained protocol allows innovations which aren’t possible with existing protocols as they are hampered by legacy clients and middleboxes.
簡單來說,就是QUIC是Google提出的一種基于UDP改進的通信協議,其目的是降低網絡通信的延遲,提供更好的用戶交互體驗。
原本的TCP+TLS的RTT耗時大,主要是由于TCP需要三次握手,以及TLS需要多次交換數據來生成安全的密鑰
而QUIC則使用了Diffie-Hellman算法(迪菲-赫爾曼算法)來保證數據交互的安全性并合并了它的加密和握手過程來減小連接建立過程中的往返次數,以此來達到0RTT的目的
Diffie-Hellman算法
DH算法,由W.Diffie和M.E.Hellman在1976年公布的一種密鑰一致性算法,該算法是一種建立密鑰的方法,并非加密方法,但其產生的密鑰可用于加密、密鑰管理或任何其它的加密方式,這種密鑰交換技術的目的在于使兩個用戶間能安全地交換密鑰(KEY)以便用于今后的報文加密。
離散對數:定義素數p的原始根為滿足如下條件的整數,如果g為p的原始根,則:g mod p,g^ mod p,…,g^(p-1) mod p是各不相同的整數,且以某種排列方式組成了從1到p-1的所有整數。
對于任意數b及素數p的原始根g,可以找到一個唯一的指數i,滿足:b=(g^ i )mod p,其中0≤i≤p-1,那么指數i 稱為b的以g為基數的模p的離散對數。
Diffie-Hellman算法的有效性依賴于計算離散對數的難度,當已知大素數p和它的一個原根a后,對于給定的b,要計算出i 被認為是很困難的,而給定i 計算b卻相對容易。
其協商密鑰的流程如下:
1)雙方都共享一個大素數p和它的原始根g
2)各自產生一個隨機數X,通過g^X mod p 計算出一個public value Y,將其發送給對方
3)各自在使用接收到的Y(public value)計算出相同的密鑰K (K = Y^X mod p)
最終雙方計算出一致的密鑰,就可以用于加解密了
將這種發送給對方的key稱為public key,保留在自己手上的key為private key
這里Ya,Yb就是public key,A和B就是private key
QUIC Handshake
QUIC協議依賴于合并加密和數據傳輸過程中的握手來創建一個安全的連接。 在連接建立成功后, 客戶端會緩存起來原始的連接信息等。 在接下來與相同的服務器建立連接的過程中, 客戶端能夠在不增加額外RTT的情況下建立一個加密的連接,數據要發送的數據可以在握手的包中捎帶著發送過去,而不用等待服務器的回復,從而實現0RTT。
所以,所謂QUIC的0RTT是指在建立連接之后,后續發送數據都不需要增加額外的RTT時間,最開始的握手還是需要1RTT的時間消耗的
注意這里的Diffie-Hellman key不單單分為public key和private key,還有各自不同的生命周期long-term key 和ephemeral key,前者用于生成初始密鑰,后者用于生成后續加密的密鑰
Initial Handshake:
開始握手時,客戶端會像服務端發送Inchoate CHLO的命令,服務器會返回REJ消息,它包括:
接收到這個REJ消息后,客戶端會進行解析認證,緩存必要的配置,之后客戶端再向服務端發送一個COMPLETE CHLO, 會在其中帶上客戶端的ephemeral DH public value
Final (and repeat) handshake
一個連接的所有key都是通過Diffie-Hellman算法來創建的。
發送Complete CHLO之后, 客戶端會產生自己的initial key (通過服務端的long-term Diffie-Hellman public value以及客戶端的ephemeral Diffie-Hellman private value)
有了密鑰,此時客戶端就能使用initial key加密應用數據發送到服務端了(為了實現這里的0RTT,客戶端不會等待COMPLETE CHLO的回復,而是直接發送加密的數據包)。 如果握手成功了, 服務端返回一個server hello(SHLO)消息。 這個消息用initial keys進行了加密, 并且含有服務端ephemeral的Diffie-Hellman的public value。
這樣客戶端和服務端就都有了對方的ephemeral的Diffie-Hellman的public value,這樣就會生成第二個密鑰 forward-secure key.服務端會使用這個密鑰來加密自己對客戶請求的response結果,而客戶端后面的數據包也會使用forward-secure key來加密
QUIC提供了兩個層面的安全性保證:
最初的handshake通過initial keys加密,后續的客戶端信息和服務端信息通過forward-secure 加密
這樣就可以保證密鑰的前向安全性,之后也可以在通信的過程中就實現對密鑰的更新。接收方意識到有新的密鑰要更新時,會嘗試使用新舊兩種密鑰對數據進行解密,直到成功才會正式更新密鑰,否則會一直保留舊密鑰。
客戶端會緩存server config和source-address token,如果再次向相同的服務器建立連接就會直接發送Complete CHLO命令,實現0-RTT
如果source-address token或者server config過期,服務端會發送REJ消息 ,就會像Initial handshake一樣,重新進行連接的建立
我認為之所以將發送連接拒絕的情形也認為是0RTT,是因為在拒絕連接的同時也返回了建立連接所需的REJ消息,這樣客戶端在下次交互時就可以直接傳輸有效的數據包給服務端而不需要額外的RTT時間
版本協商:
Quic 的客戶端會在第一個packet里聲明自己的version ,如果server端并不使用這個client version,那么服務端會發送一個帶有所有server端支持的版本信息的Version Negotiation packet,以供客戶端選擇合適的版本
最后是一個Google官網關于QUIC握手的一個流程圖
(1) 客戶端判斷本地是否已有服務器的全部配置參數,如果有則直接跳轉到(5)發送client hello
(2) 客戶端向服務器發送inchoate client hello(CHLO)消息,嘗試建立連接
(3) 服務器收到CHLO,回復rejection(REJ)消息,其中包含服務器的server config ,long-term DH public key 等
(4) 客戶端收到REJ,提取并認證和存儲服務器server config
(5) 客戶端向服務器發送client hello消息,開始準備發送數據包,消息中包括客戶端的DH public vlaue。此時客戶端可以根據server config參數和自己的public value,計算出初始密鑰。
(6) 服務器收到 client hello,如果不同意連接就回復REJ,同(3);如果同意連接,根據客戶端的public value計算出初始密鑰initial key,并回復server hello(SHLO)消息,SHLO使用initial key加密,并且其中包含服務器的臨時的DH public value
(7) 客戶端收到服務器的回復,如果是REJ則情況同(4);如果是SHLO,則嘗試用初始密鑰解密,提取出ephemeral DH public value
(8) 客戶端和服務器根據得到的ephemeral public key, 計算出新的forward-secure key
(9) 雙方更換為使用 forward-secure key 通信,QUIC握手過程完畢。之后會話密鑰更新的流程與以上過程類似。
參考鏈接:
The QUIC Transport Protocol:Design and Internet-Scale Deployment
QUIC Crypto
總結
以上是生活随笔為你收集整理的QUIC学习笔记之 如何做到0RTT加密传输的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 让互联网更快的协议,QUIC在腾讯的实践
- 下一篇: TCP的拥塞控制--慢启动,拥塞避免,快