1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 // Original author: ekr@rtfm.com
6
7 #include "logging.h"
8 #include "SrtpFlow.h"
9
10 #include "srtp.h"
11
12 #include "transportlayerdtls.h"
13
14 #include "mozilla/RefPtr.h"
15
16 using namespace mozilla;
17
18 namespace mozilla {
19
20 MOZ_MTLOG_MODULE("mtransport")
21 bool SrtpFlow::initialized; // Static
22
~SrtpFlow()23 SrtpFlow::~SrtpFlow() {
24 if (session_) {
25 srtp_dealloc(session_);
26 }
27 }
28
KeySize(int cipher_suite)29 unsigned int SrtpFlow::KeySize(int cipher_suite) {
30 srtp_profile_t profile = static_cast<srtp_profile_t>(cipher_suite);
31 return srtp_profile_get_master_key_length(profile);
32 }
33
SaltSize(int cipher_suite)34 unsigned int SrtpFlow::SaltSize(int cipher_suite) {
35 srtp_profile_t profile = static_cast<srtp_profile_t>(cipher_suite);
36 return srtp_profile_get_master_salt_length(profile);
37 }
38
Create(int cipher_suite,bool inbound,const void * key,size_t key_len)39 RefPtr<SrtpFlow> SrtpFlow::Create(int cipher_suite, bool inbound,
40 const void* key, size_t key_len) {
41 nsresult res = Init();
42 if (!NS_SUCCEEDED(res)) return nullptr;
43
44 RefPtr<SrtpFlow> flow = new SrtpFlow();
45
46 if (!key) {
47 MOZ_MTLOG(ML_ERROR, "Null SRTP key specified");
48 return nullptr;
49 }
50
51 if ((key_len > SRTP_MAX_KEY_LENGTH) || (key_len < SRTP_MIN_KEY_LENGTH)) {
52 MOZ_ASSERT(false, "Invalid SRTP key length");
53 return nullptr;
54 }
55
56 srtp_policy_t policy;
57 memset(&policy, 0, sizeof(srtp_policy_t));
58
59 // Note that we set the same cipher suite for RTP and RTCP
60 // since any flow can only have one cipher suite with DTLS-SRTP
61 switch (cipher_suite) {
62 case kDtlsSrtpAeadAes256Gcm:
63 MOZ_MTLOG(ML_DEBUG, "Setting SRTP cipher suite SRTP_AEAD_AES_256_GCM");
64 srtp_crypto_policy_set_aes_gcm_256_16_auth(&policy.rtp);
65 srtp_crypto_policy_set_aes_gcm_256_16_auth(&policy.rtcp);
66 break;
67 case kDtlsSrtpAeadAes128Gcm:
68 MOZ_MTLOG(ML_DEBUG, "Setting SRTP cipher suite SRTP_AEAD_AES_128_GCM");
69 srtp_crypto_policy_set_aes_gcm_128_16_auth(&policy.rtp);
70 srtp_crypto_policy_set_aes_gcm_128_16_auth(&policy.rtcp);
71 break;
72 case kDtlsSrtpAes128CmHmacSha1_80:
73 MOZ_MTLOG(ML_DEBUG,
74 "Setting SRTP cipher suite SRTP_AES128_CM_HMAC_SHA1_80");
75 srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtp);
76 srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp);
77 break;
78 case kDtlsSrtpAes128CmHmacSha1_32:
79 MOZ_MTLOG(ML_DEBUG,
80 "Setting SRTP cipher suite SRTP_AES128_CM_HMAC_SHA1_32");
81 srtp_crypto_policy_set_aes_cm_128_hmac_sha1_32(&policy.rtp);
82 srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(
83 &policy.rtcp); // 80-bit per RFC 5764
84 break; // S 4.1.2.
85 default:
86 MOZ_MTLOG(ML_ERROR, "Request to set unknown SRTP cipher suite");
87 return nullptr;
88 }
89 // This key is copied into the srtp_t object, so we don't
90 // need to keep it.
91 policy.key =
92 const_cast<unsigned char*>(static_cast<const unsigned char*>(key));
93 policy.ssrc.type = inbound ? ssrc_any_inbound : ssrc_any_outbound;
94 policy.ssrc.value = 0;
95 policy.ekt = nullptr;
96 policy.window_size =
97 1024; // Use the Chrome value. Needs to be revisited. Default is 128
98 policy.allow_repeat_tx = 1; // Use Chrome value; needed for NACK mode to work
99 policy.next = nullptr;
100
101 // Now make the session
102 srtp_err_status_t r = srtp_create(&flow->session_, &policy);
103 if (r != srtp_err_status_ok) {
104 MOZ_MTLOG(ML_ERROR, "Error creating srtp session");
105 return nullptr;
106 }
107
108 return flow;
109 }
110
CheckInputs(bool protect,void * in,int in_len,int max_len,int * out_len)111 nsresult SrtpFlow::CheckInputs(bool protect, void* in, int in_len, int max_len,
112 int* out_len) {
113 MOZ_ASSERT(in);
114 if (!in) {
115 MOZ_MTLOG(ML_ERROR, "NULL input value");
116 return NS_ERROR_NULL_POINTER;
117 }
118
119 if (in_len < 0) {
120 MOZ_MTLOG(ML_ERROR, "Input length is negative");
121 return NS_ERROR_ILLEGAL_VALUE;
122 }
123
124 if (max_len < 0) {
125 MOZ_MTLOG(ML_ERROR, "Max output length is negative");
126 return NS_ERROR_ILLEGAL_VALUE;
127 }
128
129 if (protect) {
130 if ((max_len < SRTP_MAX_EXPANSION) ||
131 ((max_len - SRTP_MAX_EXPANSION) < in_len)) {
132 MOZ_MTLOG(ML_ERROR, "Output too short");
133 return NS_ERROR_ILLEGAL_VALUE;
134 }
135 } else {
136 if (in_len > max_len) {
137 MOZ_MTLOG(ML_ERROR, "Output too short");
138 return NS_ERROR_ILLEGAL_VALUE;
139 }
140 }
141
142 return NS_OK;
143 }
144
ProtectRtp(void * in,int in_len,int max_len,int * out_len)145 nsresult SrtpFlow::ProtectRtp(void* in, int in_len, int max_len, int* out_len) {
146 nsresult res = CheckInputs(true, in, in_len, max_len, out_len);
147 if (NS_FAILED(res)) return res;
148
149 int len = in_len;
150 srtp_err_status_t r = srtp_protect(session_, in, &len);
151
152 if (r != srtp_err_status_ok) {
153 MOZ_MTLOG(ML_ERROR, "Error protecting SRTP packet");
154 return NS_ERROR_FAILURE;
155 }
156
157 MOZ_ASSERT(len <= max_len);
158 *out_len = len;
159
160 MOZ_MTLOG(ML_DEBUG,
161 "Successfully protected an SRTP packet of len " << *out_len);
162
163 return NS_OK;
164 }
165
UnprotectRtp(void * in,int in_len,int max_len,int * out_len)166 nsresult SrtpFlow::UnprotectRtp(void* in, int in_len, int max_len,
167 int* out_len) {
168 nsresult res = CheckInputs(false, in, in_len, max_len, out_len);
169 if (NS_FAILED(res)) return res;
170
171 int len = in_len;
172 srtp_err_status_t r = srtp_unprotect(session_, in, &len);
173
174 if (r != srtp_err_status_ok) {
175 MOZ_MTLOG(ML_ERROR, "Error unprotecting SRTP packet error=" << (int)r);
176 return NS_ERROR_FAILURE;
177 }
178
179 MOZ_ASSERT(len <= max_len);
180 *out_len = len;
181
182 MOZ_MTLOG(ML_DEBUG,
183 "Successfully unprotected an SRTP packet of len " << *out_len);
184
185 return NS_OK;
186 }
187
ProtectRtcp(void * in,int in_len,int max_len,int * out_len)188 nsresult SrtpFlow::ProtectRtcp(void* in, int in_len, int max_len,
189 int* out_len) {
190 nsresult res = CheckInputs(true, in, in_len, max_len, out_len);
191 if (NS_FAILED(res)) return res;
192
193 int len = in_len;
194 srtp_err_status_t r = srtp_protect_rtcp(session_, in, &len);
195
196 if (r != srtp_err_status_ok) {
197 MOZ_MTLOG(ML_ERROR, "Error protecting SRTCP packet");
198 return NS_ERROR_FAILURE;
199 }
200
201 MOZ_ASSERT(len <= max_len);
202 *out_len = len;
203
204 MOZ_MTLOG(ML_DEBUG,
205 "Successfully protected an SRTCP packet of len " << *out_len);
206
207 return NS_OK;
208 }
209
UnprotectRtcp(void * in,int in_len,int max_len,int * out_len)210 nsresult SrtpFlow::UnprotectRtcp(void* in, int in_len, int max_len,
211 int* out_len) {
212 nsresult res = CheckInputs(false, in, in_len, max_len, out_len);
213 if (NS_FAILED(res)) return res;
214
215 int len = in_len;
216 srtp_err_status_t r = srtp_unprotect_rtcp(session_, in, &len);
217
218 if (r != srtp_err_status_ok) {
219 MOZ_MTLOG(ML_ERROR, "Error unprotecting SRTCP packet error=" << (int)r);
220 return NS_ERROR_FAILURE;
221 }
222
223 MOZ_ASSERT(len <= max_len);
224 *out_len = len;
225
226 MOZ_MTLOG(ML_DEBUG,
227 "Successfully unprotected an SRTCP packet of len " << *out_len);
228
229 return NS_OK;
230 }
231
232 // Statics
srtp_event_handler(srtp_event_data_t * data)233 void SrtpFlow::srtp_event_handler(srtp_event_data_t* data) {
234 // TODO(ekr@rtfm.com): Implement this
235 MOZ_CRASH();
236 }
237
Init()238 nsresult SrtpFlow::Init() {
239 if (!initialized) {
240 srtp_err_status_t r = srtp_init();
241 if (r != srtp_err_status_ok) {
242 MOZ_MTLOG(ML_ERROR, "Could not initialize SRTP");
243 MOZ_ASSERT(PR_FALSE);
244 return NS_ERROR_FAILURE;
245 }
246
247 r = srtp_install_event_handler(&SrtpFlow::srtp_event_handler);
248 if (r != srtp_err_status_ok) {
249 MOZ_MTLOG(ML_ERROR, "Could not install SRTP event handler");
250 MOZ_ASSERT(PR_FALSE);
251 return NS_ERROR_FAILURE;
252 }
253
254 initialized = true;
255 }
256
257 return NS_OK;
258 }
259
260 } // namespace mozilla
261