mirror of
https://github.com/mii443/libdatachannel.git
synced 2025-08-22 15:15:28 +00:00
Added fingerprint check
This commit is contained in:
@ -40,6 +40,11 @@ inline bool match_prefix(string_view str, string_view prefix) {
|
|||||||
std::mismatch(prefix.begin(), prefix.end(), str.begin()).first == prefix.end();
|
std::mismatch(prefix.begin(), prefix.end(), str.begin()).first == prefix.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void trim_begin(string &str) {
|
||||||
|
str.erase(str.begin(),
|
||||||
|
std::find_if(str.begin(), str.end(), [](char c) { return !std::isspace(c); }));
|
||||||
|
}
|
||||||
|
|
||||||
inline void trim_end(string &str) {
|
inline void trim_end(string &str) {
|
||||||
str.erase(
|
str.erase(
|
||||||
std::find_if(str.rbegin(), str.rend(), [](char c) { return !std::isspace(c); }).base(),
|
std::find_if(str.rbegin(), str.rend(), [](char c) { return !std::isspace(c); }).base(),
|
||||||
@ -61,12 +66,27 @@ template <typename T> T to_integer(string_view s) {
|
|||||||
const string str(s);
|
const string str(s);
|
||||||
try {
|
try {
|
||||||
return std::is_signed<T>::value ? T(std::stol(str)) : T(std::stoul(str));
|
return std::is_signed<T>::value ? T(std::stol(str)) : T(std::stoul(str));
|
||||||
}
|
} catch (...) {
|
||||||
catch(...) {
|
|
||||||
throw std::invalid_argument("Invalid integer \"" + str + "\" in description");
|
throw std::invalid_argument("Invalid integer \"" + str + "\" in description");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool is_sha256_fingerprint(string_view f) {
|
||||||
|
if (f.size() != 32 * 3 - 1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < f.size(); ++i) {
|
||||||
|
if (i % 3 == 2) {
|
||||||
|
if (f[i] != ':')
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
if (!std::isxdigit(f[i]))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace rtc {
|
namespace rtc {
|
||||||
@ -106,10 +126,9 @@ Description::Description(const string &sdp, Type type, Role role)
|
|||||||
|
|
||||||
} else if (key == "fingerprint") {
|
} else if (key == "fingerprint") {
|
||||||
if (match_prefix(value, "sha-256 ")) {
|
if (match_prefix(value, "sha-256 ")) {
|
||||||
mFingerprint = value.substr(8);
|
string fingerprint{value.substr(8)};
|
||||||
std::transform(mFingerprint->begin(), mFingerprint->end(),
|
trim_begin(fingerprint);
|
||||||
mFingerprint->begin(),
|
setFingerprint(std::move(fingerprint));
|
||||||
[](char c) { return char(std::toupper(c)); });
|
|
||||||
} else {
|
} else {
|
||||||
PLOG_WARNING << "Unknown SDP fingerprint format: " << value;
|
PLOG_WARNING << "Unknown SDP fingerprint format: " << value;
|
||||||
}
|
}
|
||||||
@ -173,6 +192,11 @@ void Description::hintType(Type type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Description::setFingerprint(string fingerprint) {
|
void Description::setFingerprint(string fingerprint) {
|
||||||
|
if (!is_sha256_fingerprint(fingerprint))
|
||||||
|
throw std::invalid_argument("Invalid SHA256 fingerprint \"" + fingerprint + "\"");
|
||||||
|
|
||||||
|
std::transform(fingerprint.begin(), fingerprint.end(), fingerprint.begin(),
|
||||||
|
[](char c) { return char(std::toupper(c)); });
|
||||||
mFingerprint.emplace(std::move(fingerprint));
|
mFingerprint.emplace(std::move(fingerprint));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -401,8 +425,7 @@ int Description::addAudio(string mid, Direction dir) {
|
|||||||
return addMedia(Audio(std::move(mid), dir));
|
return addMedia(Audio(std::move(mid), dir));
|
||||||
}
|
}
|
||||||
|
|
||||||
variant<Description::Media *, Description::Application *>
|
variant<Description::Media *, Description::Application *> Description::media(unsigned int index) {
|
||||||
Description::media(unsigned int index) {
|
|
||||||
if (index >= mEntries.size())
|
if (index >= mEntries.size())
|
||||||
throw std::out_of_range("Media index out of range");
|
throw std::out_of_range("Media index out of range");
|
||||||
|
|
||||||
@ -523,21 +546,22 @@ Description::Entry::removeAttribute(std::vector<string>::iterator it) {
|
|||||||
return mAttributes.erase(it);
|
return mAttributes.erase(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Description::Media::addSSRC(uint32_t ssrc, optional<string> name,
|
void Description::Media::addSSRC(uint32_t ssrc, optional<string> name, optional<string> msid,
|
||||||
optional<string> msid, optional<string> trackID) {
|
optional<string> trackID) {
|
||||||
if (name)
|
if (name)
|
||||||
mAttributes.emplace_back("ssrc:" + std::to_string(ssrc) + " cname:" + *name);
|
mAttributes.emplace_back("ssrc:" + std::to_string(ssrc) + " cname:" + *name);
|
||||||
else
|
else
|
||||||
mAttributes.emplace_back("ssrc:" + std::to_string(ssrc));
|
mAttributes.emplace_back("ssrc:" + std::to_string(ssrc));
|
||||||
|
|
||||||
if (msid)
|
if (msid)
|
||||||
mAttributes.emplace_back("ssrc:" + std::to_string(ssrc) + " msid:" + *msid + " " + trackID.value_or(*msid));
|
mAttributes.emplace_back("ssrc:" + std::to_string(ssrc) + " msid:" + *msid + " " +
|
||||||
|
trackID.value_or(*msid));
|
||||||
|
|
||||||
mSsrcs.emplace_back(ssrc);
|
mSsrcs.emplace_back(ssrc);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Description::Media::replaceSSRC(uint32_t oldSSRC, uint32_t ssrc, optional<string> name,
|
void Description::Media::replaceSSRC(uint32_t oldSSRC, uint32_t ssrc, optional<string> name,
|
||||||
optional<string> msid, optional<string> trackID) {
|
optional<string> msid, optional<string> trackID) {
|
||||||
auto it = mAttributes.begin();
|
auto it = mAttributes.begin();
|
||||||
while (it != mAttributes.end()) {
|
while (it != mAttributes.end()) {
|
||||||
if (it->find("ssrc:" + std::to_string(oldSSRC)) == 0) {
|
if (it->find("ssrc:" + std::to_string(oldSSRC)) == 0) {
|
||||||
@ -708,8 +732,7 @@ void Description::Media::removeFormat(const string &fmt) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Description::Video::addVideoCodec(int payloadType, string codec,
|
void Description::Video::addVideoCodec(int payloadType, string codec, optional<string> profile) {
|
||||||
optional<string> profile) {
|
|
||||||
RTPMap map(std::to_string(payloadType) + ' ' + codec + "/90000");
|
RTPMap map(std::to_string(payloadType) + ' ' + codec + "/90000");
|
||||||
map.addFB("nack");
|
map.addFB("nack");
|
||||||
map.addFB("nack pli");
|
map.addFB("nack pli");
|
||||||
@ -733,8 +756,7 @@ void Description::Video::addVideoCodec(int payloadType, string codec,
|
|||||||
// ";rtx-time=3000"); addRTPMap(rtx);
|
// ";rtx-time=3000"); addRTPMap(rtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Description::Audio::addAudioCodec(int payloadType, string codec,
|
void Description::Audio::addAudioCodec(int payloadType, string codec, optional<string> profile) {
|
||||||
optional<string> profile) {
|
|
||||||
// TODO This 48000/2 should be parameterized
|
// TODO This 48000/2 should be parameterized
|
||||||
RTPMap map(std::to_string(payloadType) + ' ' + codec + "/48000/2");
|
RTPMap map(std::to_string(payloadType) + ' ' + codec + "/48000/2");
|
||||||
if (profile)
|
if (profile)
|
||||||
@ -843,9 +865,7 @@ void Description::Media::addRTPMap(const Description::Media::RTPMap &map) {
|
|||||||
mRtpMap.emplace(map.pt, map);
|
mRtpMap.emplace(map.pt, map);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint32_t> Description::Media::getSSRCs() {
|
std::vector<uint32_t> Description::Media::getSSRCs() { return mSsrcs; }
|
||||||
return mSsrcs;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::map<int, Description::Media::RTPMap>::iterator Description::Media::beginMaps() {
|
std::map<int, Description::Media::RTPMap>::iterator Description::Media::beginMaps() {
|
||||||
return mRtpMap.begin();
|
return mRtpMap.begin();
|
||||||
|
@ -355,10 +355,14 @@ void PeerConnection::rollbackLocalDescription() {
|
|||||||
|
|
||||||
bool PeerConnection::checkFingerprint(const std::string &fingerprint) const {
|
bool PeerConnection::checkFingerprint(const std::string &fingerprint) const {
|
||||||
std::lock_guard lock(mRemoteDescriptionMutex);
|
std::lock_guard lock(mRemoteDescriptionMutex);
|
||||||
if (auto expectedFingerprint =
|
auto expectedFingerprint = mRemoteDescription ? mRemoteDescription->fingerprint() : nullopt;
|
||||||
mRemoteDescription ? mRemoteDescription->fingerprint() : nullopt) {
|
if (expectedFingerprint && *expectedFingerprint == fingerprint) {
|
||||||
return *expectedFingerprint == fingerprint;
|
PLOG_VERBOSE << "Valid fingerprint \"" << fingerprint << "\"";
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PLOG_ERROR << "Invalid fingerprint \"" << fingerprint << "\", expected \""
|
||||||
|
<< expectedFingerprint.value_or("[none]") << "\"";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -482,16 +486,16 @@ optional<std::string> PeerConnection::getMidFromSsrc(uint32_t ssrc) {
|
|||||||
if (!mRemoteDescription)
|
if (!mRemoteDescription)
|
||||||
return nullopt;
|
return nullopt;
|
||||||
for (unsigned int i = 0; i < mRemoteDescription->mediaCount(); ++i) {
|
for (unsigned int i = 0; i < mRemoteDescription->mediaCount(); ++i) {
|
||||||
if (auto found = std::visit(
|
if (auto found =
|
||||||
rtc::overloaded{[&](Description::Application *) -> optional<string> {
|
std::visit(rtc::overloaded{[&](Description::Application *) -> optional<string> {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
},
|
},
|
||||||
[&](Description::Media *media) -> optional<string> {
|
[&](Description::Media *media) -> optional<string> {
|
||||||
return media->hasSSRC(ssrc)
|
return media->hasSSRC(ssrc)
|
||||||
? std::make_optional(media->mid())
|
? std::make_optional(media->mid())
|
||||||
: nullopt;
|
: nullopt;
|
||||||
}},
|
}},
|
||||||
mRemoteDescription->media(i))) {
|
mRemoteDescription->media(i))) {
|
||||||
|
|
||||||
mMidFromSsrc.emplace(ssrc, *found);
|
mMidFromSsrc.emplace(ssrc, *found);
|
||||||
return *found;
|
return *found;
|
||||||
@ -503,16 +507,16 @@ optional<std::string> PeerConnection::getMidFromSsrc(uint32_t ssrc) {
|
|||||||
if (!mLocalDescription)
|
if (!mLocalDescription)
|
||||||
return nullopt;
|
return nullopt;
|
||||||
for (unsigned int i = 0; i < mLocalDescription->mediaCount(); ++i) {
|
for (unsigned int i = 0; i < mLocalDescription->mediaCount(); ++i) {
|
||||||
if (auto found = std::visit(
|
if (auto found =
|
||||||
rtc::overloaded{[&](Description::Application *) -> optional<string> {
|
std::visit(rtc::overloaded{[&](Description::Application *) -> optional<string> {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
},
|
},
|
||||||
[&](Description::Media *media) -> optional<string> {
|
[&](Description::Media *media) -> optional<string> {
|
||||||
return media->hasSSRC(ssrc)
|
return media->hasSSRC(ssrc)
|
||||||
? std::make_optional(media->mid())
|
? std::make_optional(media->mid())
|
||||||
: nullopt;
|
: nullopt;
|
||||||
}},
|
}},
|
||||||
mLocalDescription->media(i))) {
|
mLocalDescription->media(i))) {
|
||||||
|
|
||||||
mMidFromSsrc.emplace(ssrc, *found);
|
mMidFromSsrc.emplace(ssrc, *found);
|
||||||
return *found;
|
return *found;
|
||||||
@ -680,7 +684,7 @@ void PeerConnection::validateRemoteDescription(const Description &description) {
|
|||||||
throw std::invalid_argument("Remote description has no ICE password");
|
throw std::invalid_argument("Remote description has no ICE password");
|
||||||
|
|
||||||
if (!description.fingerprint())
|
if (!description.fingerprint())
|
||||||
throw std::invalid_argument("Remote description has no fingerprint");
|
throw std::invalid_argument("Remote description has no valid fingerprint");
|
||||||
|
|
||||||
if (description.mediaCount() == 0)
|
if (description.mediaCount() == 0)
|
||||||
throw std::invalid_argument("Remote description has no media line");
|
throw std::invalid_argument("Remote description has no media line");
|
||||||
|
Reference in New Issue
Block a user