Commit fe3d0f80 authored by Adrien Béraud's avatar Adrien Béraud Committed by Guillaume Roguez

srtp: negotiate sdes in sdp

Refs #66174

Change-Id: I4cfea1aeabc5c9d488bd9501d3c01d0aa7fdf1ec
Signed-off-by: Guillaume Roguez's avatarGuillaume Roguez <guillaume.roguez@savoirfairelinux.com>
parent 21761eaa
......@@ -411,6 +411,12 @@ AudioRtpSession::start()
try {
socketPair_.reset(new SocketPair(getRemoteRtpUri().c_str(),
local_.addr.getPort()));
if (local_.crypto and remote_.crypto) {
socketPair_->createSRTP(local_.crypto.getCryptoSuite().c_str(),
local_.crypto.getSrtpKeyInfo().c_str(),
remote_.crypto.getCryptoSuite().c_str(),
remote_.crypto.getSrtpKeyInfo().c_str());
}
} catch (const std::runtime_error &e) {
RING_ERR("Socket creation failed on port %d: %s",
local_.addr.getPort(), e.what());
......@@ -435,6 +441,12 @@ AudioRtpSession::start(std::unique_ptr<IceSocket> rtp_sock,
try {
socketPair_.reset(new SocketPair(std::move(rtp_sock),
std::move(rtcp_sock)));
if (local_.crypto and remote_.crypto) {
socketPair_->createSRTP(local_.crypto.getCryptoSuite().c_str(),
local_.crypto.getSrtpKeyInfo().c_str(),
remote_.crypto.getCryptoSuite().c_str(),
remote_.crypto.getSrtpKeyInfo().c_str());
}
} catch (const std::runtime_error &e) {
RING_ERR("Socket creation failed");
return;
......
......@@ -34,7 +34,6 @@
#define __MEDIA_CODEC_H__
#include "audio/audiobuffer.h" // for AudioFormat
#include "sip/sdes_negotiator.h"
#include "ip_utils.h"
#include <string>
......@@ -173,6 +172,65 @@ struct AccountVideoCodecInfo : AccountCodecInfo
};
bool operator== (SystemCodecInfo codec1, SystemCodecInfo codec2);
class CryptoAttribute {
public:
CryptoAttribute() {}
CryptoAttribute(const std::string& tag,
const std::string& cryptoSuite,
const std::string& srtpKeyMethod,
const std::string& srtpKeyInfo,
const std::string& lifetime,
const std::string& mkiValue,
const std::string& mkiLength) :
tag_(tag),
cryptoSuite_(cryptoSuite),
srtpKeyMethod_(srtpKeyMethod),
srtpKeyInfo_(srtpKeyInfo),
lifetime_(lifetime),
mkiValue_(mkiValue),
mkiLength_(mkiLength) {
}
std::string getTag() const {
return tag_;
}
std::string getCryptoSuite() const {
return cryptoSuite_;
}
std::string getSrtpKeyMethod() const {
return srtpKeyMethod_;
}
std::string getSrtpKeyInfo() const {
return srtpKeyInfo_;
}
std::string getLifetime() const {
return lifetime_;
}
std::string getMkiValue() const {
return mkiValue_;
}
std::string getMkiLength() const {
return mkiLength_;
}
operator bool() const {
return not tag_.empty();
}
std::string to_string() const {
return tag_+" "+cryptoSuite_+" "+srtpKeyMethod_+":"+srtpKeyInfo_;
}
private:
std::string tag_;
std::string cryptoSuite_;
std::string srtpKeyMethod_;
std::string srtpKeyInfo_;
std::string lifetime_;
std::string mkiValue_;
std::string mkiLength_;
};
/**
* MediaDescription
* Negotiated RTP media slot
......
......@@ -220,7 +220,6 @@ void SocketPair::createSRTP(const char *out_suite, const char *out_key,
in_suite, in_key));
}
void SocketPair::interrupt()
{
interrupted_ = true;
......
......@@ -141,8 +141,8 @@ RingAccount::newOutgoingCall(const std::string& id, const std::string& toUrl)
if (dhtf != std::string::npos) {
dhtf = dhtf+5;
} else {
dhtf = toUrl.find("sip:");
dhtf = (dhtf == std::string::npos) ? 0 : dhtf+4;
dhtf = toUrl.find("sips:");
dhtf = (dhtf == std::string::npos) ? 0 : dhtf+5;
}
if (toUrl.length() - dhtf < 40)
throw std::invalid_argument("id must be a ring infohash");
......@@ -155,6 +155,7 @@ RingAccount::newOutgoingCall(const std::string& id, const std::string& toUrl)
auto call = Manager::instance().callFactory.newCall<SIPCall, RingAccount>(*this, id, Call::OUTGOING);
call->setIPToIP(true);
call->setSecure(isTlsEnabled());
auto& iceTransportFactory = Manager::instance().getIceTransportFactory();
auto ice = iceTransportFactory.createTransport(
......
......@@ -42,14 +42,12 @@
namespace ring {
SdesNegotiator::SdesNegotiator(const std::vector<CryptoSuiteDefinition>& localCapabilites,
const std::vector<std::string>& remoteAttribute) :
remoteAttribute_(remoteAttribute),
SdesNegotiator::SdesNegotiator(const std::vector<CryptoSuiteDefinition>& localCapabilites) :
localCapabilities_(localCapabilites)
{}
std::vector<CryptoAttribute>
SdesNegotiator::parse() const
SdesNegotiator::parse(const std::vector<std::string>& attributes)
{
// The patterns below try to follow
// the ABNF grammar rules described in
......@@ -88,7 +86,7 @@ SdesNegotiator::parse() const
std::vector<CryptoAttribute> cryptoAttributeVector;
for (const auto &item : remoteAttribute_) {
for (const auto &item : attributes) {
// Split the line into its component
// that we will analyze further down.
......@@ -171,10 +169,10 @@ SdesNegotiator::parse() const
}
CryptoAttribute
SdesNegotiator::negotiate() const
SdesNegotiator::negotiate(const std::vector<std::string>& attributes) const
{
try {
auto cryptoAttributeVector(parse());
auto cryptoAttributeVector(parse(attributes));
for (const auto& iter_offer : cryptoAttributeVector) {
for (const auto& iter_local : localCapabilities_) {
if (iter_offer.getCryptoSuite().compare(iter_local.name))
......
......@@ -31,6 +31,8 @@
#ifndef __SDES_NEGOTIATOR_H__
#define __SDES_NEGOTIATOR_H__
#include "media/media_codec.h"
#include <stdexcept>
#include <string>
#include <vector>
......@@ -81,67 +83,18 @@ struct CryptoSuiteDefinition {
* List of accepted Crypto-Suites
* as defined in RFC4568 (6.2)
*/
static const CryptoSuiteDefinition CryptoSuites[] = {
{ "AES_CM_128_HMAC_SHA1_80", 128, 112, 48, 31, AESCounterMode, 128, HMACSHA1, 80, 80, 160, 160 },
{ "AES_CM_128_HMAC_SHA1_32", 128, 112, 48, 31, AESCounterMode, 128, HMACSHA1, 32, 80, 160, 160 },
{ "F8_128_HMAC_SHA1_80", 128, 112, 48, 31, AESF8Mode, 128, HMACSHA1, 80, 80, 160, 160 }
};
constexpr static CryptoSuiteDefinition CryptoSuites[] = {
{ "AES_CM_128_HMAC_SHA1_80",
128, 112, 48, 31, AESCounterMode, 128, HMACSHA1, 80, 80, 160, 160 },
class CryptoAttribute {
public:
CryptoAttribute() {}
CryptoAttribute(const std::string &tag,
const std::string &cryptoSuite,
const std::string &srtpKeyMethod,
const std::string &srtpKeyInfo,
const std::string &lifetime,
const std::string &mkiValue,
const std::string &mkiLength) :
tag_(tag),
cryptoSuite_(cryptoSuite),
srtpKeyMethod_(srtpKeyMethod),
srtpKeyInfo_(srtpKeyInfo),
lifetime_(lifetime),
mkiValue_(mkiValue),
mkiLength_(mkiLength) {}
std::string getTag() const {
return tag_;
}
std::string getCryptoSuite() const {
return cryptoSuite_;
}
std::string getSrtpKeyMethod() const {
return srtpKeyMethod_;
}
std::string getSrtpKeyInfo() const {
return srtpKeyInfo_;
}
std::string getLifetime() const {
return lifetime_;
}
std::string getMkiValue() const {
return mkiValue_;
}
std::string getMkiLength() const {
return mkiLength_;
}
{ "AES_CM_128_HMAC_SHA1_32",
128, 112, 48, 31, AESCounterMode, 128, HMACSHA1, 32, 80, 160, 160 },
operator bool() const {
return not tag_.empty();
}
private:
std::string tag_;
std::string cryptoSuite_;
std::string srtpKeyMethod_;
std::string srtpKeyInfo_;
std::string lifetime_;
std::string mkiValue_;
std::string mkiLength_;
{ "F8_128_HMAC_SHA1_80",
128, 112, 48, 31, AESF8Mode, 128, HMACSHA1, 80, 80, 160, 160 }
};
class SdesNegotiator {
/**
* Constructor for an SDES crypto attributes
......@@ -154,21 +107,28 @@ class SdesNegotiator {
* from it.
*/
public:
SdesNegotiator(const std::vector<CryptoSuiteDefinition>& localCapabilites,
const std::vector<std::string>& remoteAttribute);
SdesNegotiator() {}
SdesNegotiator(const std::vector<CryptoSuiteDefinition>& capabilites);
CryptoAttribute negotiate() const;
ring::CryptoAttribute
negotiate(const std::vector<std::string>& attributes) const;
operator bool() const {
return not localCapabilities_.empty();
}
private:
static std::vector<CryptoAttribute>
parse(const std::vector<std::string>& attributes);
/**
* A vector list containing the remote attributes.
* Multiple crypto lines can be sent, and the
* preferred method is then chosen from that list.
*/
std::vector<std::string> remoteAttribute_;
std::vector<CryptoSuiteDefinition> localCapabilities_;
std::vector<CryptoAttribute> parse() const;
};
}
} // namespace ring
#endif // __SDES_NEGOTIATOR_H__
......@@ -39,13 +39,13 @@
#include "sipaccount.h"
#include "sipvoiplink.h"
#include "sdes_negotiator.h"
#include "string_utils.h"
#include "base64.h"
#include "manager.h"
#include "logger.h"
#include "libav_utils.h"
#include "array_size.h"
#include "media_codec.h"
#include "system_codec_container.h"
......@@ -82,6 +82,7 @@ Sdp::Sdp(const std::string& id)
, localAudioControlPort_(0)
, localVideoDataPort_(0)
, localVideoControlPort_(0)
, sdesNego_ {{*CryptoSuites}}
, zrtpHelloHash_()
, telephoneEventPayload_(101) // same as asterisk
{
......@@ -635,6 +636,16 @@ Sdp::getFilteredSdp(const pjmedia_sdp_session* session, unsigned media_keep)
i--;
}
// we handle crypto ourselfs, don't tell libav about it
for (unsigned i = 0; i < cloned->media_count; i++) {
auto media = cloned->media[i];
while (auto attr = pjmedia_sdp_attr_find2(media->attr_count,
media->attr, "crypto",
nullptr)) {
pjmedia_sdp_attr_remove(&media->attr_count, media->attr, attr);
}
}
char buffer[BUF_SZ];
size_t size = pjmedia_sdp_print(cloned, buffer, sizeof(buffer));
string sessionStr(buffer, std::min(size, sizeof(buffer)));
......@@ -644,7 +655,7 @@ Sdp::getFilteredSdp(const pjmedia_sdp_session* session, unsigned media_keep)
std::vector<MediaDescription>
Sdp::getMediaSlots(const pjmedia_sdp_session* session, bool remote)
Sdp::getMediaSlots(const pjmedia_sdp_session* session, bool remote) const
{
static const pj_str_t STR_RTPMAP = { (char*) "rtpmap", 6 };
......@@ -721,6 +732,15 @@ Sdp::getMediaSlots(const pjmedia_sdp_session* session, bool remote)
descr.receiving_sdp = getFilteredSdp(session, i);
RING_WARN("receiving_sdp : %s", descr.receiving_sdp.c_str());
}
// get crypto info
std::vector<std::string> crypto;
for (unsigned j = 0; j < media->attr_count; j++) {
const auto attribute = media->attr[j];
if (pj_stricmp2(&attribute->name, "crypto") == 0)
crypto.emplace_back(attribute->value.ptr, attribute->value.slen);
}
descr.crypto = sdesNego_.negotiate(crypto);
}
return ret;
}
......
......@@ -34,6 +34,8 @@
#define SDP_H_
#include "noncopyable.h"
#include "sdes_negotiator.h"
#include "sip_utils.h"
#include "ip_utils.h"
#include "ice_transport.h"
#include "media_codec.h"
......@@ -207,7 +209,8 @@ class Sdp {
void addAttributeToLocalVideoMedia(const char *attr);
void removeAttributeFromLocalVideoMedia(const char *attr);
static std::vector<MediaDescription> getMediaSlots(const pjmedia_sdp_session* session, bool remote);
std::vector<MediaDescription>
getMediaSlots(const pjmedia_sdp_session* session, bool remote) const;
using MediaSlot = std::pair<MediaDescription, MediaDescription>;
std::vector<MediaSlot> getMediaSlots() const;
......@@ -298,6 +301,7 @@ class Sdp {
int localVideoDataPort_;
int localVideoControlPort_;
SdesNegotiator sdesNego_;
std::string zrtpHelloHash_;
unsigned int telephoneEventPayload_;
......
......@@ -749,6 +749,12 @@ SIPCall::startIce()
void
SIPCall::startAllMedia()
{
if (isSecure() && not transport_->isSecure()) {
RING_ERR("Can't perform secure call over insecure SIP transport");
Manager::instance().callFailure(*this);
removeCall();
return;
}
auto slots = sdp_->getMediaSlots();
unsigned ice_comp_id = 0;
......@@ -759,6 +765,10 @@ SIPCall::startAllMedia()
RING_ERR("Inconsistent media types between local and remote for SDP media slot");
continue;
}
if (isSecure() && (not local.crypto || not remote.crypto)) {
RING_ERR("Can't perform secure call over insecure RTP transport");
continue;
}
RtpSession* rtp = local.type == MEDIA_AUDIO
? static_cast<RtpSession*>(avformatrtp_.get())
: static_cast<RtpSession*>(&videortp_);
......@@ -832,4 +842,15 @@ SIPCall::openPortsUPnP()
}
}
void
SIPCall::setSecure(bool sec)
{
if (srtpEnabled_)
return;
if (sec && getConnectionState() != DISCONNECTED) {
throw std::runtime_error("Can't enable security since call is already connected");
}
srtpEnabled_ = sec;
}
} // namespace ring
......@@ -118,6 +118,12 @@ class SIPCall : public Call
struct InvSessionDeleter { void operator()(pjsip_inv_session*) {} };
std::unique_ptr<pjsip_inv_session, InvSessionDeleter> inv {};
void setSecure(bool sec);
bool isSecure() const {
return srtpEnabled_;
}
void setCallMediaLocal(const pj_sockaddr& localIP);
void setContactHeader(pj_str_t *contact);
......@@ -216,6 +222,8 @@ class SIPCall : public Call
video::VideoRtpSession videortp_;
#endif
bool srtpEnabled_ {false};
/**
* Hold the transport used for SIP communication.
* Will be different from the account registration transport for
......
......@@ -127,6 +127,10 @@ class SipTransport
void addStateListener(uintptr_t lid, SipTransportStateCallback cb);
bool removeStateListener(uintptr_t lid);
bool isSecure() const {
return PJSIP_TRANSPORT_IS_SECURE(transport_);
}
static bool isAlive(const std::shared_ptr<SipTransport>&, pjsip_transport_state state);
private:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment