webrtc/pc/srtpfilter.cc
Artem Titov a76af0ca2e Move base64.h to the proper location.
Move base64.h to the proper location and put redirect header into the
old place to be able to switch downstream users on new location.

Bug: webrtc:8366
Change-Id: I5191fe631d32178d2efd1315ca9abd4250102291
Reviewed-on: https://webrtc-review.googlesource.com/88223
Commit-Queue: Artem Titov <titovartem@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Reviewed-by: Henrik Andreassson <henrika@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#24069}
2018-07-23 15:40:36 +00:00

284 lines
9.3 KiB
C++

/*
* Copyright 2009 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "pc/srtpfilter.h"
#include <string.h>
#include <algorithm>
#include <utility>
#include "media/base/rtputils.h"
#include "pc/srtpsession.h"
#include "rtc_base/byteorder.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "rtc_base/stringencode.h"
#include "rtc_base/third_party/base64/base64.h"
#include "rtc_base/timeutils.h"
#include "rtc_base/zero_memory.h"
namespace cricket {
SrtpFilter::SrtpFilter() {}
SrtpFilter::~SrtpFilter() {}
bool SrtpFilter::IsActive() const {
return state_ >= ST_ACTIVE;
}
bool SrtpFilter::Process(const std::vector<CryptoParams>& cryptos,
webrtc::SdpType type,
ContentSource source) {
bool ret = false;
switch (type) {
case webrtc::SdpType::kOffer:
ret = SetOffer(cryptos, source);
break;
case webrtc::SdpType::kPrAnswer:
ret = SetProvisionalAnswer(cryptos, source);
break;
case webrtc::SdpType::kAnswer:
ret = SetAnswer(cryptos, source);
break;
default:
break;
}
if (!ret) {
return false;
}
return true;
}
bool SrtpFilter::SetOffer(const std::vector<CryptoParams>& offer_params,
ContentSource source) {
if (!ExpectOffer(source)) {
RTC_LOG(LS_ERROR) << "Wrong state to update SRTP offer";
return false;
}
return StoreParams(offer_params, source);
}
bool SrtpFilter::SetAnswer(const std::vector<CryptoParams>& answer_params,
ContentSource source) {
return DoSetAnswer(answer_params, source, true);
}
bool SrtpFilter::SetProvisionalAnswer(
const std::vector<CryptoParams>& answer_params,
ContentSource source) {
return DoSetAnswer(answer_params, source, false);
}
bool SrtpFilter::ExpectOffer(ContentSource source) {
return ((state_ == ST_INIT) || (state_ == ST_ACTIVE) ||
(state_ == ST_SENTOFFER && source == CS_LOCAL) ||
(state_ == ST_SENTUPDATEDOFFER && source == CS_LOCAL) ||
(state_ == ST_RECEIVEDOFFER && source == CS_REMOTE) ||
(state_ == ST_RECEIVEDUPDATEDOFFER && source == CS_REMOTE));
}
bool SrtpFilter::StoreParams(const std::vector<CryptoParams>& params,
ContentSource source) {
offer_params_ = params;
if (state_ == ST_INIT) {
state_ = (source == CS_LOCAL) ? ST_SENTOFFER : ST_RECEIVEDOFFER;
} else if (state_ == ST_ACTIVE) {
state_ =
(source == CS_LOCAL) ? ST_SENTUPDATEDOFFER : ST_RECEIVEDUPDATEDOFFER;
}
return true;
}
bool SrtpFilter::ExpectAnswer(ContentSource source) {
return ((state_ == ST_SENTOFFER && source == CS_REMOTE) ||
(state_ == ST_RECEIVEDOFFER && source == CS_LOCAL) ||
(state_ == ST_SENTUPDATEDOFFER && source == CS_REMOTE) ||
(state_ == ST_RECEIVEDUPDATEDOFFER && source == CS_LOCAL) ||
(state_ == ST_SENTPRANSWER_NO_CRYPTO && source == CS_LOCAL) ||
(state_ == ST_SENTPRANSWER && source == CS_LOCAL) ||
(state_ == ST_RECEIVEDPRANSWER_NO_CRYPTO && source == CS_REMOTE) ||
(state_ == ST_RECEIVEDPRANSWER && source == CS_REMOTE));
}
bool SrtpFilter::DoSetAnswer(const std::vector<CryptoParams>& answer_params,
ContentSource source,
bool final) {
if (!ExpectAnswer(source)) {
RTC_LOG(LS_ERROR) << "Invalid state for SRTP answer";
return false;
}
// If the answer doesn't requests crypto complete the negotiation of an
// unencrypted session.
// Otherwise, finalize the parameters and apply them.
if (answer_params.empty()) {
if (final) {
return ResetParams();
} else {
// Need to wait for the final answer to decide if
// we should go to Active state.
state_ = (source == CS_LOCAL) ? ST_SENTPRANSWER_NO_CRYPTO
: ST_RECEIVEDPRANSWER_NO_CRYPTO;
return true;
}
}
CryptoParams selected_params;
if (!NegotiateParams(answer_params, &selected_params))
return false;
const CryptoParams& new_send_params =
(source == CS_REMOTE) ? selected_params : answer_params[0];
const CryptoParams& new_recv_params =
(source == CS_REMOTE) ? answer_params[0] : selected_params;
if (!ApplySendParams(new_send_params) || !ApplyRecvParams(new_recv_params)) {
return false;
}
applied_send_params_ = new_send_params;
applied_recv_params_ = new_recv_params;
if (final) {
offer_params_.clear();
state_ = ST_ACTIVE;
} else {
state_ = (source == CS_LOCAL) ? ST_SENTPRANSWER : ST_RECEIVEDPRANSWER;
}
return true;
}
bool SrtpFilter::NegotiateParams(const std::vector<CryptoParams>& answer_params,
CryptoParams* selected_params) {
// We're processing an accept. We should have exactly one set of params,
// unless the offer didn't mention crypto, in which case we shouldn't be here.
bool ret = (answer_params.size() == 1U && !offer_params_.empty());
if (ret) {
// We should find a match between the answer params and the offered params.
std::vector<CryptoParams>::const_iterator it;
for (it = offer_params_.begin(); it != offer_params_.end(); ++it) {
if (answer_params[0].Matches(*it)) {
break;
}
}
if (it != offer_params_.end()) {
*selected_params = *it;
} else {
ret = false;
}
}
if (!ret) {
RTC_LOG(LS_WARNING) << "Invalid parameters in SRTP answer";
}
return ret;
}
bool SrtpFilter::ResetParams() {
offer_params_.clear();
applied_send_params_ = CryptoParams();
applied_recv_params_ = CryptoParams();
send_cipher_suite_ = absl::nullopt;
recv_cipher_suite_ = absl::nullopt;
send_key_.Clear();
recv_key_.Clear();
state_ = ST_INIT;
return true;
}
bool SrtpFilter::ApplySendParams(const CryptoParams& send_params) {
if (applied_send_params_.cipher_suite == send_params.cipher_suite &&
applied_send_params_.key_params == send_params.key_params) {
RTC_LOG(LS_INFO) << "Applying the same SRTP send parameters again. No-op.";
// We do not want to reset the ROC if the keys are the same. So just return.
return true;
}
send_cipher_suite_ = rtc::SrtpCryptoSuiteFromName(send_params.cipher_suite);
if (send_cipher_suite_ == rtc::SRTP_INVALID_CRYPTO_SUITE) {
RTC_LOG(LS_WARNING) << "Unknown crypto suite(s) received:"
" send cipher_suite "
<< send_params.cipher_suite;
return false;
}
int send_key_len, send_salt_len;
if (!rtc::GetSrtpKeyAndSaltLengths(*send_cipher_suite_, &send_key_len,
&send_salt_len)) {
RTC_LOG(LS_WARNING) << "Could not get lengths for crypto suite(s):"
" send cipher_suite "
<< send_params.cipher_suite;
return false;
}
send_key_ = rtc::ZeroOnFreeBuffer<uint8_t>(send_key_len + send_salt_len);
return ParseKeyParams(send_params.key_params, send_key_.data(),
send_key_.size());
}
bool SrtpFilter::ApplyRecvParams(const CryptoParams& recv_params) {
if (applied_recv_params_.cipher_suite == recv_params.cipher_suite &&
applied_recv_params_.key_params == recv_params.key_params) {
RTC_LOG(LS_INFO) << "Applying the same SRTP recv parameters again. No-op.";
// We do not want to reset the ROC if the keys are the same. So just return.
return true;
}
recv_cipher_suite_ = rtc::SrtpCryptoSuiteFromName(recv_params.cipher_suite);
if (recv_cipher_suite_ == rtc::SRTP_INVALID_CRYPTO_SUITE) {
RTC_LOG(LS_WARNING) << "Unknown crypto suite(s) received:"
" recv cipher_suite "
<< recv_params.cipher_suite;
return false;
}
int recv_key_len, recv_salt_len;
if (!rtc::GetSrtpKeyAndSaltLengths(*recv_cipher_suite_, &recv_key_len,
&recv_salt_len)) {
RTC_LOG(LS_WARNING) << "Could not get lengths for crypto suite(s):"
" recv cipher_suite "
<< recv_params.cipher_suite;
return false;
}
recv_key_ = rtc::ZeroOnFreeBuffer<uint8_t>(recv_key_len + recv_salt_len);
return ParseKeyParams(recv_params.key_params, recv_key_.data(),
recv_key_.size());
}
bool SrtpFilter::ParseKeyParams(const std::string& key_params,
uint8_t* key,
size_t len) {
// example key_params: "inline:YUJDZGVmZ2hpSktMbW9QUXJzVHVWd3l6MTIzNDU2"
// Fail if key-method is wrong.
if (key_params.find("inline:") != 0) {
return false;
}
// Fail if base64 decode fails, or the key is the wrong size.
std::string key_b64(key_params.substr(7)), key_str;
if (!rtc::Base64::Decode(key_b64, rtc::Base64::DO_STRICT, &key_str,
nullptr) ||
key_str.size() != len) {
return false;
}
memcpy(key, key_str.c_str(), len);
// TODO(bugs.webrtc.org/8905): Switch to ZeroOnFreeBuffer for storing
// sensitive data.
rtc::ExplicitZeroMemory(&key_str[0], key_str.size());
return true;
}
} // namespace cricket