Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(850)

Side by Side Diff: webrtc/pc/srtpfilter.cc

Issue 2997983002: Completed the functionalities of SrtpTransport. (Closed)
Patch Set: Added unit tests for SrtpTransport. Created 3 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright 2009 The WebRTC project authors. All Rights Reserved. 2 * Copyright 2009 The WebRTC project authors. All Rights Reserved.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license 4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source 5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found 6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may 7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree. 8 * be found in the AUTHORS file in the root of the source tree.
9 */ 9 */
10 10
11 #include "webrtc/pc/srtpfilter.h" 11 #include "webrtc/pc/srtpfilter.h"
12 12
13 #include <string.h> 13 #include <string.h>
14 14
15 #include <algorithm> 15 #include <algorithm>
16 16
17 #include "webrtc/media/base/rtputils.h" 17 #include "webrtc/media/base/rtputils.h"
18 #include "webrtc/pc/srtpsession.h" 18 #include "webrtc/pc/srtpsession.h"
19 #include "webrtc/pc/srtptransport.h"
19 #include "webrtc/rtc_base/base64.h" 20 #include "webrtc/rtc_base/base64.h"
20 #include "webrtc/rtc_base/buffer.h"
21 #include "webrtc/rtc_base/byteorder.h" 21 #include "webrtc/rtc_base/byteorder.h"
22 #include "webrtc/rtc_base/checks.h" 22 #include "webrtc/rtc_base/checks.h"
23 #include "webrtc/rtc_base/logging.h" 23 #include "webrtc/rtc_base/logging.h"
24 #include "webrtc/rtc_base/stringencode.h" 24 #include "webrtc/rtc_base/stringencode.h"
25 #include "webrtc/rtc_base/timeutils.h" 25 #include "webrtc/rtc_base/timeutils.h"
26 26
27 namespace cricket { 27 namespace cricket {
28 28
29 // NOTE: This is called from ChannelManager D'tor. 29 // NOTE: This is called from ChannelManager D'tor.
30 void ShutdownSrtp() { 30 void ShutdownSrtp() {
(...skipping 25 matching lines...) Expand all
56 ContentSource source) { 56 ContentSource source) {
57 return DoSetAnswer(answer_params, source, true); 57 return DoSetAnswer(answer_params, source, true);
58 } 58 }
59 59
60 bool SrtpFilter::SetProvisionalAnswer( 60 bool SrtpFilter::SetProvisionalAnswer(
61 const std::vector<CryptoParams>& answer_params, 61 const std::vector<CryptoParams>& answer_params,
62 ContentSource source) { 62 ContentSource source) {
63 return DoSetAnswer(answer_params, source, false); 63 return DoSetAnswer(answer_params, source, false);
64 } 64 }
65 65
66 bool SrtpFilter::SetRtpParams(int send_cs, 66 void SrtpFilter::EnableDtlsSrtp() {
67 const uint8_t* send_key,
68 int send_key_len,
69 int recv_cs,
70 const uint8_t* recv_key,
71 int recv_key_len) {
72 if (IsActive()) {
73 LOG(LS_ERROR) << "Tried to set SRTP Params when filter already active";
74 return false;
75 }
76 CreateSrtpSessions();
77 send_session_->SetEncryptedHeaderExtensionIds(
78 send_encrypted_header_extension_ids_);
79 if (!send_session_->SetSend(send_cs, send_key, send_key_len)) {
80 return false;
81 }
82
83 recv_session_->SetEncryptedHeaderExtensionIds(
84 recv_encrypted_header_extension_ids_);
85 if (!recv_session_->SetRecv(recv_cs, recv_key, recv_key_len)) {
86 return false;
87 }
88
89 state_ = ST_ACTIVE; 67 state_ = ST_ACTIVE;
90 68 LOG(LS_INFO) << "DTLS-SRTP is enabled. SRTP is activated.";
91 LOG(LS_INFO) << "SRTP activated with negotiated parameters:"
92 << " send cipher_suite " << send_cs
93 << " recv cipher_suite " << recv_cs;
94 return true;
95 }
96
97 bool SrtpFilter::UpdateRtpParams(int send_cs,
98 const uint8_t* send_key,
99 int send_key_len,
100 int recv_cs,
101 const uint8_t* recv_key,
102 int recv_key_len) {
103 if (!IsActive()) {
104 LOG(LS_ERROR) << "Tried to update SRTP Params when filter is not active";
105 return false;
106 }
107 send_session_->SetEncryptedHeaderExtensionIds(
108 send_encrypted_header_extension_ids_);
109 if (!send_session_->UpdateSend(send_cs, send_key, send_key_len)) {
110 return false;
111 }
112
113 recv_session_->SetEncryptedHeaderExtensionIds(
114 recv_encrypted_header_extension_ids_);
115 if (!recv_session_->UpdateRecv(recv_cs, recv_key, recv_key_len)) {
116 return false;
117 }
118
119 LOG(LS_INFO) << "SRTP updated with negotiated parameters:"
120 << " send cipher_suite " << send_cs
121 << " recv cipher_suite " << recv_cs;
122 return true;
123 }
124
125 // This function is provided separately because DTLS-SRTP behaves
126 // differently in RTP/RTCP mux and non-mux modes.
127 //
128 // - In the non-muxed case, RTP and RTCP are keyed with different
129 // keys (from different DTLS handshakes), and so we need a new
130 // SrtpSession.
131 // - In the muxed case, they are keyed with the same keys, so
132 // this function is not needed
133 bool SrtpFilter::SetRtcpParams(int send_cs,
134 const uint8_t* send_key,
135 int send_key_len,
136 int recv_cs,
137 const uint8_t* recv_key,
138 int recv_key_len) {
139 // This can only be called once, but can be safely called after
140 // SetRtpParams
141 if (send_rtcp_session_ || recv_rtcp_session_) {
142 LOG(LS_ERROR) << "Tried to set SRTCP Params when filter already active";
143 return false;
144 }
145
146 send_rtcp_session_.reset(new SrtpSession());
147 if (!send_rtcp_session_->SetRecv(send_cs, send_key, send_key_len)) {
148 return false;
149 }
150
151 recv_rtcp_session_.reset(new SrtpSession());
152 if (!recv_rtcp_session_->SetRecv(recv_cs, recv_key, recv_key_len)) {
153 return false;
154 }
155
156 LOG(LS_INFO) << "SRTCP activated with negotiated parameters:"
157 << " send cipher_suite " << send_cs
158 << " recv cipher_suite " << recv_cs;
159
160 return true;
161 }
162
163 bool SrtpFilter::ProtectRtp(void* p, int in_len, int max_len, int* out_len) {
164 if (!IsActive()) {
165 LOG(LS_WARNING) << "Failed to ProtectRtp: SRTP not active";
166 return false;
167 }
168 RTC_CHECK(send_session_);
169 return send_session_->ProtectRtp(p, in_len, max_len, out_len);
170 }
171
172 bool SrtpFilter::ProtectRtp(void* p,
173 int in_len,
174 int max_len,
175 int* out_len,
176 int64_t* index) {
177 if (!IsActive()) {
178 LOG(LS_WARNING) << "Failed to ProtectRtp: SRTP not active";
179 return false;
180 }
181 RTC_CHECK(send_session_);
182 return send_session_->ProtectRtp(p, in_len, max_len, out_len, index);
183 }
184
185 bool SrtpFilter::ProtectRtcp(void* p, int in_len, int max_len, int* out_len) {
186 if (!IsActive()) {
187 LOG(LS_WARNING) << "Failed to ProtectRtcp: SRTP not active";
188 return false;
189 }
190 if (send_rtcp_session_) {
191 return send_rtcp_session_->ProtectRtcp(p, in_len, max_len, out_len);
192 } else {
193 RTC_CHECK(send_session_);
194 return send_session_->ProtectRtcp(p, in_len, max_len, out_len);
195 }
196 }
197
198 bool SrtpFilter::UnprotectRtp(void* p, int in_len, int* out_len) {
199 if (!IsActive()) {
200 LOG(LS_WARNING) << "Failed to UnprotectRtp: SRTP not active";
201 return false;
202 }
203 RTC_CHECK(recv_session_);
204 return recv_session_->UnprotectRtp(p, in_len, out_len);
205 }
206
207 bool SrtpFilter::UnprotectRtcp(void* p, int in_len, int* out_len) {
208 if (!IsActive()) {
209 LOG(LS_WARNING) << "Failed to UnprotectRtcp: SRTP not active";
210 return false;
211 }
212 if (recv_rtcp_session_) {
213 return recv_rtcp_session_->UnprotectRtcp(p, in_len, out_len);
214 } else {
215 RTC_CHECK(recv_session_);
216 return recv_session_->UnprotectRtcp(p, in_len, out_len);
217 }
218 }
219
220 bool SrtpFilter::GetRtpAuthParams(uint8_t** key, int* key_len, int* tag_len) {
221 if (!IsActive()) {
222 LOG(LS_WARNING) << "Failed to GetRtpAuthParams: SRTP not active";
223 return false;
224 }
225
226 RTC_CHECK(send_session_);
227 return send_session_->GetRtpAuthParams(key, key_len, tag_len);
228 }
229
230 bool SrtpFilter::GetSrtpOverhead(int* srtp_overhead) const {
231 if (!IsActive()) {
232 LOG(LS_WARNING) << "Failed to GetSrtpOverhead: SRTP not active";
233 return false;
234 }
235
236 RTC_CHECK(send_session_);
237 *srtp_overhead = send_session_->GetSrtpOverhead();
238 return true;
239 }
240
241 void SrtpFilter::EnableExternalAuth() {
242 RTC_DCHECK(!IsActive());
243 external_auth_enabled_ = true;
244 }
245
246 bool SrtpFilter::IsExternalAuthEnabled() const {
247 return external_auth_enabled_;
248 }
249
250 bool SrtpFilter::IsExternalAuthActive() const {
251 if (!IsActive()) {
252 LOG(LS_WARNING) << "Failed to check IsExternalAuthActive: SRTP not active";
253 return false;
254 }
255
256 RTC_CHECK(send_session_);
257 return send_session_->IsExternalAuthActive();
258 }
259
260 void SrtpFilter::SetEncryptedHeaderExtensionIds(ContentSource source,
261 const std::vector<int>& extension_ids) {
262 if (source == CS_LOCAL) {
263 recv_encrypted_header_extension_ids_ = extension_ids;
264 } else {
265 send_encrypted_header_extension_ids_ = extension_ids;
266 }
267 } 69 }
268 70
269 bool SrtpFilter::ExpectOffer(ContentSource source) { 71 bool SrtpFilter::ExpectOffer(ContentSource source) {
270 return ((state_ == ST_INIT) || 72 return ((state_ == ST_INIT) ||
271 (state_ == ST_ACTIVE) || 73 (state_ == ST_ACTIVE) ||
272 (state_ == ST_SENTOFFER && source == CS_LOCAL) || 74 (state_ == ST_SENTOFFER && source == CS_LOCAL) ||
273 (state_ == ST_SENTUPDATEDOFFER && source == CS_LOCAL) || 75 (state_ == ST_SENTUPDATEDOFFER && source == CS_LOCAL) ||
274 (state_ == ST_RECEIVEDOFFER && source == CS_REMOTE) || 76 (state_ == ST_RECEIVEDOFFER && source == CS_REMOTE) ||
275 (state_ == ST_RECEIVEDUPDATEDOFFER && source == CS_REMOTE)); 77 (state_ == ST_RECEIVEDUPDATEDOFFER && source == CS_REMOTE));
276 } 78 }
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
316 // Need to wait for the final answer to decide if 118 // Need to wait for the final answer to decide if
317 // we should go to Active state. 119 // we should go to Active state.
318 state_ = (source == CS_LOCAL) ? ST_SENTPRANSWER_NO_CRYPTO : 120 state_ = (source == CS_LOCAL) ? ST_SENTPRANSWER_NO_CRYPTO :
319 ST_RECEIVEDPRANSWER_NO_CRYPTO; 121 ST_RECEIVEDPRANSWER_NO_CRYPTO;
320 return true; 122 return true;
321 } 123 }
322 } 124 }
323 CryptoParams selected_params; 125 CryptoParams selected_params;
324 if (!NegotiateParams(answer_params, &selected_params)) 126 if (!NegotiateParams(answer_params, &selected_params))
325 return false; 127 return false;
326 const CryptoParams& send_params = 128
129 CryptoParams new_send_params =
327 (source == CS_REMOTE) ? selected_params : answer_params[0]; 130 (source == CS_REMOTE) ? selected_params : answer_params[0];
328 const CryptoParams& recv_params = 131 CryptoParams new_recv_params =
329 (source == CS_REMOTE) ? answer_params[0] : selected_params; 132 (source == CS_REMOTE) ? answer_params[0] : selected_params;
330 if (!ApplyParams(send_params, recv_params)) { 133 if (!ParseSendParams(new_send_params) || !ParseRecvParams(new_recv_params)) {
331 return false; 134 return false;
332 } 135 }
136 send_params_ = new_send_params;
137 recv_params_ = new_recv_params;
333 138
334 if (final) { 139 if (final) {
335 offer_params_.clear(); 140 offer_params_.clear();
336 state_ = ST_ACTIVE; 141 state_ = ST_ACTIVE;
337 } else { 142 } else {
338 state_ = 143 state_ =
339 (source == CS_LOCAL) ? ST_SENTPRANSWER : ST_RECEIVEDPRANSWER; 144 (source == CS_LOCAL) ? ST_SENTPRANSWER : ST_RECEIVEDPRANSWER;
340 } 145 }
341 return true; 146 return true;
342 } 147 }
343 148
344 void SrtpFilter::CreateSrtpSessions() {
345 send_session_.reset(new SrtpSession());
346 applied_send_params_ = CryptoParams();
347 recv_session_.reset(new SrtpSession());
348 applied_recv_params_ = CryptoParams();
349
350 if (external_auth_enabled_) {
351 send_session_->EnableExternalAuth();
352 }
353 }
354
355 bool SrtpFilter::NegotiateParams(const std::vector<CryptoParams>& answer_params, 149 bool SrtpFilter::NegotiateParams(const std::vector<CryptoParams>& answer_params,
356 CryptoParams* selected_params) { 150 CryptoParams* selected_params) {
357 // We're processing an accept. We should have exactly one set of params, 151 // We're processing an accept. We should have exactly one set of params,
358 // unless the offer didn't mention crypto, in which case we shouldn't be here. 152 // unless the offer didn't mention crypto, in which case we shouldn't be here.
359 bool ret = (answer_params.size() == 1U && !offer_params_.empty()); 153 bool ret = (answer_params.size() == 1U && !offer_params_.empty());
360 if (ret) { 154 if (ret) {
361 // We should find a match between the answer params and the offered params. 155 // We should find a match between the answer params and the offered params.
362 std::vector<CryptoParams>::const_iterator it; 156 std::vector<CryptoParams>::const_iterator it;
363 for (it = offer_params_.begin(); it != offer_params_.end(); ++it) { 157 for (it = offer_params_.begin(); it != offer_params_.end(); ++it) {
364 if (answer_params[0].Matches(*it)) { 158 if (answer_params[0].Matches(*it)) {
365 break; 159 break;
366 } 160 }
367 } 161 }
368 162
369 if (it != offer_params_.end()) { 163 if (it != offer_params_.end()) {
370 *selected_params = *it; 164 *selected_params = *it;
371 } else { 165 } else {
372 ret = false; 166 ret = false;
373 } 167 }
374 } 168 }
375 169
376 if (!ret) { 170 if (!ret) {
377 LOG(LS_WARNING) << "Invalid parameters in SRTP answer"; 171 LOG(LS_WARNING) << "Invalid parameters in SRTP answer";
378 } 172 }
379 return ret; 173 return ret;
380 } 174 }
381 175
382 bool SrtpFilter::ApplyParams(const CryptoParams& send_params, 176 bool SrtpFilter::ResetParams() {
383 const CryptoParams& recv_params) { 177 offer_params_.clear();
384 // TODO(jiayl): Split this method to apply send and receive CryptoParams 178 send_params_ = CryptoParams();
385 // independently, so that we can skip one method when either send or receive 179 recv_params_ = CryptoParams();
386 // CryptoParams is unchanged. 180 send_cipher_suite_ = rtc::Optional<int>();
387 if (applied_send_params_.cipher_suite == send_params.cipher_suite && 181 recv_cipher_suite_ = rtc::Optional<int>();
388 applied_send_params_.key_params == send_params.key_params && 182 send_key_.clear();
389 applied_recv_params_.cipher_suite == recv_params.cipher_suite && 183 recv_key_.clear();
390 applied_recv_params_.key_params == recv_params.key_params) { 184 state_ = ST_INIT;
391 LOG(LS_INFO) << "Applying the same SRTP parameters again. No-op."; 185 return true;
186 }
187
188 bool SrtpFilter::ParseSendParams(const CryptoParams& send_params) {
189 if (send_params_.cipher_suite == send_params.cipher_suite &&
190 send_params_.key_params == send_params.key_params) {
191 LOG(LS_INFO) << "Applying the same SRTP send parameters again. No-op.";
392 192
393 // We do not want to reset the ROC if the keys are the same. So just return. 193 // We do not want to reset the ROC if the keys are the same. So just return.
394 return true; 194 return true;
395 } 195 }
396 196
397 int send_suite = rtc::SrtpCryptoSuiteFromName(send_params.cipher_suite); 197 send_cipher_suite_ = rtc::Optional<int>(
398 int recv_suite = rtc::SrtpCryptoSuiteFromName(recv_params.cipher_suite); 198 rtc::SrtpCryptoSuiteFromName(send_params.cipher_suite));
399 if (send_suite == rtc::SRTP_INVALID_CRYPTO_SUITE || 199 if (send_cipher_suite_ == rtc::SRTP_INVALID_CRYPTO_SUITE) {
400 recv_suite == rtc::SRTP_INVALID_CRYPTO_SUITE) {
401 LOG(LS_WARNING) << "Unknown crypto suite(s) received:" 200 LOG(LS_WARNING) << "Unknown crypto suite(s) received:"
402 << " send cipher_suite " << send_params.cipher_suite 201 << " send cipher_suite " << send_params.cipher_suite;
202 return false;
203 }
204
205 int send_key_len, send_salt_len;
206 if (!rtc::GetSrtpKeyAndSaltLengths(*send_cipher_suite_, &send_key_len,
207 &send_salt_len)) {
208 LOG(LS_WARNING) << "Could not get lengths for crypto suite(s):"
209 << " send cipher_suite " << send_params.cipher_suite;
210 return false;
211 }
212
213 return ParseKeyParams(send_params.key_params, &send_key_,
214 send_key_len + send_salt_len);
215 }
216
217 bool SrtpFilter::ParseRecvParams(const CryptoParams& recv_params) {
218 if (recv_params_.cipher_suite == recv_params.cipher_suite &&
219 recv_params_.key_params == recv_params.key_params) {
220 LOG(LS_INFO) << "Applying the same SRTP recv parameters again. No-op.";
221
222 // We do not want to reset the ROC if the keys are the same. So just return.
223 return true;
224 }
225
226 recv_cipher_suite_ = rtc::Optional<int>(
227 rtc::SrtpCryptoSuiteFromName(recv_params.cipher_suite));
228 if (recv_cipher_suite_ == rtc::SRTP_INVALID_CRYPTO_SUITE) {
229 LOG(LS_WARNING) << "Unknown crypto suite(s) received:"
403 << " recv cipher_suite " << recv_params.cipher_suite; 230 << " recv cipher_suite " << recv_params.cipher_suite;
404 return false; 231 return false;
405 } 232 }
406 233
407 int send_key_len, send_salt_len;
408 int recv_key_len, recv_salt_len; 234 int recv_key_len, recv_salt_len;
409 if (!rtc::GetSrtpKeyAndSaltLengths(send_suite, &send_key_len, 235 if (!rtc::GetSrtpKeyAndSaltLengths(*recv_cipher_suite_, &recv_key_len,
410 &send_salt_len) ||
411 !rtc::GetSrtpKeyAndSaltLengths(recv_suite, &recv_key_len,
412 &recv_salt_len)) { 236 &recv_salt_len)) {
413 LOG(LS_WARNING) << "Could not get lengths for crypto suite(s):" 237 LOG(LS_WARNING) << "Could not get lengths for crypto suite(s):"
414 << " send cipher_suite " << send_params.cipher_suite
415 << " recv cipher_suite " << recv_params.cipher_suite; 238 << " recv cipher_suite " << recv_params.cipher_suite;
416 return false; 239 return false;
417 } 240 }
418 241
419 // TODO(juberti): Zero these buffers after use. 242 return ParseKeyParams(recv_params.key_params, &recv_key_,
420 bool ret; 243 recv_key_len + recv_salt_len);
421 rtc::Buffer send_key(send_key_len + send_salt_len);
422 rtc::Buffer recv_key(recv_key_len + recv_salt_len);
423 ret = (ParseKeyParams(send_params.key_params, send_key.data(),
424 send_key.size()) &&
425 ParseKeyParams(recv_params.key_params, recv_key.data(),
426 recv_key.size()));
427 if (ret) {
428 CreateSrtpSessions();
429 send_session_->SetEncryptedHeaderExtensionIds(
430 send_encrypted_header_extension_ids_);
431 recv_session_->SetEncryptedHeaderExtensionIds(
432 recv_encrypted_header_extension_ids_);
433 ret = (send_session_->SetSend(
434 rtc::SrtpCryptoSuiteFromName(send_params.cipher_suite),
435 send_key.data(), send_key.size()) &&
436 recv_session_->SetRecv(
437 rtc::SrtpCryptoSuiteFromName(recv_params.cipher_suite),
438 recv_key.data(), recv_key.size()));
439 }
440 if (ret) {
441 LOG(LS_INFO) << "SRTP activated with negotiated parameters:"
442 << " send cipher_suite " << send_params.cipher_suite
443 << " recv cipher_suite " << recv_params.cipher_suite;
444 applied_send_params_ = send_params;
445 applied_recv_params_ = recv_params;
446 } else {
447 LOG(LS_WARNING) << "Failed to apply negotiated SRTP parameters";
448 }
449 return ret;
450 }
451
452 bool SrtpFilter::ResetParams() {
453 offer_params_.clear();
454 state_ = ST_INIT;
455 send_session_ = nullptr;
456 recv_session_ = nullptr;
457 send_rtcp_session_ = nullptr;
458 recv_rtcp_session_ = nullptr;
459 LOG(LS_INFO) << "SRTP reset to init state";
460 return true;
461 } 244 }
462 245
463 bool SrtpFilter::ParseKeyParams(const std::string& key_params, 246 bool SrtpFilter::ParseKeyParams(const std::string& key_params,
464 uint8_t* key, 247 std::vector<unsigned char>* key,
465 size_t len) { 248 size_t len) {
466 // example key_params: "inline:YUJDZGVmZ2hpSktMbW9QUXJzVHVWd3l6MTIzNDU2" 249 // example key_params: "inline:YUJDZGVmZ2hpSktMbW9QUXJzVHVWd3l6MTIzNDU2"
467 250
468 // Fail if key-method is wrong. 251 // Fail if key-method is wrong.
469 if (key_params.find("inline:") != 0) { 252 if (key_params.find("inline:") != 0) {
470 return false; 253 return false;
471 } 254 }
472 255
473 // Fail if base64 decode fails, or the key is the wrong size. 256 // Fail if base64 decode fails, or the key is the wrong size.
474 std::string key_b64(key_params.substr(7)), key_str; 257 std::string key_b64(key_params.substr(7)), key_str;
475 if (!rtc::Base64::Decode(key_b64, rtc::Base64::DO_STRICT, 258 if (!rtc::Base64::Decode(key_b64, rtc::Base64::DO_STRICT, &key_str,
476 &key_str, nullptr) || key_str.size() != len) { 259 nullptr) ||
260 key_str.size() != len) {
477 return false; 261 return false;
478 } 262 }
479 263
480 memcpy(key, key_str.c_str(), len); 264 std::vector<unsigned char> temp_key(key_str.c_str(), key_str.c_str() + len);
265 *key = temp_key;
481 return true; 266 return true;
482 } 267 }
483 268
484 } // namespace cricket 269 } // namespace cricket
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698