Skip to content

Commit

Permalink
Added OpenSSL as an alternative to GnuTLS
Browse files Browse the repository at this point in the history
  • Loading branch information
paullouisageneau committed Dec 10, 2019
1 parent 585450d commit e75ae36
Show file tree
Hide file tree
Showing 10 changed files with 459 additions and 34 deletions.
37 changes: 25 additions & 12 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,19 +44,10 @@ else()
target_compile_options(usrsctp-static PRIVATE -Wno-error=address-of-packed-member -Wno-error=format-truncation)
endif()

option(USE_GNUTLS "Use GnuTLS instead of OpenSSL" OFF)

find_package(GnuTLS REQUIRED)
find_package(LibNice REQUIRED)

if(NOT TARGET GnuTLS::GnuTLS)
add_library(GnuTLS::GnuTLS UNKNOWN IMPORTED)
set_target_properties(GnuTLS::GnuTLS PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${GNUTLS_INCLUDE_DIRS}"
INTERFACE_COMPILE_DEFINITIONS "${GNUTLS_DEFINITIONS}"
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
IMPORTED_LOCATION "${GNUTLS_LIBRARIES}")
endif()

add_library(datachannel SHARED ${LIBDATACHANNEL_SOURCES})
set_target_properties(datachannel PROPERTIES
VERSION ${PROJECT_VERSION}
Expand All @@ -65,7 +56,7 @@ set_target_properties(datachannel PROPERTIES
target_include_directories(datachannel PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_include_directories(datachannel PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/rtc)
target_include_directories(datachannel PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
target_link_libraries(datachannel usrsctp-static GnuTLS::GnuTLS LibNice::LibNice)
target_link_libraries(datachannel usrsctp-static LibNice::LibNice)

add_library(datachannel-static STATIC EXCLUDE_FROM_ALL ${LIBDATACHANNEL_SOURCES})
set_target_properties(datachannel-static PROPERTIES
Expand All @@ -75,7 +66,29 @@ set_target_properties(datachannel-static PROPERTIES
target_include_directories(datachannel-static PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_include_directories(datachannel-static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/rtc)
target_include_directories(datachannel-static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
target_link_libraries(datachannel-static usrsctp-static GnuTLS::GnuTLS LibNice::LibNice)
target_link_libraries(datachannel-static usrsctp-static LibNice::LibNice)

if (USE_GNUTLS)
find_package(GnuTLS REQUIRED)
if(NOT TARGET GnuTLS::GnuTLS)
add_library(GnuTLS::GnuTLS UNKNOWN IMPORTED)
set_target_properties(GnuTLS::GnuTLS PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${GNUTLS_INCLUDE_DIRS}"
INTERFACE_COMPILE_DEFINITIONS "${GNUTLS_DEFINITIONS}"
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
IMPORTED_LOCATION "${GNUTLS_LIBRARIES}")
endif()
target_compile_definitions(datachannel PRIVATE USE_GNUTLS=1)
target_link_libraries(datachannel GnuTLS::GnuTLS)
target_compile_definitions(datachannel-static PRIVATE USE_GNUTLS=1)
target_link_libraries(datachannel-static GnuTLS::GnuTLS)
else()
find_package(OpenSSL REQUIRED)
target_compile_definitions(datachannel PRIVATE USE_GNUTLS=0)
target_link_libraries(datachannel OpenSSL::SSL)
target_compile_definitions(datachannel-static PRIVATE USE_GNUTLS=0)
target_link_libraries(datachannel-static OpenSSL::SSL)
endif()

add_library(LibDataChannel::LibDataChannel ALIAS datachannel)
add_library(LibDataChannel::LibDataChannelStatic ALIAS datachannel-static)
Expand Down
6 changes: 4 additions & 2 deletions Jamfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ lib libdatachannel
[ glob ./src/*.cpp ]
: # requirements
<include>./include/rtc
<cxxflags>"`pkg-config --cflags gnutls glib-2.0 gobject-2.0 nice`"
<define>USE_GNUTLS=0
<cxxflags>"`pkg-config --cflags openssl glib-2.0 gobject-2.0 nice`"
<library>/libdatachannel//usrsctp
: # default build
<link>static
: # usage requirements
<include>./include
<linkflags>"`pkg-config --libs gnutls glib-2.0 gobject-2.0 nice`"
<cxxflags>-pthread
<linkflags>"`pkg-config --libs openssl glib-2.0 gobject-2.0 nice`"
;

alias usrsctp
Expand Down
15 changes: 12 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,21 @@ RM=rm -f
CPPFLAGS=-O2 -pthread -fPIC -Wall -Wno-address-of-packed-member
CXXFLAGS=-std=c++17
LDFLAGS=-pthread
LIBS=gnutls glib-2.0 gobject-2.0 nice
LIBS=glib-2.0 gobject-2.0 nice
USRSCTP_DIR=usrsctp

USE_GNUTLS ?= 0
ifeq ($(USE_GNUTLS), 1)
CPPFLAGS+= -DUSE_GNUTLS=1
LIBS+= gnutls
else
CPPFLAGS+= -DUSE_GNUTLS=0
LIBS+= openssl
endif

LDLIBS= $(shell pkg-config --libs $(LIBS))
INCLUDES=-Iinclude/rtc -I$(USRSCTP_DIR)/usrsctplib $(shell pkg-config --cflags $(LIBS))

USRSCTP_DIR:=usrsctp

SRCS=$(shell printf "%s " src/*.cpp)
OBJS=$(subst .cpp,.o,$(SRCS))

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ The library aims at fully implementing SCTP DataChannels ([draft-ietf-rtcweb-dat
## Dependencies

- libnice: https://github.com/libnice/libnice
- GnuTLS: https://www.gnutls.org/
- GnuTLS: https://www.gnutls.org/ or OpenSSL: https://www.openssl.org/

Submodules:
- usrsctp: https://github.com/sctplab/usrsctp
Expand All @@ -24,7 +24,7 @@ Submodules:
$ git submodule update --init --recursive
$ mkdir build
$ cd build
$ cmake ..
$ cmake -DUSE_GNUTLS=1 ..
$ make
```

Expand Down
128 changes: 124 additions & 4 deletions src/certificate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,13 @@
#include <sstream>
#include <unordered_map>

#include <gnutls/crypto.h>

using std::shared_ptr;
using std::string;
using std::unique_ptr;

#if USE_GNUTLS

#include <gnutls/crypto.h>

namespace {

Expand Down Expand Up @@ -117,10 +120,10 @@ Certificate::Certificate(gnutls_x509_crt_t crt, gnutls_x509_privkey_t privkey)
"Unable to set certificate and key pair in credentials");
}

string Certificate::fingerprint() const { return mFingerprint; }

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

string Certificate::fingerprint() const { return mFingerprint; }

string make_fingerprint(gnutls_x509_crt_t crt) {
const size_t size = 32;
unsigned char buffer[size];
Expand Down Expand Up @@ -177,3 +180,120 @@ shared_ptr<Certificate> make_certificate(const string &commonName) {
}

} // namespace rtc

#else

#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>

namespace rtc {

Certificate::Certificate(string crt_pem, string key_pem) {
BIO *bio;

bio = BIO_new(BIO_s_mem());
BIO_write(bio, crt_pem.data(), crt_pem.size());
mX509 = shared_ptr<X509>(PEM_read_bio_X509(bio, nullptr, 0, 0), X509_free);
BIO_free(bio);
if (!mX509)
throw std::invalid_argument("Unable to import certificate PEM");

bio = BIO_new(BIO_s_mem());
BIO_write(bio, key_pem.data(), key_pem.size());
mPKey = shared_ptr<EVP_PKEY>(PEM_read_bio_PrivateKey(bio, nullptr, 0, 0), EVP_PKEY_free);
BIO_free(bio);
if (!mPKey)
throw std::invalid_argument("Unable to import PEM key PEM");

mFingerprint = make_fingerprint(mX509.get());
}

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

string Certificate::fingerprint() const { return mFingerprint; }

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))
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]);
}
return oss.str();
}


shared_ptr<Certificate> make_certificate(const string &commonName) {
static std::unordered_map<string, shared_ptr<Certificate>> cache;
static std::mutex cacheMutex;

std::lock_guard<std::mutex> lock(cacheMutex);
if (auto it = cache.find(commonName); it != cache.end())
return it->second;

if (cache.empty()) {
// This is the first call to OpenSSL
OPENSSL_init_ssl(0, NULL);
SSL_load_error_strings();
ERR_load_crypto_strings();
}

shared_ptr<X509> x509(X509_new(), X509_free);
shared_ptr<EVP_PKEY> pkey(EVP_PKEY_new(), EVP_PKEY_free);

unique_ptr<RSA, decltype(&RSA_free)> rsa(RSA_new(), RSA_free);
unique_ptr<BIGNUM, decltype(&BN_free)> exponent(BN_new(), BN_free);
unique_ptr<BIGNUM, decltype(&BN_free)> serial_number(BN_new(), BN_free);
unique_ptr<X509_NAME, decltype(&X509_NAME_free)> name(X509_NAME_new(), X509_NAME_free);

if (!x509 || !pkey || !rsa || !exponent || !serial_number || !name)
throw std::runtime_error("Unable allocate structures for certificate generation");

const int bits = 4096;
const unsigned int e = 65537; // 2^16 + 1

if (!pkey || !rsa || !exponent || !BN_set_word(exponent.get(), e) ||
!RSA_generate_key_ex(rsa.get(), bits, exponent.get(), NULL) ||
!EVP_PKEY_assign_RSA(pkey.get(), rsa.release())) // the key will be freed when pkey is freed
throw std::runtime_error("Unable to generate key pair");

const size_t serialSize = 16;
const auto *commonNameBytes = reinterpret_cast<const unsigned char *>(commonName.c_str());

if (!X509_gmtime_adj(X509_get_notBefore(x509.get()), 3600 * -1) ||
!X509_gmtime_adj(X509_get_notAfter(x509.get()), 3600 * 24 * 365) ||
!X509_set_version(x509.get(), 1) || !X509_set_pubkey(x509.get(), pkey.get()) ||
!BN_pseudo_rand(serial_number.get(), serialSize, 0, 0) ||
!BN_to_ASN1_INTEGER(serial_number.get(), X509_get_serialNumber(x509.get())) ||
!X509_NAME_add_entry_by_NID(name.get(), NID_commonName, MBSTRING_UTF8, commonNameBytes, -1,
-1, 0) ||
!X509_set_subject_name(x509.get(), name.get()) ||
!X509_set_issuer_name(x509.get(), name.get()))
throw std::runtime_error("Unable to set certificate properties");

if (!X509_sign(x509.get(), pkey.get(), EVP_sha256()))
throw std::runtime_error("Unable to auto-sign certificate");

auto certificate = std::make_shared<Certificate>(x509, pkey);
cache.emplace(std::make_pair(commonName, certificate));
return certificate;
}

} // namespace rtc

#endif

27 changes: 25 additions & 2 deletions src/certificate.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,47 @@

#include "include.hpp"

#include <tuple>

#if USE_GNUTLS
#include <gnutls/x509.h>
#else
#include <openssl/x509.h>
#endif

namespace rtc {

class Certificate {
public:
Certificate(gnutls_x509_crt_t crt, gnutls_x509_privkey_t privkey);
Certificate(string crt_pem, string key_pem);

string fingerprint() const;
#if USE_GNUTLS
Certificate(gnutls_x509_crt_t crt, gnutls_x509_privkey_t privkey);
gnutls_certificate_credentials_t credentials() const;
#else
Certificate(std::shared_ptr<X509> x509, std::shared_ptr<EVP_PKEY> pkey);
std::tuple<X509 *, EVP_PKEY *> credentials() const;
#endif

string fingerprint() const;

private:
#if USE_GNUTLS
std::shared_ptr<gnutls_certificate_credentials_t> mCredentials;
#else
std::shared_ptr<X509> mX509;
std::shared_ptr<EVP_PKEY> mPKey;
#endif

string mFingerprint;
};

#if USE_GNUTLS
string make_fingerprint(gnutls_x509_crt_t crt);
#else
string make_fingerprint(X509 *x509);
#endif

std::shared_ptr<Certificate> make_certificate(const string &commonName);

} // namespace rtc
Expand Down
Loading

0 comments on commit e75ae36

Please sign in to comment.