From c53e0f927246262483f880b8e5bf69f3db7c9d8f Mon Sep 17 00:00:00 2001 From: Adrien Beraud <adrien.beraud@savoirfairelinux.com> Date: Sun, 2 Apr 2017 18:13:07 +0200 Subject: [PATCH] libsodium --- CMakeLists.txt | 5 +- include/opendht/crypto.h | 145 +++++++++++++++++++++++++++++++ include/opendht/network_engine.h | 3 + include/opendht/node.h | 4 + src/network_engine.cpp | 3 +- 5 files changed, 157 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 34dd5cfb..5a8e35ab 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 ca01b883..6470c4fe 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 7a012561..40c903a9 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 f3a324ea..751319bc 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 eeca01f1..47d37d92 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)) -- GitLab