Skip to content

Commit

Permalink
Support all Certificate Fingerprint Algorithms
Browse files Browse the repository at this point in the history
Before was hardcoded to sha256

Resolves #1076

Co-authored-by: Paul-Louis Ageneau <[email protected]>
  • Loading branch information
Sean-Der and paullouisageneau committed Jan 9, 2024
1 parent 715e296 commit 42ec088
Show file tree
Hide file tree
Showing 10 changed files with 252 additions and 94 deletions.
20 changes: 16 additions & 4 deletions include/rtc/description.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,17 @@ const string DEFAULT_OPUS_AUDIO_PROFILE =
const string DEFAULT_H264_VIDEO_PROFILE =
"profile-level-id=42e01f;packetization-mode=1;level-asymmetry-allowed=1";

struct CertificateFingerprint {
enum class Algorithm { Sha1, Sha224, Sha256, Sha384, Sha512 };
static string AlgorithmIdentifier(Algorithm algorithm);
static size_t AlgorithmSize(Algorithm algorithm);

bool isValid() const;

Algorithm algorithm;
string value;
};

class RTC_CPP_EXPORT Description {
public:
enum class Type { Unspec, Offer, Answer, Pranswer, Rollback };
Expand All @@ -51,11 +62,11 @@ class RTC_CPP_EXPORT Description {
std::vector<string> iceOptions() const;
optional<string> iceUfrag() const;
optional<string> icePwd() const;
optional<string> fingerprint() const;
optional<CertificateFingerprint> fingerprint() const;
bool ended() const;

void hintType(Type type);
void setFingerprint(string fingerprint);
void setFingerprint(CertificateFingerprint f);
void addIceOption(string option);
void removeIceOption(const string &option);

Expand Down Expand Up @@ -291,7 +302,7 @@ class RTC_CPP_EXPORT Description {
string mSessionId;
std::vector<string> mIceOptions;
optional<string> mIceUfrag, mIcePwd;
optional<string> mFingerprint;
optional<CertificateFingerprint> mFingerprint;
std::vector<string> mAttributes; // other attributes

// Entries
Expand All @@ -308,6 +319,7 @@ class RTC_CPP_EXPORT Description {
RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, const rtc::Description &description);
RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, rtc::Description::Type type);
RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, rtc::Description::Role role);
RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, const rtc::Description::Direction &direction);
RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out,
const rtc::Description::Direction &direction);

#endif
131 changes: 95 additions & 36 deletions src/description.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,6 @@ inline bool match_prefix(string_view str, string_view prefix) {
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) {
str.erase(
std::find_if(str.rbegin(), str.rend(), [](char c) { return !std::isspace(c); }).base(),
Expand Down Expand Up @@ -71,22 +66,6 @@ template <typename T> T to_integer(string_view s) {
}
}

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 rtc {
Expand Down Expand Up @@ -131,12 +110,35 @@ Description::Description(const string &sdp, Type type, Role role)
// media-level SDP attribute. If it is a session-level attribute, it applies to all
// TLS sessions for which no media-level fingerprint attribute is defined.
if (!mFingerprint || index == 0) { // first media overrides session-level
if (match_prefix(value, "sha-256 ") || match_prefix(value, "SHA-256 ")) {
string fingerprint{value.substr(8)};
trim_begin(fingerprint);
setFingerprint(std::move(fingerprint));
} else {
auto fingerprintExploded = utils::explode(string(value), ' ');
if (fingerprintExploded.size() != 2) {
PLOG_WARNING << "Unknown SDP fingerprint format: " << value;
continue;
}

auto first = fingerprintExploded.at(0);
std::transform(first.begin(), first.end(), first.begin(),
[](char c) { return char(std::tolower(c)); });

std::optional<CertificateFingerprint::Algorithm> fingerprintAlgorithm;

for (auto a : std::array<CertificateFingerprint::Algorithm, 5>{
CertificateFingerprint::Algorithm::Sha1,
CertificateFingerprint::Algorithm::Sha224,
CertificateFingerprint::Algorithm::Sha256,
CertificateFingerprint::Algorithm::Sha384,
CertificateFingerprint::Algorithm::Sha512}) {
if (first == CertificateFingerprint::AlgorithmIdentifier(a)) {
fingerprintAlgorithm = a;
break;
}
}

if (fingerprintAlgorithm.has_value()) {
setFingerprint(CertificateFingerprint{
fingerprintAlgorithm.value(), std::move(fingerprintExploded.at(1))});
} else {
PLOG_WARNING << "Unknown certificate fingerprint algorithm: " << first;
}
}
} else if (key == "ice-ufrag") {
Expand Down Expand Up @@ -205,7 +207,7 @@ std::vector<string> Description::iceOptions() const { return mIceOptions; }

optional<string> Description::icePwd() const { return mIcePwd; }

optional<string> Description::fingerprint() const { return mFingerprint; }
optional<CertificateFingerprint> Description::fingerprint() const { return mFingerprint; }

bool Description::ended() const { return mEnded; }

Expand All @@ -214,13 +216,13 @@ void Description::hintType(Type type) {
mType = type;
}

void Description::setFingerprint(string fingerprint) {
if (!is_sha256_fingerprint(fingerprint))
throw std::invalid_argument("Invalid SHA256 fingerprint \"" + fingerprint + "\"");
void Description::setFingerprint(CertificateFingerprint f) {
if (!f.isValid())
throw std::invalid_argument("Invalid " + CertificateFingerprint::AlgorithmIdentifier(f.algorithm) + " fingerprint \"" + f.value + "\"");

std::transform(fingerprint.begin(), fingerprint.end(), fingerprint.begin(),
std::transform(f.value.begin(), f.value.end(), f.value.begin(),
[](char c) { return char(std::toupper(c)); });
mFingerprint.emplace(std::move(fingerprint));
mFingerprint = std::move(f);
}

void Description::addIceOption(string option) {
Expand Down Expand Up @@ -315,7 +317,9 @@ string Description::generateSdp(string_view eol) const {
if (!mIceOptions.empty())
sdp << "a=ice-options:" << utils::implode(mIceOptions, ',') << eol;
if (mFingerprint)
sdp << "a=fingerprint:sha-256 " << *mFingerprint << eol;
sdp << "a=fingerprint:"
<< CertificateFingerprint::AlgorithmIdentifier(mFingerprint->algorithm) << " "
<< mFingerprint->value << eol;

for (const auto &attr : mAttributes)
sdp << "a=" << attr << eol;
Expand Down Expand Up @@ -378,7 +382,9 @@ string Description::generateApplicationSdp(string_view eol) const {
if (!mIceOptions.empty())
sdp << "a=ice-options:" << utils::implode(mIceOptions, ',') << eol;
if (mFingerprint)
sdp << "a=fingerprint:sha-256 " << *mFingerprint << eol;
sdp << "a=fingerprint:"
<< CertificateFingerprint::AlgorithmIdentifier(mFingerprint->algorithm) << " "
<< mFingerprint->value << eol;

for (const auto &attr : mAttributes)
sdp << "a=" << attr << eol;
Expand Down Expand Up @@ -876,8 +882,7 @@ void Description::Application::parseSdpLine(string_view line) {
}
}

Description::Media::Media(const string &sdp)
: Entry(get_first_line(sdp), "", Direction::Unknown) {
Description::Media::Media(const string &sdp) : Entry(get_first_line(sdp), "", Direction::Unknown) {
string line;
std::istringstream ss(sdp);
std::getline(ss, line); // discard first line
Expand Down Expand Up @@ -1288,6 +1293,60 @@ string Description::typeToString(Type type) {
}
}

size_t
CertificateFingerprint::AlgorithmSize(CertificateFingerprint::Algorithm fingerprintAlgorithm) {
switch (fingerprintAlgorithm) {
case CertificateFingerprint::Algorithm::Sha1:
return 20;
case CertificateFingerprint::Algorithm::Sha224:
return 28;
case CertificateFingerprint::Algorithm::Sha256:
return 32;
case CertificateFingerprint::Algorithm::Sha384:
return 48;
case CertificateFingerprint::Algorithm::Sha512:
return 64;
default:
return 0;
}
}

std::string CertificateFingerprint::AlgorithmIdentifier(
CertificateFingerprint::Algorithm fingerprintAlgorithm) {
switch (fingerprintAlgorithm) {
case CertificateFingerprint::Algorithm::Sha1:
return "sha-1";
case CertificateFingerprint::Algorithm::Sha224:
return "sha-224";
case CertificateFingerprint::Algorithm::Sha256:
return "sha-256";
case CertificateFingerprint::Algorithm::Sha384:
return "sha-256";
case CertificateFingerprint::Algorithm::Sha512:
return "sha-512";
default:
return "unknown";
}
}

bool CertificateFingerprint::isValid() const {
size_t expectedSize = AlgorithmSize(this->algorithm);
if (expectedSize == 0 || this->value.size() != expectedSize * 3 - 1) {
return false;
}

for (size_t i = 0; i < this->value.size(); ++i) {
if (i % 3 == 2) {
if (this->value[i] != ':')
return false;
} else {
if (!std::isxdigit(this->value[i]))
return false;
}
}
return true;
}

} // namespace rtc

std::ostream &operator<<(std::ostream &out, const rtc::Description &description) {
Expand Down
Loading

0 comments on commit 42ec088

Please sign in to comment.