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