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
  • Loading branch information
Sean-Der committed Dec 30, 2023
1 parent 715e296 commit cacf5fd
Show file tree
Hide file tree
Showing 10 changed files with 225 additions and 67 deletions.
19 changes: 17 additions & 2 deletions include/rtc/description.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,18 @@ class RTC_CPP_EXPORT Description {
enum class Type { Unspec, Offer, Answer, Pranswer, Rollback };
enum class Role { ActPass, Passive, Active };

enum class FingerprintAlgorithm {
MD5, // RFC3279
SHA1, // RFC3279
SHA224, // RFC4055
SHA256, // RFC4055
SHA384, // RFC4055
SHA512, // RFC4055
};

static size_t fingerprintAlgorithmSize(FingerprintAlgorithm fingerprintAlgorithm);
static std::string fingerprintAlgorithmIdentifier(FingerprintAlgorithm fingerprintAlgorithm);

enum class Direction {
SendOnly = RTC_DIRECTION_SENDONLY,
RecvOnly = RTC_DIRECTION_RECVONLY,
Expand All @@ -52,10 +64,11 @@ class RTC_CPP_EXPORT Description {
optional<string> iceUfrag() const;
optional<string> icePwd() const;
optional<string> fingerprint() const;
optional<Description::FingerprintAlgorithm> fingerprintAlgorithm() const;
bool ended() const;

void hintType(Type type);
void setFingerprint(string fingerprint);
void setFingerprint(string fingerprint, Description::FingerprintAlgorithm fingerprintAlgorithm);
void addIceOption(string option);
void removeIceOption(const string &option);

Expand Down Expand Up @@ -292,6 +305,7 @@ class RTC_CPP_EXPORT Description {
std::vector<string> mIceOptions;
optional<string> mIceUfrag, mIcePwd;
optional<string> mFingerprint;
optional<FingerprintAlgorithm> mFingerprintAlgorithm;
std::vector<string> mAttributes; // other attributes

// Entries
Expand All @@ -308,6 +322,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
109 changes: 92 additions & 17 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,9 +66,13 @@ template <typename T> T to_integer(string_view s) {
}
}

inline bool is_sha256_fingerprint(string_view f) {
if (f.size() != 32 * 3 - 1)
inline bool is_valid_fingerprint(string_view f, size_t expectedSize) {
auto valuesOnly = std::string(f);
valuesOnly.erase(std::remove(valuesOnly.begin(), valuesOnly.end(), ':'), valuesOnly.end());

if (valuesOnly.size() != expectedSize * 2) {
return false;
}

for (size_t i = 0; i < f.size(); ++i) {
if (i % 3 == 2) {
Expand Down Expand Up @@ -131,13 +130,41 @@ 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)); });

Description::FingerprintAlgorithm fingerprintAlgorithm;
if (first ==
fingerprintAlgorithmIdentifier(Description::FingerprintAlgorithm::MD5)) {
fingerprintAlgorithm = Description::FingerprintAlgorithm::MD5;
} else if (first == fingerprintAlgorithmIdentifier(
Description::FingerprintAlgorithm::SHA1)) {
fingerprintAlgorithm = Description::FingerprintAlgorithm::SHA1;
} else if (first == fingerprintAlgorithmIdentifier(
Description::FingerprintAlgorithm::SHA224)) {
fingerprintAlgorithm = Description::FingerprintAlgorithm::SHA224;
} else if (first == fingerprintAlgorithmIdentifier(
Description::FingerprintAlgorithm::SHA256)) {
fingerprintAlgorithm = Description::FingerprintAlgorithm::SHA256;
} else if (first == fingerprintAlgorithmIdentifier(
Description::FingerprintAlgorithm::SHA384)) {
fingerprintAlgorithm = Description::FingerprintAlgorithm::SHA384;
} else if (first == fingerprintAlgorithmIdentifier(
Description::FingerprintAlgorithm::SHA512)) {
fingerprintAlgorithm = Description::FingerprintAlgorithm::SHA512;
} else {
PLOG_WARNING << "Unknown certificate fingerprint algorithm: " << first;
continue;
}

setFingerprint(std::move(fingerprintExploded.at(1)), fingerprintAlgorithm);
}
} else if (key == "ice-ufrag") {
// RFC 8839: The "ice-pwd" and "ice-ufrag" attributes can appear at either the
Expand Down Expand Up @@ -207,20 +234,26 @@ optional<string> Description::icePwd() const { return mIcePwd; }

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

optional<Description::FingerprintAlgorithm> Description::fingerprintAlgorithm() const {
return mFingerprintAlgorithm;
}

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

void Description::hintType(Type type) {
if (mType == Type::Unspec)
mType = type;
}

void Description::setFingerprint(string fingerprint) {
if (!is_sha256_fingerprint(fingerprint))
throw std::invalid_argument("Invalid SHA256 fingerprint \"" + fingerprint + "\"");
void Description::setFingerprint(string fingerprint, FingerprintAlgorithm fingerprintAlgorithm) {
if (!is_valid_fingerprint(fingerprint,
Description::fingerprintAlgorithmSize(fingerprintAlgorithm)))
throw std::invalid_argument("Invalid certificate fingerprint \"" + fingerprint + "\"");

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

void Description::addIceOption(string option) {
Expand Down Expand Up @@ -315,7 +348,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:"
<< Description::fingerprintAlgorithmIdentifier(mFingerprintAlgorithm.value()) << " "
<< *mFingerprint << eol;

for (const auto &attr : mAttributes)
sdp << "a=" << attr << eol;
Expand Down Expand Up @@ -378,7 +413,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:"
<< Description::fingerprintAlgorithmIdentifier(mFingerprintAlgorithm.value()) << " "
<< *mFingerprint << eol;

for (const auto &attr : mAttributes)
sdp << "a=" << attr << eol;
Expand Down Expand Up @@ -1288,6 +1325,44 @@ string Description::typeToString(Type type) {
}
}

size_t Description::fingerprintAlgorithmSize(Description::FingerprintAlgorithm fingerprintAlgorithm) {
switch (fingerprintAlgorithm) {
case Description::FingerprintAlgorithm::MD5:
return 16;
case Description::FingerprintAlgorithm::SHA1:
return 20;
case Description::FingerprintAlgorithm::SHA224:
return 28;
case Description::FingerprintAlgorithm::SHA256:
return 32;
case Description::FingerprintAlgorithm::SHA384:
return 48;
case Description::FingerprintAlgorithm::SHA512:
return 64;
}

return 0;
}

std::string Description::fingerprintAlgorithmIdentifier(Description::FingerprintAlgorithm fingerprintAlgorithm) {
switch (fingerprintAlgorithm) {
case Description::FingerprintAlgorithm::MD5:
return "md5";
case Description::FingerprintAlgorithm::SHA1:
return "sha-1";
case Description::FingerprintAlgorithm::SHA224:
return "sha-224";
case Description::FingerprintAlgorithm::SHA256:
return "sha-256";
case Description::FingerprintAlgorithm::SHA384:
return "sha-256";
case Description::FingerprintAlgorithm::SHA512:
return "sha-512";
}

return "";
}

} // namespace rtc

std::ostream &operator<<(std::ostream &out, const rtc::Description &description) {
Expand Down
99 changes: 75 additions & 24 deletions src/impl/certificate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,18 +100,20 @@ Certificate Certificate::Generate(CertificateType type, const string &commonName

Certificate::Certificate(gnutls_x509_crt_t crt, gnutls_x509_privkey_t privkey)
: mCredentials(gnutls::new_credentials(), gnutls::free_credentials),
mFingerprint(make_fingerprint(crt)) {
mFingerprint(make_fingerprint(crt, Description::FingerprintAlgorithm::SHA256)) {

gnutls::check(gnutls_certificate_set_x509_key(*mCredentials, &crt, 1, privkey),
"Unable to set certificate and key pair in credentials");
}

Certificate::Certificate(shared_ptr<gnutls_certificate_credentials_t> creds)
: mCredentials(std::move(creds)), mFingerprint(make_fingerprint(*mCredentials)) {}
: mCredentials(std::move(creds)),
mFingerprint(make_fingerprint(*mCredentials, Description::FingerprintAlgorithm::SHA256)) {}

gnutls_certificate_credentials_t Certificate::credentials() const { return *mCredentials; }

string make_fingerprint(gnutls_certificate_credentials_t credentials) {
string make_fingerprint(gnutls_certificate_credentials_t credentials,
Description::FingerprintAlgorithm fingerprintAlgorithm) {
auto new_crt_list = [credentials]() -> gnutls_x509_crt_t * {
gnutls_x509_crt_t *crt_list = nullptr;
unsigned int crt_list_size = 0;
Expand All @@ -127,38 +129,71 @@ string make_fingerprint(gnutls_certificate_credentials_t credentials) {

unique_ptr<gnutls_x509_crt_t, decltype(free_crt_list)> crt_list(new_crt_list(), free_crt_list);

return make_fingerprint(*crt_list);
return make_fingerprint(*crt_list, fingerprintAlgorithm);
}

string make_fingerprint(gnutls_x509_crt_t crt) {
const size_t size = 32;
unsigned char buffer[size];
string make_fingerprint(gnutls_x509_crt_t crt,
Description::FingerprintAlgorithm fingerprintAlgorithm) {
const size_t size = Description::fingerprintAlgorithmSize(fingerprintAlgorithm);
std::vector<unsigned char> buffer(size);
size_t len = size;
gnutls::check(gnutls_x509_crt_get_fingerprint(crt, GNUTLS_DIG_SHA256, buffer, &len),
auto hashFunc = GNUTLS_DIG_SHA256;
if (fingerprintAlgorithm == Description::FingerprintAlgorithm::MD5) {
hashFunc = GNUTLS_DIG_MD5;
} else if (fingerprintAlgorithm == Description::FingerprintAlgorithm::SHA1) {
hashFunc = GNUTLS_DIG_SHA1;
} else if (fingerprintAlgorithm == Description::FingerprintAlgorithm::SHA224) {
hashFunc = GNUTLS_DIG_SHA224;
} else if (fingerprintAlgorithm == Description::FingerprintAlgorithm::SHA384) {
hashFunc = GNUTLS_DIG_SHA384;
} else if (fingerprintAlgorithm == Description::FingerprintAlgorithm::SHA512) {
hashFunc = GNUTLS_DIG_SHA512;
}

gnutls::check(gnutls_x509_crt_get_fingerprint(crt, hashFunc, buffer.data(), &len),
"X509 fingerprint error");

std::ostringstream oss;
oss << std::hex << std::uppercase << std::setfill('0');
for (size_t i = 0; i < len; ++i) {
if (i)
oss << std::setw(1) << ':';
oss << std::setw(2) << unsigned(buffer[i]);
oss << std::setw(2) << unsigned(buffer.at(i));
}
return oss.str();
}

#elif USE_MBEDTLS
string make_fingerprint(mbedtls_x509_crt *crt) {
const int size = 32;
uint8_t buffer[size];
string make_fingerprint(mbedtls_x509_crt *crt,
Description::FingerprintAlgorithm fingerprintAlgorithm) {
const int size = Description::fingerprintAlgorithmSize(fingerprintAlgorithm);
std::vector<unsigned char> buffer(size);
std::stringstream fingerprint;

mbedtls::check(
mbedtls_sha256(crt->raw.p, crt->raw.len, reinterpret_cast<unsigned char *>(buffer), 0),
"Failed to generate certificate fingerprint");
if (fingerprintAlgorithm == Description::FingerprintAlgorithm::SHA1) {
mbedtls::check(
mbedtls_sha1(crt->raw.p, crt->raw.len, buffer.data(),
"Failed to generate certificate fingerprint");
} else if (fingerprintAlgorithm == Description::FingerprintAlgorithm::SHA224) {
mbedtls::check(
mbedtls_sha256(crt->raw.p, crt->raw.len, buffer.data(), 1),
"Failed to generate certificate fingerprint");
} else if (fingerprintAlgorithm == Description::FingerprintAlgorithm::SHA256) {
mbedtls::check(
mbedtls_sha256(crt->raw.p, crt->raw.len, buffer.data(), 0),
"Failed to generate certificate fingerprint");
} else if (fingerprintAlgorithm == Description::FingerprintAlgorithm::SHA384) {
mbedtls::check(
mbedtls_sha512(crt->raw.p, crt->raw.len, buffer.data(), 1),
"Failed to generate certificate fingerprint");
} else if (fingerprintAlgorithm == Description::FingerprintAlgorithm::SHA512) {
mbedtls::check(
mbedtls_sha512(crt->raw.p, crt->raw.len, buffer.data(), 0),
"Failed to generate certificate fingerprint");
}

for (auto i = 0; i < size; i++) {
fingerprint << std::setfill('0') << std::setw(2) << std::hex << static_cast<int>(buffer[i]);
fingerprint << std::setfill('0') << std::setw(2) << std::hex << static_cast<int>(buffer.at(i));
if (i != (size - 1)) {
fingerprint << ":";
}
Expand All @@ -168,7 +203,8 @@ string make_fingerprint(mbedtls_x509_crt *crt) {
}

Certificate::Certificate(shared_ptr<mbedtls_x509_crt> crt, shared_ptr<mbedtls_pk_context> pk)
: mCrt(crt), mPk(pk), mFingerprint(make_fingerprint(crt.get())) {}
: mCrt(crt), mPk(pk),
mFingerprint(make_fingerprint(crt.get(), Description::FingerprintAlgorithm::SHA256)) {}

Certificate Certificate::FromString(string crt_pem, string key_pem) {
PLOG_DEBUG << "Importing certificate from PEM string (MbedTLS)";
Expand Down Expand Up @@ -465,25 +501,40 @@ Certificate Certificate::Generate(CertificateType type, const string &commonName
}

Certificate::Certificate(shared_ptr<X509> x509, shared_ptr<EVP_PKEY> pkey)
: mX509(std::move(x509)), mPKey(std::move(pkey)), mFingerprint(make_fingerprint(mX509.get())) {}
: mX509(std::move(x509)), mPKey(std::move(pkey)),
mFingerprint(make_fingerprint(mX509.get(), Description::FingerprintAlgorithm::SHA256)) {}

std::tuple<X509 *, EVP_PKEY *> Certificate::credentials() const {
return {mX509.get(), mPKey.get()};
}

string make_fingerprint(X509 *x509) {
const size_t size = 32;
unsigned char buffer[size];
unsigned int len = size;
if (!X509_digest(x509, EVP_sha256(), buffer, &len))
string make_fingerprint(X509 *x509, Description::FingerprintAlgorithm fingerprintAlgorithm) {
size_t size = Description::fingerprintAlgorithmSize(fingerprintAlgorithm);
std::vector<unsigned char> buffer(size);
auto len = static_cast<unsigned int>(size);

auto hashFunc = EVP_sha256();
if (fingerprintAlgorithm == Description::FingerprintAlgorithm::MD5) {
hashFunc = EVP_md5();
} else if (fingerprintAlgorithm == Description::FingerprintAlgorithm::SHA1) {
hashFunc = EVP_sha1();
} else if (fingerprintAlgorithm == Description::FingerprintAlgorithm::SHA224) {
hashFunc = EVP_sha224();
} else if (fingerprintAlgorithm == Description::FingerprintAlgorithm::SHA384) {
hashFunc = EVP_sha384();
} else if (fingerprintAlgorithm == Description::FingerprintAlgorithm::SHA512) {
hashFunc = EVP_sha512();
}

if (!X509_digest(x509, hashFunc, buffer.data(), &len))
throw std::runtime_error("X509 fingerprint error");

std::ostringstream oss;
oss << std::hex << std::uppercase << std::setfill('0');
for (size_t i = 0; i < len; ++i) {
if (i)
oss << std::setw(1) << ':';
oss << std::setw(2) << unsigned(buffer[i]);
oss << std::setw(2) << unsigned(buffer.at(i));
}
return oss.str();
}
Expand Down
Loading

0 comments on commit cacf5fd

Please sign in to comment.