Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support all Certificate Fingerprint Algorithms #1077

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading