簡介:SRTP:安全傳輸協議(Secure Real-time Transport)html
經過 DTLS 協商後,RTC 通訊的雙方完成
MasterKey
和MasterSalt
的協商。接下來,咱們繼續分析在 WebRTC 中,如何使用交換的密鑰,來對 RTP 和 RTCP 進行加密,實現數據的安全傳輸。同時,本文會對 libsrtp 使用中,遇到的問題的進行解答,例如,什麼是 ROC,ROC 爲何是 32-bits?爲何會返回 error\_code=9, error\_code=10?交換的密鑰有生命週期嗎,若是有是多長時間呢?閱讀本篇以前建議閱讀 DTLS 協商篇,二者結合,效果更佳哦!git
做者|進學github
審校|泰一算法
RTP/RTCP
協議並無對它的負載數據進行任何保護。所以,若是攻擊者經過抓包工具,如 Wireshark,將音視頻數據抓取到後,經過該工具就能夠直接將音視頻流播放出來,這是很是恐怖的事情。api
在 WebRTC 中,爲了防止這類事情發生,沒有直接使用 RTP/RTCP
協議,而是使用了 SRTP/SRTCP
協議 ,即安全的 RTP/RTCP
協議。WebRTC 使用了很是有名的 libsrtp 庫將原來的 RTP/RTCP
協議數據轉換成 SRTP/SRTCP
協議數據。緩存
SRTP 要解決的問題:安全
・對 RTP/RTCP
的負載 (payload) 進行加密,保證數據安全;服務器
・保證 RTP/RTCP
包的完整性,同時防重放攻擊。網絡
從 SRTP 結構圖中能夠看到:session
1. 加密部分 Encrypted Portion
,由 payload
, RTP padding
和 RTP pad count
部分組成。也就是咱們一般所說的僅對 RTP 負載數據加密。
2. 須要校驗部分 Authenticated Portion
,由 RTP Header
, RTP Header extension
和 Encrypted Portion
部分組成。
一般狀況下只須要對 RTP 負載數據進行加密,若是須要對 RTP header extension 進行加密,RFC6904 給出了詳細方案,在 libsrtp 中也完成了實現。
從 SRTCP
結構圖中能夠看到:
1. 加密部分 Encrypted Portion
,爲 RTCP Header
以後的部分,對 Compound RTCP
也是一樣。
2. E-flag 顯式給出了 RTCP 包是否加密。(PS:一個 RTP 包怎麼判斷是加密的?)
3. SRTCP index
顯示給出了 RTCP 包的序列號,用來防重放攻擊。(PS:一個 RTP 包的 16-bits 的序列號能夠防重放攻擊嗎?)
4. 待校驗部分 Authenticated Portion
,由 RTCP Header
和 Encrypted Portion
部分組成。在初步認識了 SRTP
和 SRTCP
的結構後,接下來介紹 Encrypted Portion
和 Authenticated Portion
如何獲得了。
在 SRTP/SRTCP
協議中,使用二元組 的方式來標識一個通訊參與者的 SRTP/SRTCP
會話,稱爲 SRTP/SRTCP Session
。
在 SRTP
協議中使用三元組 來標識一個 stream,一個 SRTP/SRTCP Session
由多個 stream 組成。對每一個 stream 的加解密相關參數的描述,稱爲 Cryptographic Context
。
每一個 stream 的 Cryptographic Context
中 中的包含以下參數:
・SSRC: Stream 使用的 SSRC。
・Cipher Parameter:加解密使用的 key, salt,算法描述 (類型,參數等)。
・Authentication Parameter: 完整性使用的 Key, salt,算法描述 (類型,參數等)。
・Anti-Replay Data: 防止重放攻擊緩存的數據信息,例如,ROC,最大序號等。
在 SRTP/SRTCP Session
中,每一個 Stream 都會使用到屬於本身的,加解密 Key,Authentication Key。這些 Key 都是在同一個 Session 中使用到的,稱爲 Session Key
。這些 Session Key
是經過對 Master Key
使用 KDF (Key Derivation Function) 導出的。
KDF
是用於導出 Session Key
函數,KDF 默認使用是加解密函數。例如,在完成 DTLS 後,協商獲得的 SRTP 加密算法的 Profile 爲:
SRTP_AES128_CM_HMAC_SHA1_80 cipher: AES_128_CM cipher_key_length: 128 cipher_salt_length: 112 maximum_lifetime: 2^31 auth_function: HMAC-SHA1 auth_key_length: 160 auth_tag_length: 80
對應的 KDF
爲 AES128_CM
。Session Key
的導出流程以下圖所示:
Session Key
的導出依賴於以下參數:• key_label
: 根據導出的 Key 的類型不一樣,key_label
取值以下:
・master\_key: DTLS 完成後,協商獲得的 Key。
・master\_salt: DTLS 完成後,協商獲得的 Salt。
・packet\_index: RTP/RTCP 的包序號。SRTP 使用 48-bits 的隱式包須要,SRTCP 使用 31-bits 包序號。參考序號管理。
・key\_derivation\_rate: key 導出速率,記爲 kdr。默認取值爲 0,執行 1 次 Key 導出。取值範圍 {{1,2,4,...,2^24}
。在 key_derivation_rate>0
的狀況下,在加密以前,執行一次 key 導出,後續在 packet\_index/key\_derivation\_rate > 0 時,執行 key 導出。
r = packet_index / kdr key_id = label || r x = key_id XOR master_salt key = KDF(master_key, x)
'/':表示整除,B=0 時,C = A/B=0。
||
:表示鏈接的含義。A,B,C 使用網絡字節序表示,C = A||B, 則 C 的高字節爲 A,低字節位爲 B。
XOR
:是異或運算,計算時按照低字節位對齊。
如下使用 AES128_CM
,舉例說明 Session Key
的導出過程,假設 DTLS
協商獲得:
master_key: E1F97A0D3E018BE0D64FA32C06DE4139 // 128-bits master_salt: 0EC675AD498AFEEBB6960B3AABE6 // 112-bits
導出加密 Key (cipher key):
packet_index/kdr: 000000000000 label: 00 master_salt: 0EC675AD498AFEEBB6960B3AABE6 ----------------------------------------------- xor: 0EC675AD498AFEEBB6960B3AABE6 (x, KDF input) x*2^16: 0EC675AD498AFEEBB6960B3AABE60000 (AES-CM input) cipher key: C61E7A93744F39EE10734AFE3FF7A087 (AES-CM output)
導出 SALT Key (cipher salt):
packet_index/kdr: 000000000000 label: 02 master_salt: 0EC675AD498AFEEBB6960B3AABE6 ---------------------------------------------- xor: 0EC675AD498AFEE9B6960B3AABE6 (x, KDF input) x*2^16: 0EC675AD498AFEE9B6960B3AABE60000 (AES-CM input) 30CBBC08863D8C85D49DB34A9AE17AC6 (AES-CM ouptut) cipher salt: 30CBBC08863D8C85D49DB34A9AE1
導出校驗 Key (auth key),須要 auth key
長度爲 94 字節:
packet_index/kdr: 000000000000 label: 01 master salt: 0EC675AD498AFEEBB6960B3AABE6 ----------------------------------------------- xor: 0EC675AD498AFEEAB6960B3AABE6 (x, KDF input) x*2^16: 0EC675AD498AFEEAB6960B3AABE60000 (AES-CM input) auth key AES input blocks CEBE321F6FF7716B6FD4AB49AF256A15 0EC675AD498AFEEAB6960B3AABE60000 6D38BAA48F0A0ACF3C34E2359E6CDBCE 0EC675AD498AFEEAB6960B3AABE60001 E049646C43D9327AD175578EF7227098 0EC675AD498AFEEAB6960B3AABE60002 6371C10C9A369AC2F94A8C5FBCDDDC25 0EC675AD498AFEEAB6960B3AABE60003 6D6E919A48B610EF17C2041E47403576 0EC675AD498AFEEAB6960B3AABE60004 6B68642C59BBFC2F34DB60DBDFB2 0EC675AD498AFEEAB6960B3AABE60005
AES-CM 的介紹,參考 AES-CM。
至此,咱們獲得了 SRTP/SRTCP
加密和認證須要的 Session Key
:cipher key,auth key,salt key。
在 RTP
包結構定義中使用 16-bit
來描述序列號。考慮到防重放攻擊,消息完整性校驗,加密數據,導出 SessionKey 的須要,在 SRTP
協議中,SRTP 包的序列號,使用隱式方式來記錄包序列號 packet_index
,使用 i 標識 packet\_index。
對於發送端來講,i 的計算方式以下:
i = 2^16 * ROC + SEQ
其中,SEQ 是 RTP 包中描述的 16-bit 包序號。ROC (rollover couter) 是 RTP 包序號 (SEQ) 翻轉計數,也就是每當 SEQ/2^16=0
, ROC 計數加 1。ROC 初始值爲 0。
對於接收端來講,考慮到丟包和亂序因素的影響,除了維護 ROC
,還須要維護一個當前收到的最大包序號 s_l
,當一個新的包到來時候,接收端須要估計出當前包所對應的實際 SRTP 包的序號。ROC 的初始值爲 0,s\_l 的初始值爲收到第一個 SRTP 包的 SEQ。後續經過以下公式,估計接收到的 SRTP 序號 i:
i = 2^16 * v + SEQ
其中,v
可能的取值 { ROC-1, ROC, ROC+1 }
,ROC 是接收端本地維護的 ROC,SEQ 是收到 SRTP 的序號。v 分別取 ROC-1,ROC,ROC+1 計算出 i,與 2^16*ROC + s_l
進行比較,那個更接近,v 就取對應的值。完成 SRTP 解密和完整性校驗後,更新 ROC 和 s\_l,分以下 3 種狀況:
1. v = ROC - 1, ROC 和 s\_l 不更新。
2. v = ROC,若是 SEQ > s\_1,則更新 s\_l = SEQ。
3. v = ROC + 1, ROC = v = ROC + 1,s\_l = SEQ。
更直觀的代碼描述:
if (s_l < 32768) if (SEQ - s_l > 32768) set v to (ROC-1) mod 2^32 else set v to ROC endif else if (s_l - 32768 > SEQ) set v to (ROC+1) mod 2^32 else set v to ROC endif endif return SEQ + v*65536
RTCP
中沒有描述序號的字段,SRTCP
的序號在 SRTCP 包,使用 31-bits
中顯示描述,詳見 SRTCP 格式,也就是說在 SRTCP 的最大序列號爲 2^31。
能夠看到 SRTP 的序列號最大值爲 2^48, SRTCP 的序列號最大值爲 2^16。在大多數應用中(假設每 128000 個 RTP 數據包至少有一個 RTCP 數據包),SRTCP 序號將首先達到上限。以 200 SRTCP 數據包 / 秒的速度, SRTCP 的 2^31 序列號空間足以確保大約 4 個月的通訊。
攻擊者將截獲的 SRTP/SRTCP 包保存下來,而後從新發送到網絡中,實現了包的重放。SRTP 接收者經過維護一個重放列表 (ReplayList) 來防止這種攻擊。理論上 Replay List 應該保存全部接收到並完成校驗的包的序列號 index。在實際狀況下 ReplayList 使用滑動窗口(sliding window)來實現防重放攻擊。使用 SRTP-WINDOW-SIZE
來描述滑動窗口的大小。
在序列號管理部分,咱們詳述了接收者,根據接收到的 SRTP 包的 SEQ,ROC,s\_l 估算出 SRTP 包的 packet_index
的方法。同時,將接收者已經接收到 SRTP 包的最大序列號,記爲 local_packet_index
。計算差值 delta
:
delta = packet_index - local_packet_index
分以下 3 種狀況說明:
1. delta > 0:表示收到了新的包。
2. delta <-(SRTP-WINDOW-SIZE - 1) < 0:表示收到的包的序列號,小於重放窗口要求的最小序號。libSRTP 收到這樣的包時,會返回 srtp_err_status_replay_old=10
, 表示收到舊的重放包。
3. delta <0, delta>= -(SRTP-WINDOW-SIZE - 1): 表示收到了重放窗口以內的包。若是在 ReplayList 找到對應的包,則是一個 index 重複的重放包。libSRTP 收到這樣的包時,會返回 srtp_err_status_replay_fail=9
。不然表示收到一個亂序包。
下圖更加直觀說明防重放攻擊的三個區域:
SRTP-WINDOW-SIZE 的取值,最小是 64。應用能夠根據須要設置成較大的值,libsrtp 會向上取整爲 32 的整數倍。例如,在 WebRTC 中
SRTP-WINDOW-SIZE
= 1024。使用者能夠根據須要進行調整,但要達到防重放攻擊的目的。
在 SRTCP 中,packet index 顯式給出。在 libsrtp 中,SRTCP 的防重放攻擊的窗口大小爲 128。使用 window_start
記錄防重放攻擊的起始序列號。SRTCP 防重放攻擊的檢查步驟以下:
1. index > window\_start + 128: 收到新的 SRTCP 包。
2. index < window\_start: 收到包的序列號在重放窗口的左側,能夠認爲咱們收到了比較老的包。libsrtp 收到這樣的包以後,會返回到 srtp_err_status_replay_old=10
。
3. replay\_list\_index = index - windwo\_start:在 ReplayList 中 replay\_list\_index 對應的標識位爲 1,表示已經收到包,libsrtp 返回 srtp_err_status_replay_fail=9
。對應的標識位爲 0,表示收到亂序包。
在 SRTP 中,使用了 CTR(Counter mode)模式的 AES 加密算法,CTR 模式經過遞增一個加密計數器以產生連續的密鑰流,計數器能夠是任意保證長時間不產生重複輸出的密鑰。根據計數方式的不一樣,分爲如下兩種類型:
• AES-ICM
: ICM 模式(Integer Counter Mode,整數計數模式),使用整數計數運算。
• AES-GCM
: GCM 模式(Galois Counter Mode,基於伽羅瓦域計數模式),計數運算定義在伽羅瓦域。
在 SRTP 中,使用 AES-ICM
完成加密算法,同時使用 HMAC-SHA1
完成 MAC
計算,對數據進行完整性校驗,加密和 MAC 計算須要分兩步完成。AES-GCM
基於 AEAD(Authenticated-Encryption with Associated-Data,關聯數據的認證加密)的思想,在對數據進行加密的同時計算 MAC
值,實現了一個步驟,完成加密和校驗信息的計算。下面分別對這個 AES-ICM
和 AES_GSM
的用法進行介紹。
上圖描述了 AES-ICM
的加密和解密過程,圖中的 K 是經過 KDF 導出的 SessionKey
。加密和加密都是經過對 Counter 進行加密,與明文 P 異或運算獲得加密數據 C,反之,與密文 C 異或運算獲得明文數據 P。考慮到安全性,Counter 生成依賴於 Session Salt
, 包的索引(packet index)和包的 SSRC。Counter 是 128-bits 的計數,生成方式以下定義:
one byte <--> 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |00|00|00|00| SSRC | packet index | b_c |---+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ v | salt (k_s) |00|00|->(+) +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | | v +-------------+ encryption key (k_e) -> | AES encrypt | +-------------+ | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | | keystream block |<--+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
散列消息認證碼(Hash-based message authentication code,縮寫爲 HMAC),是一種經過特別計算方式以後產生的消息認證碼(MAC),使用密碼散列函數,同時結合一個加密密鑰,它能夠用來保證數據的完整性,同時能夠用來做某個消息的身份驗證。HMAC 經過一個標準算法,在計算哈希的過程當中,把 key 混入計算過程當中。HMAC 的加密實現以下:
HMAC(K,M) = H ( (K XOR opad ) + H( (K XOR ipad ) + M ) )
・H:hash 算法,好比,MD5,SHA-1,SHA-256。
・B:塊字節的長度,塊是 hash 操做的基本單位。這裏 B=64。
・L:hash 算法計算出來的字節長度。(L=16 for MD5, L=20 for SHA-1)。
・K:共享密鑰,K 的長度能夠是任意的,可是爲了安全考慮,仍是推薦 K 的長度 > B。
當 K 長度大於 B 時候,會先在 K 上面執行 hash 算法,將獲得的 L 長度結果做爲新的共享密鑰。若是 K 的長度
・M:要認證的內容。
・opad:外部填充常量,是 0x5C 重複 B 次。
・ipad:內部填充常量,是 0x36 重複 B 次。
・XOR:異或運算。
・+:表明 "鏈接" 運算。
計算步驟以下:
1. 將 0x00 填充到 K 的後面,直到其長度等於 B。
2. 將步驟 1 的結果跟 ipad 作異或。
3. 將要加密的信息附在步驟 2 的結果後面。
4. 調用 H 方法。
5. 將步驟 1 的結果跟 opad 作異或。
6. 將步驟 4 的結果附在步驟 5 的結果後面。
7. 調用 H 方法。
SRTP
和 SRTCP
計算 Authentication tag
,使用的 K
對應 Key 管理部分描述的 RTP auth key
和 RTCP auth key
,使用的 Hash 算法爲 SHA-1
,Authentication tag
的長度爲 80-bits。
在計算 SRTP 的,要認證的內容 M 爲:
M = Authenticated Portion + ROC
其中,+
表明 "鏈接" 運算,Authenticated Portion
在 SRTP
的結構圖中給出。
在計算 SRTCP
時,要認證的內容 M 爲:
M=Authenticated Portion
其中,Authenticated Portion
在 SRTCP
的結構圖中給出。
經過使用 Authenticated Portion
算法,計算獲得 SRTP/SRTCP 的 Encrypted Portion Portion
部分。
AES-GCM
使用計數器模式來加密數據,該操做能夠有效地流水線化,GCM 身份驗證使用的操做特別適合於硬件中的有效實現。在 GCM-SPEC 詳述了 GCM 的理論知識, Section4.2 Hardware 詳述了硬件實現。
AES-GCM
在 SRTP
加密中的應用,在 RFC7714 進行了詳細描述。Key 管理和序列號管理與本文中描述的相同,須要注意的是:
AES-GCM
做爲一種 AEAD(Authenticated Encryption with Associated Data)加密算法,輸入和輸出是什麼,對應到 SRTP/SRTCP
的包結構中理解。Counter
的是計算方式和 AES-ICM 中描述的計算方式不一樣,須要重點關注。libsrtp 已經實現了 AES-GCM
,有興趣的同窗,能夠結合代碼進行研讀。
libsrtp 是被普遍使用的 SRTP/SRTCP 加密的開源項目。常常用到的 api 以下:
1. srtp_init
,初始化 srtp 庫,初始化內部加密算法,在使用 srtp 前,必需要調用了。
2. srtp_create
, 建立 srtp\_session,能夠結合本文中介紹的 session,session key 等概念一塊兒理解。
3. srtp_unprotect/srtp_protect
,RTP 包加解密接口。
4. srtp_protect_rtcp/srtp_unprotect_rtcp
,RTCP 包的加解密接口。
5. srtp_set_stream_roc/srtp_get_stream_roc
, 設置和獲取 stream 的 ROC,這兩個接口在最新的 2.3 版本加入。
重要的結構 srtp_policy_t
,用來初始化加解密參數,在 srtp_create
中使用這個結構。如下參數須要關注:
1. DTLS 協商後獲得的 MasterKey
和 MasterSalt
經過這個結構傳遞給 libsrtp,用於 session key 的生成。
2. window_size
,對應咱們以前描述的 srtp 防重放攻擊的窗口大小。
3. allow_repeat_tx
,是否容許重傳相同序號的包。
SRS 是一個新生代實時通訊服務器,對 libsrtp 感興趣的同窗,能夠快速在本機搭起調試環境,進行相關測試,更加深刻理解相關的算法。
本文經過對 SRTP/SRTCP
相關原理的深刻詳細解讀,對 libsrtp 使用遇到的問題進行解答,但願可以給實時音視頻通訊的相關領域的同窗以幫助。
RFC6904: Encrypted SRTP Header Extensions
RFC-6188: The Use of AES-192 and AES-256 in Secure RTP
RFC2202: Test Cases for HMAC-MD5 and HMAC-SHA-1
「視頻雲技術」你最值得關注的音視頻技術公衆號,每週推送來自阿里雲一線的實踐技術文章,在這裏與音視頻領域一流工程師交流切磋。公衆號後臺回覆【技術】可加入阿里雲視頻雲技術交流羣,和做者一塊兒探討音視頻技術,獲取更多行業最新信息。
本文內容由阿里雲實名註冊用戶自發貢獻,版權歸原做者全部,阿里雲開發者社區不擁有其著做權,亦不承擔相應法律責任。具體規則請查看《阿里雲開發者社區用戶服務協議》和《阿里雲開發者社區知識產權保護指引》。若是您發現本社區中有涉嫌抄襲的內容,填寫侵權投訴表單進行舉報,一經查實,本社區將馬上刪除涉嫌侵權內容。