注:以下為rfc5764的學習筆記,不保證完全正確。
DTLS-SRTP是DTLS的一個擴展,將SRTP加解密與DTLS的key交換和會話管理相結合。從SRTP的角度看,是為其提供一種新的key協商管理的方法;從DTLS的角度看,是為應用數據提供一個新的數據格式(SRTP/SRTCP)。
1,應用層數據加解密是由SRTP完成的,要求必須是RTP/RTCP的格式。
2,DTLS的握手過程是為SRTP加解密過程協商使用哪種profile和密鑰。
3,除了應用數據加密為SRTP格式,其他record-layer的報文仍為普通的DTLS格式(比如TLS control message)
4,當發送SRTP格式的應用層數據時,需要直接跳過DTLS加密層,將SRTP數據包透傳到下層的數據傳輸層做發送。
由於密鑰和加密參數是在DTLS握手過程中協商得到的,而此過程是保密的,因而相比常規的方式(比如在通過SDP消息交互來協商)更為安全。在發起DTLS握手之前,需要先設置use-srtp擴展。
接收端使用 DTLS-SRTP
自DTLS下層的傳輸層收到報文之后,需要根據包頭特征手動區分做demultiplexing,一般可以如下進行
檢查第一個報文的第一個字節
1,是[0, 1]時,表示可能是STUN報文
2,是[128, 191]時,表示可能時RTP(SRTP)報文
3,是[20, 63]時,表示可能是DTLS record layer報文
其他的類別請根據實際情況做區分處理
DTLS握手成功之后,需要導出key material,然后從中分離出server/client端用於SRTP對稱加密的密鑰。key material中對應SRTP的密鑰,構成如下
client_master_key server_master_key client_salt server_salt
其中 master_key 和 salt 的長度是根據最終協商的 SRTP 的profile來確定的,具體可查 RFC3711 SRTP
key material導出的規范見 RFC5705 Keying Material Exporters for TLS
webrtc中對此代碼實現見,webrtc/pc/channel.cc中 BaseChannel::SetupDtlsSrtp_n() 函數
Code Sample using DTLS-SRTP
1 #define SRTP_MASTER_KEY_KEY_LEN 16 2 #define SRTP_MASTER_KEY_SALT_LEN 14 3 static void dtls_srtp_init( struct transport_dtls *dtls ) 4 { 5 6 /* 7 When SRTP mode is in effect, different keys are used for ordinary 8 DTLS record protection and SRTP packet protection. These keys are 9 generated using a TLS exporter [RFC5705] to generate 10 11 2 * (SRTPSecurityParams.master_key_len + 12 SRTPSecurityParams.master_salt_len) bytes of data 13 14 which are assigned as shown below. The per-association context value 15 is empty. 16 17 client_write_SRTP_master_key[SRTPSecurityParams.master_key_len]; 18 server_write_SRTP_master_key[SRTPSecurityParams.master_key_len]; 19 client_write_SRTP_master_salt[SRTPSecurityParams.master_salt_len]; 20 server_write_SRTP_master_salt[SRTPSecurityParams.master_salt_len]; 21 */ 22 int code; 23 err_status_t err; 24 srtp_policy_t policy; 25 char dtls_buffer[SRTP_MASTER_KEY_KEY_LEN * 2 + SRTP_MASTER_KEY_SALT_LEN * 2]; 26 char client_write_key[SRTP_MASTER_KEY_KEY_LEN + SRTP_MASTER_KEY_SALT_LEN]; 27 char server_write_key[SRTP_MASTER_KEY_KEY_LEN + SRTP_MASTER_KEY_SALT_LEN]; 28 size_t offset = 0; 29 30 /* 31 The exporter label for this usage is "EXTRACTOR-dtls_srtp". (The 32 "EXTRACTOR" prefix is for historical compatibility.) 33 RFC 5764 4.2. Key Derivation 34 */ 35 const char * label = "EXTRACTOR-dtls_srtp"; 36 37 SRTP_PROTECTION_PROFILE * srtp_profile= SSL_get_selected_srtp_profile( dtls->ssl ); 38 39 /* SSL_export_keying_material exports a value derived from the master secret, 40 * as specified in RFC 5705. It writes |olen| bytes to |out| given a label and 41 * optional context. (Since a zero length context is allowed, the |use_context| 42 * flag controls whether a context is included.) 43 * 44 * It returns 1 on success and zero otherwise. 45 */ 46 code = SSL_export_keying_material(dtls->ssl, 47 dtls_buffer, 48 sizeof(dtls_buffer), 49 label, 50 strlen( label), 51 NULL, 52 0, 53 PJ_FALSE); 54 55 memcpy(&client_write_key[0], &dtls_buffer[offset], SRTP_MASTER_KEY_KEY_LEN); 56 offset += SRTP_MASTER_KEY_KEY_LEN; 57 memcpy(&server_write_key[0], &dtls_buffer[offset], SRTP_MASTER_KEY_KEY_LEN); 58 offset += SRTP_MASTER_KEY_KEY_LEN; 59 memcpy(&client_write_key[SRTP_MASTER_KEY_KEY_LEN], &dtls_buffer[offset], SRTP_MASTER_KEY_SALT_LEN); 60 offset += SRTP_MASTER_KEY_SALT_LEN; 61 memcpy(&server_write_key[SRTP_MASTER_KEY_KEY_LEN], &dtls_buffer[offset], SRTP_MASTER_KEY_SALT_LEN); 62 63 switch( srtp_profile->id ) 64 { 65 case SRTP_AES128_CM_SHA1_80: 66 crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtp); 67 crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp); 68 break; 69 case SRTP_AES128_CM_SHA1_32: 70 crypto_policy_set_aes_cm_128_hmac_sha1_32(&policy.rtp); // rtp is 32, 71 crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp); // rtcp still 80 72 break; 73 default: 74 assert(0); 75 } 76 policy.ssrc.value = 0; 77 policy.next = NULL; 78 79 /* Init transmit direction */ 80 policy.ssrc.type = ssrc_any_outbound; 81 policy.key = client_write_key; 82 83 err = srtp_create(&dtls->srtp_ctx_rx, &policy); 84 if (err != err_status_ok) { 85 printf("not working\n"); 86 } 87 88 /* Init receive direction */ 89 policy.ssrc.type = ssrc_any_inbound; 90 policy.key = server_write_key; 91 92 err = srtp_create(&dtls->srtp_ctx_tx, &policy); 93 if (err != err_status_ok) { 94 printf("not working\n"); 95 } 96 97 }