diff --git a/CMakeLists.txt b/CMakeLists.txt index 34dd5cfb4e3fd748ff7f547b2b633eb95a6d500f..5a8e35abe5d2db82a9483e10482453aaa159b7c6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,7 @@ option (OPENDHT_LTO "Build with LTO" OFF) list (APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") find_package (PkgConfig) pkg_search_module(libuv libuv) +pkg_search_module(libsodium libsodium) find_package (GnuTLS 3.3 REQUIRED) pkg_search_module (Nettle nettle) find_package (Msgpack 1.2 REQUIRED) @@ -167,7 +168,7 @@ if (OPENDHT_STATIC) target_link_libraries(opendht-static ${argon2_LIBRARIES}) target_include_directories(opendht-static SYSTEM PRIVATE ${argon2_INCLUDE_DIRS}) endif () - target_link_libraries(opendht-static ${GNUTLS_LIBRARIES} ${Nettle_LIBRARIES} ${libuv_LIBRARIES}) + target_link_libraries(opendht-static ${GNUTLS_LIBRARIES} ${Nettle_LIBRARIES} ${libuv_LIBRARIES} ${libsodium_LIBRARIES}) install (TARGETS opendht-static DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT opendht) endif () @@ -188,7 +189,7 @@ if (OPENDHT_SHARED) target_link_libraries(opendht ${argon2_LIBRARIES}) target_include_directories(opendht SYSTEM PRIVATE ${argon2_INCLUDE_DIRS}) endif () - target_link_libraries(opendht ${GNUTLS_LIBRARIES} ${Nettle_LIBRARIES} ${libuv_LIBRARIES}) + target_link_libraries(opendht ${GNUTLS_LIBRARIES} ${Nettle_LIBRARIES} ${libuv_LIBRARIES} ${libsodium_LIBRARIES}) install (TARGETS opendht DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT opendht) endif () diff --git a/include/opendht/crypto.h b/include/opendht/crypto.h index ca01b88374f87b3b4b522f82372c5e2202e5f5e6..6470c4fe66db90fc5710fd48196c0c62fca72f7e 100644 --- a/include/opendht/crypto.h +++ b/include/opendht/crypto.h @@ -28,6 +28,8 @@ extern "C" { #include <gnutls/x509.h> } +#include <sodium.h> + #include <vector> #include <memory> @@ -525,6 +527,7 @@ public: clean(); munlock(data_.data(), data_.size()*sizeof(T)); data_ = c.data_; + mlock(data_.data(), data_.size()*sizeof(T)); return *this; } secure_vector<T>& operator=(secure_vector<T>&& c) { @@ -545,6 +548,7 @@ public: std::vector<T>& writable() { clean(); return data_; } const std::vector<T>& makeInsecure() const { return data_; } const uint8_t* data() const { return data_.data(); } + uint8_t* data() { return data_.data(); } void clean() { clean(data_.begin(), data_.end()); @@ -588,8 +592,149 @@ private: std::vector<T> data_; }; + +template <class T, size_t N> +class OPENDHT_PUBLIC secure_array +{ +public: + secure_array() { + mlock(data_.data(), data_.size()*sizeof(T)); + } + explicit secure_array(T _item): data_( _item) { + mlock(data_.data(), data_.size()*sizeof(T)); + } + explicit secure_array(const std::array<T,N>& c): data_(c) { + mlock(data_.data(), data_.size()*sizeof(T)); + } + secure_array(secure_array<T,N> const&) = default; + secure_array(secure_array<T,N> &&) = delete; + ~secure_array() { clean(); munlock(data_.data(), data_.size()*sizeof(T)); } + + static secure_array<T,N> getRandom() { + secure_array<T,N> ret; + crypto::random_device rdev; + std::uniform_int_distribution<uint8_t> rand_byte; + std::generate_n((uint8_t*)ret.data_.data(), ret.size()*sizeof(T), std::bind(rand_byte, std::ref(rdev))); + return ret; + } + secure_array<T,N>& operator=(const secure_array<T,N>& c) { + if (&c == this) + return *this; + clean(); + data_ = c.data_; + return *this; + } + secure_array<T,N>& operator=(secure_array<T,N>&& c) = delete; + std::array<T,N>& writable() { clean(); return data_; } + const std::array<T,N>& makeInsecure() const { return data_; } + const uint8_t* data() const { return data_.data(); } + uint8_t* data() { return data_.data(); } + + void clean() { + clean(data_.begin(), data_.end()); + } + size_t size() const { return data_.size(); } + +private: + /** + * Securely wipe memory + */ + static void clean(const typename std::array<T,N>::iterator& i, const typename std::array<T,N>::iterator& j) { + volatile uint8_t* b = reinterpret_cast<uint8_t*>(&*i); + volatile uint8_t* e = reinterpret_cast<uint8_t*>(&*j); + std::fill(b, e, 0); + } + + std::array<T, N> data_; +}; + + using SecureBlob = secure_vector<uint8_t>; +class EcPublicKey : public std::array<uint8_t, crypto_box_PUBLICKEYBYTES> { +public: + Blob encrypt(const Blob& message) const { + Blob ret(crypto_box_SEALBYTES+message.size()); + crypto_box_seal(ret.data(), message.data(), message.size(), data()); + return ret; + } + +}; + +class EcSecretKey { +public: + using KeyData = secure_array<uint8_t, crypto_box_SECRETKEYBYTES>; + + static EcSecretKey generate() { + EcSecretKey ret; + crypto_box_keypair(ret.pk.data(), ret.key.data()); + return ret; + } + + const EcPublicKey& getPublicKey() const { + return pk; + } + + Blob encrypt(const Blob& message, const EcPublicKey& pub) const { + Blob ret(crypto_box_NONCEBYTES+crypto_box_MACBYTES+message.size()); + randombytes_buf(ret.data(), crypto_box_NONCEBYTES); + if (crypto_box_easy(ret.data()+crypto_box_NONCEBYTES, message.data(), message.size(), ret.data(), + pub.data(), key.data()) != 0) { + throw CryptoException("Can't encrypt data"); + } + return ret; + } + + Blob decrypt(const Blob& cypher) const { + if (cypher.size() <= crypto_box_SEALBYTES) + throw DecryptError("Unexpected cipher length"); + Blob ret(cypher.size() - crypto_box_SEALBYTES); + if (crypto_box_seal_open(ret.data(), cypher.data(), cypher.size(), pk.data(), key.data()) != 0) { + throw DecryptError("Can't decrypt data"); + } + return ret; + } + + Blob decrypt(const Blob& cypher, const EcPublicKey& pub) const { + if (cypher.size() <= crypto_box_NONCEBYTES+crypto_box_MACBYTES) + throw DecryptError("Unexpected cipher length"); + Blob ret(cypher.size() - crypto_box_NONCEBYTES - crypto_box_MACBYTES); + if (crypto_box_open_easy(ret.data(), + cypher.data()+crypto_box_NONCEBYTES, + cypher.size()-crypto_box_NONCEBYTES, + cypher.data(), + pub.data(), key.data()) != 0) { + throw DecryptError("Can't decrypt data"); + } + return ret; + } + + SecureBlob computeSecret(const EcPublicKey& pub, bool sending) const { + // shared key = h(q ‖ client_publickey ‖ server_publickey) + crypto_generichash_state h; + crypto_generichash_init(&h, nullptr, 0U, crypto_generichash_BYTES); + { + std::array<uint8_t, crypto_scalarmult_BYTES> q; + if (auto err = crypto_scalarmult(q.data(), key.data(), pub.data())) + throw CryptoException("Can't compute secret: " + std::to_string(err)); + crypto_generichash_update(&h, q.data(), q.size()); + } + { + const auto& sender_pk = sending ? pk : pub; + const auto& receiver_pk = sending ? pub : pk; + crypto_generichash_update(&h, sender_pk.data(), sender_pk.size()); + crypto_generichash_update(&h, receiver_pk.data(), receiver_pk.size()); + } + SecureBlob ret(crypto_generichash_BYTES); + crypto_generichash_final(&h, ret.data(), ret.size()); + return ret; + } + +private: + KeyData key; + EcPublicKey pk; +}; + /** * Generate an RSA key pair (4096 bits) and a certificate. * @param name the name used in the generated certificate diff --git a/include/opendht/network_engine.h b/include/opendht/network_engine.h index 7a012561d592c3c6928641889b6cb3f5eeaf7f28..40c903a976357fd3639062a8818dfeb1138d194d 100644 --- a/include/opendht/network_engine.h +++ b/include/opendht/network_engine.h @@ -569,6 +569,9 @@ private: std::set<Sp<TcpSocket>> pending_connect; NodeCache cache {}; + crypto::EcSecretKey id_key; + //crypto::EcSecretKey id_key; + /** * A comparator to classify IP addresses, only considering the * first 64 bits in IPv6. diff --git a/include/opendht/node.h b/include/opendht/node.h index f3a324eab4005da64a5194903e0bc000e05ed4c7..751319bc9cb4733177b409165dfaf6f4564ae7d6 100644 --- a/include/opendht/node.h +++ b/include/opendht/node.h @@ -23,6 +23,7 @@ #include "utils.h" #include "sockaddr.h" #include "uv_utils.h" +#include "crypto.h" #include <list> @@ -37,6 +38,9 @@ struct Node { SockAddr addr; Sp<TcpSocket> sock; + crypto::EcPublicKey last_known_pk; + //std::list<crypto::EcSecretKey> + time_point time {time_point::min()}; /* last time eared about */ time_point reply_time {time_point::min()}; /* time of last correct reply received */ diff --git a/src/network_engine.cpp b/src/network_engine.cpp index eeca01f124af93eaf68b492c54b4fcbfac728b41..47d37d92b52010675c1a2868a4c26ffe01fa5489 100644 --- a/src/network_engine.cpp +++ b/src/network_engine.cpp @@ -186,7 +186,8 @@ NetworkEngine::NetworkEngine(uv_loop_t* loop, const NetworkConfig& config, InfoH decltype(NetworkEngine::onRefresh) onRefresh) : onError(onError), onNewNode(onNewNode), onReportedAddr(onReportedAddr), onPing(onPing), onFindNode(onFindNode), onGetValues(onGetValues), onListen(onListen), onAnnounce(onAnnounce), onRefresh(onRefresh), myid(myid), - network(net), DHT_LOG(log), sock(std::make_shared<UdpSocket>(loop)), tcp_sock(std::make_shared<TcpSocket>(loop)), scheduler(scheduler) + network(net), DHT_LOG(log), sock(std::make_shared<UdpSocket>(loop)), tcp_sock(std::make_shared<TcpSocket>(loop)), + id_key(crypto::EcSecretKey::generate()), scheduler(scheduler) { if (dht_socket >= 0) { if (!set_nonblocking(dht_socket, 1))