]> git.seodisparate.com - UDPConnection/commitdiff
Add set peer_pk, sk/pk, start/stop threaded update
authorStephen Seo <seo.disparate@gmail.com>
Tue, 19 Nov 2019 11:55:20 +0000 (20:55 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Tue, 19 Nov 2019 11:55:20 +0000 (20:55 +0900)
Note, this code is UNTESTED.

src/UDPC_Defines.hpp
src/UDPConnection.cpp
src/UDPConnection.h

index 4016449f3042315cfc23a2371f9f037da37856b1..5eb25647a20bef63ad530b3c950ea6352b5cee84 100644 (file)
@@ -86,7 +86,9 @@ struct ConnectionData {
         UDPC_IPV6_ADDR_TYPE addr,
         uint32_t scope_id,
         uint16_t port,
-        bool isUsingLibsodium);
+        bool isUsingLibsodium,
+        unsigned char *sk,
+        unsigned char *pk);
 
     // copy
     ConnectionData(const ConnectionData& other) = delete;
@@ -106,6 +108,7 @@ struct ConnectionData {
      * 4 - is id set
      * 5 - error initializing keys for public key encryption
      * 6 - using libsodium for header verification
+     * 7 - peer_pk pre-set
      */
     std::bitset<8> flags;
     uint32_t id;
@@ -229,6 +232,9 @@ public:
     std::mutex mutex;
 
     std::chrono::milliseconds threadedSleepTime;
+    unsigned char sk[crypto_sign_SECRETKEYBYTES];
+    unsigned char pk[crypto_sign_PUBLICKEYBYTES];
+    std::atomic_bool keysSet;
 
 }; // struct Context
 
index 38b32813a3c46c192e9d8b4f3db1f6ede422a832..d5f10bddc7ea0b823d915b198df6b2de1c62311f 100644 (file)
@@ -126,7 +126,9 @@ UDPC::ConnectionData::ConnectionData(
         UDPC_IPV6_ADDR_TYPE addr,
         uint32_t scope_id,
         uint16_t port,
-        bool isUsingLibsodium) :
+        bool isUsingLibsodium,
+        unsigned char *sk,
+        unsigned char *pk) :
 flags(),
 id(0),
 lseq(0),
@@ -157,7 +159,12 @@ rtt(std::chrono::steady_clock::duration::zero())
 #ifdef UDPC_LIBSODIUM_ENABLED
     if(isUsingLibsodium) {
         if(sodium_init() >= 0) {
-            crypto_sign_keypair(pk, sk);
+            if(sk && pk) {
+                std::memcpy(this->sk, sk, crypto_sign_SECRETKEYBYTES);
+                std::memcpy(this->pk, pk, crypto_sign_PUBLICKEYBYTES);
+            } else {
+                crypto_sign_keypair(pk, sk);
+            }
             flags.reset(5);
             flags.set(6);
         } else {
@@ -216,6 +223,7 @@ mutex()
     rng_engine.seed(std::chrono::system_clock::now().time_since_epoch().count());
 
     threadRunning.store(true);
+    keysSet.store(false);
 }
 
 bool UDPC::Context::willLog(UDPC_LoggingType type) {
@@ -260,6 +268,12 @@ void UDPC::Context::update_impl() {
             switch(optE.value().type) {
             case UDPC_ET_REQUEST_CONNECT:
             {
+                unsigned char *sk = nullptr;
+                unsigned char *pk = nullptr;
+                if(keysSet.load()) {
+                    sk = this->sk;
+                    pk = this->pk;
+                }
                 UDPC::ConnectionData newCon(
                     false,
                     this,
@@ -267,9 +281,11 @@ void UDPC::Context::update_impl() {
                     optE.value().conId.scope_id,
                     optE.value().conId.port,
 #ifdef UDPC_LIBSODIUM_ENABLED
-                    flags.test(2) && optE.value().v.enableLibSodium != 0);
+                    flags.test(2) && optE.value().v.enableLibSodium != 0,
+                    sk, pk);
 #else
-                    false);
+                    false,
+                    sk, pk);
 #endif
                 if(newCon.flags.test(5)) {
                     UDPC_CHECK_LOG(this,
@@ -283,6 +299,7 @@ void UDPC::Context::update_impl() {
                 }
                 newCon.sent = std::chrono::steady_clock::now() - UDPC::INIT_PKT_INTERVAL_DT;
                 if(flags.test(2) && newCon.flags.test(6)) {
+                    // set up verification string to send to server
                     std::stringstream ss;
                     auto timeT = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
                     ss << std::put_time(std::gmtime(&timeT), "%c %Z");
@@ -292,6 +309,95 @@ void UDPC::Context::update_impl() {
                     std::memcpy(newCon.verifyMessage.get() + 4, timeString.c_str(), timeString.size());
                 }
 
+                if(conMap.find(optE.value().conId) == conMap.end()) {
+                    conMap.insert(std::make_pair(
+                        optE.value().conId,
+                        std::move(newCon)));
+                    auto addrConIter = addrConMap.find(optE.value().conId.addr);
+                    if(addrConIter == addrConMap.end()) {
+                        auto insertResult = addrConMap.insert(std::make_pair(
+                            optE.value().conId.addr,
+                            std::unordered_set<UDPC_ConnectionId, UDPC::ConnectionIdHasher>{}));
+                        assert(insertResult.second &&
+                            "new connection insert into addrConMap must not fail");
+                        addrConIter = insertResult.first;
+                    }
+                    addrConIter->second.insert(optE.value().conId);
+                    UDPC_CHECK_LOG(this,
+                        UDPC_LoggingType::UDPC_INFO,
+                        "Client initiating connection to ",
+                        UDPC_atostr((UDPC_HContext)this, optE.value().conId.addr),
+                        " port ",
+                        optE.value().conId.port,
+                        " ...");
+                } else {
+                    UDPC_CHECK_LOG(this,
+                        UDPC_LoggingType::UDPC_WARNING,
+                        "Client initiate connection, already connected to peer ",
+                        UDPC_atostr((UDPC_HContext)this, optE.value().conId.addr),
+                        " port ",
+                        optE.value().conId.port);
+                }
+            }
+                break;
+            case UDPC_ET_REQUEST_CONNECT_PK:
+            {
+                assert(flags.test(2) &&
+                    "libsodium should be explictly enabled");
+                unsigned char *sk = nullptr;
+                unsigned char *pk = nullptr;
+                if(keysSet.load()) {
+                    sk = this->sk;
+                    pk = this->pk;
+                }
+                UDPC::ConnectionData newCon(
+                    false,
+                    this,
+                    optE.value().conId.addr,
+                    optE.value().conId.scope_id,
+                    optE.value().conId.port,
+#ifdef UDPC_LIBSODIUM_ENABLED
+                    true,
+                    sk, pk);
+#else
+                    false,
+                    sk, pk);
+                assert(!"compiled without libsodium support");
+                delete[] optE.value().v.pk;
+                break;
+#endif
+                if(newCon.flags.test(5)) {
+                    delete[] optE.value().v.pk;
+                    UDPC_CHECK_LOG(this,
+                        UDPC_LoggingType::UDPC_ERROR,
+                        "Failed to init ConnectionData instance (libsodium "
+                        "init fail) while client establishing connection with ",
+                        UDPC_atostr((UDPC_HContext)this, optE.value().conId.addr),
+                        " port ",
+                        optE.value().conId.port);
+                    continue;
+                }
+                newCon.sent = std::chrono::steady_clock::now() - UDPC::INIT_PKT_INTERVAL_DT;
+                if(flags.test(2) && newCon.flags.test(6)) {
+                    // set up verification string to send to server
+                    std::stringstream ss;
+                    auto timeT = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
+                    ss << std::put_time(std::gmtime(&timeT), "%c %Z");
+                    auto timeString = ss.str();
+                    newCon.verifyMessage = std::make_unique<char[]>(4 + timeString.size());
+                    *((uint32_t*)newCon.verifyMessage.get()) = timeString.size();
+                    std::memcpy(newCon.verifyMessage.get() + 4, timeString.c_str(), timeString.size());
+
+                    // set peer public key
+                    std::memcpy(
+                        newCon.peer_pk,
+                        optE.value().v.pk,
+                        crypto_sign_PUBLICKEYBYTES);
+                    newCon.flags.set(7);
+                }
+
+                delete[] optE.value().v.pk;
+
                 if(conMap.find(optE.value().conId) == conMap.end()) {
                     conMap.insert(std::make_pair(
                         optE.value().conId,
@@ -1040,6 +1146,12 @@ void UDPC::Context::update_impl() {
                 && conMap.find(identifier) == conMap.end()
                 && isAcceptNewConnections.load()) {
             // is receiving as server, connection did not already exist
+            unsigned char *sk = nullptr;
+            unsigned char *pk = nullptr;
+            if(keysSet.load()) {
+                sk = this->sk;
+                pk = this->pk;
+            }
             UDPC::ConnectionData newConnection(
                 true,
                 this,
@@ -1047,9 +1159,11 @@ void UDPC::Context::update_impl() {
                 receivedData.sin6_scope_id,
                 ntohs(receivedData.sin6_port),
 #ifdef UDPC_LIBSODIUM_ENABLED
-                pktType == 1 && flags.test(2));
+                pktType == 1 && flags.test(2),
+                sk, pk);
 #else
-                false);
+                false,
+                sk, pk);
 #endif
             if(newConnection.flags.test(5)) {
                 UDPC_CHECK_LOG(this,
@@ -1123,8 +1237,20 @@ void UDPC::Context::update_impl() {
 
             if(pktType == 2 && flags.test(2) && iter->second.flags.test(6)) {
 #ifdef UDPC_LIBSODIUM_ENABLED
-                std::memcpy(iter->second.peer_pk, recvBuf + UDPC_MIN_HEADER_SIZE + 4,
-                    crypto_sign_PUBLICKEYBYTES);
+                if(iter->second.flags.test(7)) {
+                    if(std::memcmp(iter->second.peer_pk,
+                            recvBuf + UDPC_MIN_HEADER_SIZE + 4,
+                            crypto_sign_PUBLICKEYBYTES) != 0) {
+                        UDPC_CHECK_LOG(this, UDPC_LoggingType::UDPC_WARNING,
+                            "peer_pk did not match pre-set peer_pk, not "
+                            "establishing connection");
+                        return;
+                    }
+                } else {
+                    std::memcpy(iter->second.peer_pk,
+                        recvBuf + UDPC_MIN_HEADER_SIZE + 4,
+                        crypto_sign_PUBLICKEYBYTES);
+                }
                 if(crypto_sign_verify_detached(
                     (unsigned char*)(recvBuf + UDPC_MIN_HEADER_SIZE + 4 + crypto_sign_PUBLICKEYBYTES),
                     (unsigned char*)(iter->second.verifyMessage.get() + 4),
@@ -1145,6 +1271,12 @@ void UDPC::Context::update_impl() {
 #endif
             } else if(pktType == 0 && iter->second.flags.test(6)) {
                 iter->second.flags.reset(6);
+                if(iter->second.flags.test(7)) {
+                    UDPC_CHECK_LOG(this, UDPC_LoggingType::UDPC_WARNING,
+                        "peer is not using libsodium, but peer_pk was "
+                        "pre-set, dropping to no-verification mode");
+                    // TODO set policy for using/not-using libsodium
+                }
             }
 
             iter->second.flags.reset(3);
@@ -1618,6 +1750,7 @@ UDPC_HContext UDPC_init_threaded_update(UDPC_ConnectionId listenId,
     if(!ctx) {
         return nullptr;
     }
+
     ctx->flags.set(0);
     ctx->threadedSleepTime = std::chrono::milliseconds(8);
     ctx->thread = std::thread(UDPC::threadedUpdate, ctx);
@@ -1635,6 +1768,7 @@ UDPC_HContext UDPC_init_threaded_update_ms(
     if(!ctx) {
         return nullptr;
     }
+
     ctx->flags.set(0);
     if(updateMS < 4) {
         ctx->threadedSleepTime = std::chrono::milliseconds(4);
@@ -1650,6 +1784,53 @@ UDPC_HContext UDPC_init_threaded_update_ms(
     return (UDPC_HContext) ctx;
 }
 
+void UDPC_enable_threaded_update(UDPC_HContext ctx) {
+    UDPC::Context *c = UDPC::verifyContext(ctx);
+    if(!c || c->flags.test(0) || c->thread.joinable()) {
+        return;
+    }
+
+    c->flags.set(0);
+    c->threadedSleepTime = std::chrono::milliseconds(8);
+    c->threadRunning.store(true);
+    c->thread = std::thread(UDPC::threadedUpdate, c);
+
+    UDPC_CHECK_LOG(c, UDPC_LoggingType::UDPC_INFO, "Started threaded update");
+}
+
+void UDPC_enable_threaded_update_ms(UDPC_HContext ctx, int updateMS) {
+    UDPC::Context *c = UDPC::verifyContext(ctx);
+    if(!c || c->flags.test(0) || c->thread.joinable()) {
+        return;
+    }
+
+    c->flags.set(0);
+    if(updateMS < 4) {
+        c->threadedSleepTime = std::chrono::milliseconds(4);
+    } else if(updateMS > 333) {
+        c->threadedSleepTime = std::chrono::milliseconds(333);
+    } else {
+        c->threadedSleepTime = std::chrono::milliseconds(updateMS);
+    }
+    c->threadRunning.store(true);
+    c->thread = std::thread(UDPC::threadedUpdate, c);
+
+    UDPC_CHECK_LOG(c, UDPC_LoggingType::UDPC_INFO, "Started threaded update");
+}
+
+void UDPC_disable_threaded_update(UDPC_HContext ctx) {
+    UDPC::Context *c = UDPC::verifyContext(ctx);
+    if(!c || !c->flags.test(0) || !c->thread.joinable()) {
+        return;
+    }
+
+    c->threadRunning.store(false);
+    c->thread.join();
+    c->flags.reset(0);
+
+    UDPC_CHECK_LOG(c, UDPC_LoggingType::UDPC_INFO, "Stopped threaded update");
+}
+
 void UDPC_destroy(UDPC_HContext ctx) {
     UDPC::Context *UDPC_ctx = UDPC::verifyContext(ctx);
     if(UDPC_ctx) {
@@ -1660,6 +1841,12 @@ void UDPC_destroy(UDPC_HContext ctx) {
 #if UDPC_PLATFORM == UDPC_PLATFORM_WINDOWS
         WSACleanup();
 #endif
+        while(!UDPC_ctx->internalEvents.empty()) {
+            auto optE = UDPC_ctx->internalEvents.top_and_pop();
+            if(optE.has_value() && optE.value().type == UDPC_ET_REQUEST_CONNECT_PK) {
+                delete[] optE.value().v.pk;
+            }
+        }
         delete UDPC_ctx;
     }
 }
@@ -1674,7 +1861,10 @@ void UDPC_update(UDPC_HContext ctx) {
     c->update_impl();
 }
 
-void UDPC_client_initiate_connection(UDPC_HContext ctx, UDPC_ConnectionId connectionId, int enableLibSodium) {
+void UDPC_client_initiate_connection(
+        UDPC_HContext ctx,
+        UDPC_ConnectionId connectionId,
+        int enableLibSodium) {
     UDPC::Context *c = UDPC::verifyContext(ctx);
     if(!c || !c->flags.test(1)) {
         return;
@@ -1690,6 +1880,34 @@ void UDPC_client_initiate_connection(UDPC_HContext ctx, UDPC_ConnectionId connec
     c->internalEvents.push(UDPC_Event{UDPC_ET_REQUEST_CONNECT, connectionId, enableLibSodium});
 }
 
+void UDPC_client_initiate_connection_pk(
+        UDPC_HContext ctx,
+        UDPC_ConnectionId connectionId,
+        unsigned char *serverPK) {
+    UDPC::Context *c = UDPC::verifyContext(ctx);
+    if(!c || !c->flags.test(1)) {
+        return;
+    }
+#ifndef UDPC_LIBSODIUM_ENABLED
+    UDPC_CHECK_LOG(c, UDPC_LoggingType::UDPC_ERROR,
+        "Cannot initiate connection with public key, UDPC was compiled "
+        "without libsodium");
+    return;
+#else
+    else if(!c->flags.test(2)) {
+        UDPC_CHECK_LOG(c, UDPC_LoggingType::UDPC_ERROR,
+            "Cannot initiate connection with public key, libsodium is not "
+            "enabled");
+        return;
+    }
+#endif
+
+    UDPC_Event event{UDPC_ET_REQUEST_CONNECT_PK, connectionId, 0};
+    event.v.pk = new unsigned char[crypto_sign_PUBLICKEYBYTES];
+    std::memcpy(event.v.pk, serverPK, crypto_sign_PUBLICKEYBYTES);
+    c->internalEvents.push(event);
+}
+
 void UDPC_queue_send(UDPC_HContext ctx, UDPC_ConnectionId destinationId,
                      int isChecked, void *data, uint32_t size) {
     if(size == 0 || !data) {
@@ -1860,6 +2078,17 @@ UDPC_PacketInfo UDPC_get_received(UDPC_HContext ctx, unsigned long *remaining) {
     return UDPC::get_empty_pinfo();
 }
 
+void UDPC_set_libsodium_keys(UDPC_HContext ctx, unsigned char *sk, unsigned char *pk) {
+    UDPC::Context *c = UDPC::verifyContext(ctx);
+    if(!c || !c->flags.test(2)) {
+        return;
+    }
+
+    std::memcpy(c->sk, sk, crypto_sign_SECRETKEYBYTES);
+    std::memcpy(c->pk, pk, crypto_sign_PUBLICKEYBYTES);
+    c->keysSet.store(true);
+}
+
 const char *UDPC_atostr_cid(UDPC_HContext ctx, UDPC_ConnectionId connectionId) {
     return UDPC_atostr(ctx, connectionId.addr);
 }
index 4b35873efc7350de457b05265ee2331e930779ab..0a229cd1c34ee0b090c4f7750f3cd78069123276 100644 (file)
@@ -101,7 +101,8 @@ typedef enum {
     UDPC_ET_CONNECTED,
     UDPC_ET_DISCONNECTED,
     UDPC_ET_GOOD_MODE,
-    UDPC_ET_BAD_MODE
+    UDPC_ET_BAD_MODE,
+    UDPC_ET_REQUEST_CONNECT_PK
 } UDPC_EventType;
 
 typedef struct {
@@ -110,6 +111,7 @@ typedef struct {
     union Value {
         int dropAllWithAddr;
         int enableLibSodium;
+        unsigned char *pk;
     } v;
 } UDPC_Event;
 
@@ -133,11 +135,23 @@ UDPC_HContext UDPC_init_threaded_update_ms(
     int updateMS,
     int isUsingLibsodium);
 
+void UDPC_enable_threaded_update(UDPC_HContext ctx);
+void UDPC_enable_threaded_update_ms(UDPC_HContext ctx, int updateMS);
+void UDPC_disable_threaded_update(UDPC_HContext ctx);
+
 void UDPC_destroy(UDPC_HContext ctx);
 
 void UDPC_update(UDPC_HContext ctx);
 
-void UDPC_client_initiate_connection(UDPC_HContext ctx, UDPC_ConnectionId connectionId, int enableLibSodium);
+void UDPC_client_initiate_connection(
+    UDPC_HContext ctx,
+    UDPC_ConnectionId connectionId,
+    int enableLibSodium);
+
+void UDPC_client_initiate_connection_pk(
+    UDPC_HContext ctx,
+    UDPC_ConnectionId connectionId,
+    unsigned char *serverPK);
 
 void UDPC_queue_send(UDPC_HContext ctx, UDPC_ConnectionId destinationId,
                      int isChecked, void *data, uint32_t size);
@@ -170,6 +184,8 @@ UDPC_Event UDPC_get_event(UDPC_HContext ctx, unsigned long *remaining);
 
 UDPC_PacketInfo UDPC_get_received(UDPC_HContext ctx, unsigned long *remaining);
 
+void UDPC_set_libsodium_keys(UDPC_HContext ctx, unsigned char *sk, unsigned char *pk);
+
 const char *UDPC_atostr_cid(UDPC_HContext ctx, UDPC_ConnectionId connectionId);
 
 const char *UDPC_atostr(UDPC_HContext ctx, UDPC_IPV6_ADDR_TYPE addr);