diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 5a3921eaf2670ed4921e522b5b8c25b541019939..9af8d012e8ec3c42ff3458ac7af1726aec898cda 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -15,9 +15,9 @@ build:linux:
   #- DEBIAN_FRONTEND=noninteractive apt-get -y upgrade
   #- DEBIAN_FRONTEND=noninteractive apt-get -y dist-upgrade
   - DEBIAN_FRONTEND=noninteractive apt-get -y install build-essential gcc clang cmake autoconf automake libtool-bin gperf gengetopt pkg-config curl wget gzip tar doxygen graphviz unzip
-  - DEBIAN_FRONTEND=noninteractive apt-get -y install libgl-dev libegl-dev libpcre2-dev libxkbcommon-dev libmd4c-dev libdbus-1-dev 
+  - DEBIAN_FRONTEND=noninteractive apt-get -y install libgl-dev libegl-dev libpcre2-dev libxkbcommon-dev libmd4c-dev libdbus-1-dev libbrotli-dev libzstd-dev zlib1g-dev
 
-  - curl -s -o artifacts.zip https://git.ralph.or.at/causal/causal-cpp-contrib/-/jobs/351/artifacts/download
+  - curl -s -o artifacts.zip https://git.ralph.or.at/causal/causal-cpp-contrib/-/jobs/1138/artifacts/download
   - unzip -q artifacts.zip
 
   script:
@@ -78,7 +78,7 @@ build:android:
   #- DEBIAN_FRONTEND=noninteractive apt-get -y upgrade
   #- DEBIAN_FRONTEND=noninteractive apt-get -y dist-upgrade
   - DEBIAN_FRONTEND=noninteractive apt-get -y install build-essential cmake autoconf automake libtool-bin gperf gengetopt pkg-config curl wget gzip tar doxygen graphviz default-jdk unzip
-  - curl -s -o artifacts.zip https://git.ralph.or.at/causal/causal-cpp-contrib/-/jobs/351/artifacts/download
+  - curl -s -o artifacts.zip https://git.ralph.or.at/causal/causal-cpp-contrib/-/jobs/1138/artifacts/download
   - unzip -q artifacts.zip
   
   - cd tool/android/ndk/25.2.9519653/toolchains/llvm/prebuilt
@@ -109,7 +109,7 @@ doc:
   #- DEBIAN_FRONTEND=noninteractive apt-get -y dist-upgrade
   - DEBIAN_FRONTEND=noninteractive apt-get -y install build-essential gcc clang clang-tools cmake autoconf automake doxygen graphviz pkg-config curl wget unzip
   
-  - curl -s -o artifacts.zip https://git.ralph.or.at/causal/causal-cpp-contrib/-/jobs/351/artifacts/download
+  - curl -s -o artifacts.zip https://git.ralph.or.at/causal/causal-cpp-contrib/-/jobs/1138/artifacts/download
   - unzip -q artifacts.zip
   
   script:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ca465bb4c22ed6dfb11c764bca8f12769afd4923..a589a45a03a0ee170f988af7eef3fdcbc3e1bec2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -92,3 +92,4 @@
 * #000010 Filesystem Junction
 * #000012 Memory Junction
 * #000006 added Web Application Server
+* #000015 Replace OpenDHT based crypto implementation for core functions with a GnuTLS based
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f8959f352cbead9ff4b09db65a917bc579d6dd57..f6a0ac8d3696820bbe19b793e69832d4c25e7e9d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -47,6 +47,8 @@ endif()
 find_package(Boost 1.74 REQUIRED COMPONENTS log stacktrace_basic)
 add_definitions(-DWITH_STACKTRACE)
 
+pkg_check_modules(GnuTLS REQUIRED gnutls>=3.7)
+
 add_library(causal-core src/trace.cpp
                         src/core/wirks.cpp
                         src/sync/util.cpp
@@ -103,9 +105,10 @@ install(TARGETS causal-core COMPONENT core DESTINATION lib)
 
 add_library(causal-data src/data/junction.cpp
                         src/data/memory.cpp
+                        src/data/crypto.cpp
                         src/data/channel.cpp
                         src/data/fs.cpp)
-target_link_libraries(causal-data causal-core)
+target_link_libraries(causal-data causal-core ${GnuTLS_LIBRARIES})
 install(FILES include/causal/data/channel.hpp
               include/causal/data/junction.hpp
               include/causal/data/memory.hpp
@@ -300,9 +303,8 @@ if(GTEST_FOUND)
         add_causal_test(NAME test_causal-data_opendht SOURCES test/data/opendht.cpp LIBS causal-data-opendht)
     endif()
     
-    if(Msgpack_FOUND AND OpenDHT_FOUND AND LMDB_FOUND)
-        # TODO: LMDB crypto Tests are powered by crypto API of OpenDHT => replace using proper GNUTLS implementation
-        add_causal_test(NAME test_causal-data_lmdb SOURCES test/data/lmdb.cpp LIBS causal-data-junction-lmdb causal-msgpack causal-data-opendht)
+    if(Msgpack_FOUND AND LMDB_FOUND)
+        add_causal_test(NAME test_causal-data_lmdb SOURCES test/data/lmdb.cpp LIBS causal-data-junction-lmdb causal-msgpack)
     endif()
 
     if(OpenSSL_FOUND AND LIBMICROHTTPD_FOUND)
diff --git a/include/causal/data/crypto.hpp b/include/causal/data/crypto.hpp
index 1cfb607f09c878155354d40a350a8ca2480321f7..95d5b56fc47647c64ba7c3b3aff8c89fe23ef022 100644
--- a/include/causal/data/crypto.hpp
+++ b/include/causal/data/crypto.hpp
@@ -6,6 +6,9 @@
 #include <memory>
 #include <map>
 
+#include <gnutls/crypto.h>
+#include <gnutls/gnutls.h>
+
 #include "causal/trace.hpp"
 
 #include "causal/core/aspect.hpp"
@@ -93,6 +96,37 @@ namespace causal {
             Certificate certificate;
         };
 
+        EXCEPTION_ERROR(gnutls_error, "GnuTLS error")
+
+        class gnutls_init final {
+        private:
+            // Private constructor to prevent instantiation from outside
+            gnutls_init();
+
+            // Singleton instance
+            static gnutls_init* instance;
+
+            // Flag for thread-safe initialization
+            static std::once_flag initFlag;
+
+            // Method to initialize the library if not already initialized
+            static void _init();
+            
+        public:
+            ~gnutls_init();
+
+            // Delete copy constructor and assignment operator to prevent copying
+            gnutls_init(const gnutls_init&) = delete;
+            void operator=(const gnutls_init&) = delete;
+
+            // Public method to access the singleton instance
+            static void init();
+        };
+
+        std::string gnutls_derrive_sha_key(const std::string& password, u_short key_length);
+
+        EXCEPTION_ERROR(sha_hash_lenght_invalid, "hash_length should be larger than or equal to 256")
+
         /// @brief abstract base of hasher
         class hasher {
         public:
@@ -101,7 +135,15 @@ namespace causal {
              * @param hash_length requested SHA length
              * @return SHA hash of data
              */
-            virtual std::string hash_sha(const std::string& data, u_short hash_length=DEFAULT_SHA_HASH_LENGTH) const = 0;
+            virtual std::string hash(const std::string& data, u_short hash_length=DEFAULT_SHA_HASH_LENGTH) const = 0;
+        };
+
+        /// @brief GnuTLS backed SHA crypto hasher
+        class gnutls_sha_hasher final : public hasher {
+        public:
+            gnutls_sha_hasher();
+
+            std::string hash(const std::string& data, u_short hash_length=DEFAULT_SHA_HASH_LENGTH) const override;
         };
 
         /// @brief abstract base of crypto provider
@@ -273,8 +315,10 @@ namespace causal {
         };
 
         /// @brief abstract base of symmetric coder
-        class crypto_coder {
+        class cipher {
         public:
+            virtual ~cipher() = default;
+
             /** @brief get hasher using coders backend
              * @return hasher to use
              */
@@ -299,6 +343,23 @@ namespace causal {
             virtual std::string decrypt(const std::string& encrypted_data) const = 0;
         };
 
+        /// @brief GnuTLS backed AES crypto coder
+        class gnutls_aes_cipher final : public cipher {
+        private:
+            const std::string key, iv;
+            const gnutls_cipher_algorithm_t algo;
+
+        public:
+            const u_short key_length;
+
+            gnutls_aes_cipher(const std::string& password, const u_short key_length = 256);
+
+            const hasher& get_hasher() const override;
+            const std::string get_token() const override;
+            std::string encrypt(const std::string& data) const override;
+            std::string decrypt(const std::string& encrypted_data) const override;
+        };
+
         /// @brief basic metadata for channels supporting identification
         struct IdentifiedMessageMeta : MessageMeta {
             PublicIdentity owner_id;
diff --git a/include/causal/data/lmdb.hpp b/include/causal/data/lmdb.hpp
index 8b9d763162a64e699477f417462b9f61805e6e1b..a1ecef73a9444dba8980ce037891f54a98610bed 100644
--- a/include/causal/data/lmdb.hpp
+++ b/include/causal/data/lmdb.hpp
@@ -3,10 +3,12 @@
 
 #pragma once
 
+#include <memory>
 #include <string>
 #include <map>
 
 #include <lmdb.h>
+#include <type_traits>
 
 #include "junction.hpp"
 #include "crypto.hpp"
@@ -38,7 +40,7 @@ namespace causal {
          */
         class lmdb_junction final : public junction {
         private:
-            const std::shared_ptr<const crypto_coder> coder;
+            const std::unique_ptr<const cipher> ciph;
 
             MDB_dbi db;
             MDB_env* env = nullptr;
@@ -71,14 +73,22 @@ namespace causal {
             lmdb_junction(const std::string path, const std::string name = std::string(), const mode_t create_mode = 0600, const size_t map_size = 10485760);
 
             /** @brief constructs encrypting junction for given LMDB
-             * @param coder to use for symmetric encryption
+             * @param ciph cipher to use for symmetric encryption
              * @param path to LMDB file
              * @param name of database, empty for using unnamed database
              * @param create_mode filemode to use when creating new LMDB file
              * @param map_size to use for mapping data
              * @remark changing map size requires to reload database file and is not supported yet
              */
-            lmdb_junction(const std::shared_ptr<const crypto_coder> coder, const std::string path, const std::string name = std::string(), const mode_t create_mode = 0600, const size_t map_size = 10485760);
+            template<typename cipherT>
+            lmdb_junction(cipherT&& ciph, const std::string path, const std::string name = std::string(),
+                          const mode_t create_mode = 0600, const size_t map_size = 10485760)
+            requires(std::is_base_of_v<cipher, cipherT>)
+            : ciph(std::make_unique<cipherT>(std::forward<cipherT>(ciph))) {
+                this->initialize(path, name, create_mode);
+                this->open(name, map_size);
+                this->check_crypto_token();
+            }
             ~lmdb_junction();
 
             void attract(junction_space& s, const std::string id) override;
diff --git a/include/causal/data/opendht.hpp b/include/causal/data/opendht.hpp
index 42e90ee7a95b2454d456ed76b68ffb555682b152..45ce37b4c3bdfc97ba437c640dba15d5dd696b4b 100644
--- a/include/causal/data/opendht.hpp
+++ b/include/causal/data/opendht.hpp
@@ -25,10 +25,10 @@ namespace causal {
     namespace data {
         MAKE_TRACE_SCOPE(scope_opendht)
 
-        /// @brief OpenDHT backed crypto hasher
-        class opendht_hasher final : public hasher {
+        /// @brief OpenDHT backed SHA crypto hasher
+        class opendht_sha_hasher final : public hasher {
         public:
-            std::string hash_sha(const std::string& data, u_short hash_length=DEFAULT_SHA_HASH_LENGTH) const override;
+            std::string hash(const std::string& data, u_short hash_length=DEFAULT_SHA_HASH_LENGTH) const override;
         };
 
         /// @brief OpenDHT backed crypto provider
@@ -79,12 +79,14 @@ namespace causal {
         };
 
         /// @brief OpenDHT backed AES crypto coder
-        class opendht_aes_coder final : public crypto_coder {
+        class opendht_aes_cipher final : public cipher {
         private:
             const dht::Blob key;
 
         public:
-            opendht_aes_coder(const std::string& password, const u_short key_length = 32);
+            const u_short key_length;
+
+            opendht_aes_cipher(const std::string& password, const u_short key_length = 256);
 
             const hasher& get_hasher() const override;
             const std::string get_token() const override;
diff --git a/src/data/crypto.cpp b/src/data/crypto.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d887a4ee0429b3b849f5c318228bdada37e3ace9
--- /dev/null
+++ b/src/data/crypto.cpp
@@ -0,0 +1,284 @@
+// Licensed under the terms of the AGPL-3.0-only (GNU AFFERO GENERAL PUBLIC LICENSE version 3)
+// Copyright (C) 2018-2024 by Ralph Alexander Bariz
+
+#include "causal/data/crypto.hpp"
+#include "causal/trace.hpp"
+
+#include <sstream>
+
+#include <gnutls/crypto.h>
+#include <gnutls/gnutls.h>
+#include <string>
+
+namespace causal {
+    namespace data {
+        gnutls_init* gnutls_init::instance = nullptr;
+        std::once_flag gnutls_init::initFlag;
+        gnutls_init::~gnutls_init() {
+            gnutls_global_deinit();
+        }
+
+        void gnutls_init::init() {
+            std::call_once(initFlag, &gnutls_init::_init);
+        }
+        
+        gnutls_init::gnutls_init() {
+            gnutls_global_init();
+            TRACE(scope_data) << "GNU TLS library successfully initialized.";
+        }
+        
+        void gnutls_init::_init() {
+            gnutls_init::instance = new gnutls_init();
+        }
+
+        gnutls_sha_hasher::gnutls_sha_hasher() {
+            gnutls_init::init();
+        }
+
+        std::string gnutls_sha_hasher::hash(const std::string& data, u_short hash_length) const {
+            gnutls_digest_algorithm_t algorithm;
+
+            if (hash_length >= 512) {
+                algorithm = GNUTLS_DIG_SHA512;
+            } else if (hash_length >= 384) {
+                algorithm = GNUTLS_DIG_SHA384;
+            } else if (hash_length >= 256) {
+                algorithm = GNUTLS_DIG_SHA256;
+            } else {
+                throw sha_hash_lenght_invalid();
+            }
+            size_t digest_size = gnutls_hash_get_len(algorithm);
+
+            unsigned char *digest = new unsigned char[digest_size];
+
+            // Hash initialization
+            gnutls_hash_hd_t handle;
+            if (gnutls_hash_init(&handle, algorithm) < 0) {
+                delete[] digest;
+                throw gnutls_error("gnutls_hash_init failed");
+            }
+
+            // Input the data to be hashed
+            if (gnutls_hash(handle, data.data(), data.length()) < 0) {
+                gnutls_hash_deinit(handle, NULL);
+                delete[] digest;
+                throw gnutls_error("gnutls_hash failed");
+            }
+
+            // Output hash value
+            gnutls_hash_output(handle, digest);
+
+            // Free and deinitialize handle
+            gnutls_hash_deinit(handle , NULL);
+
+            // Convert binary hash value to hexadecimal string
+            std::ostringstream oss;
+            for (size_t i = 0; i < digest_size; i++)
+                oss << static_cast<char>(digest[i]);
+
+            delete[] digest;
+            return oss.str();
+        }
+
+        // Generate a random Initialization Vector of length 16 bytes
+        std::string generate_iv() {
+            unsigned char iv[16]; // AES requires a 128-bit IV
+            
+            int ret = gnutls_rnd(GNUTLS_RND_NONCE, iv, sizeof(iv));
+            if (ret < 0) {
+                throw gnutls_error("Generating random nonce failed.");
+            }
+
+            return std::string((const char *)iv, sizeof(iv));
+        }
+
+        std::string gnutls_derrive_key_sha(const std::string& password, u_short key_length) {
+            gnutls_mac_algorithm_t algorithm;
+
+            if (key_length >= 512) {
+                algorithm = GNUTLS_MAC_SHA512;
+            } else if (key_length >= 384) {
+                algorithm = GNUTLS_MAC_SHA384;
+            } else if (key_length >= 256) {
+                algorithm = GNUTLS_MAC_SHA256;
+            } else {
+                throw sha_hash_lenght_invalid();
+            }
+            
+            // Choose suitable this->algo based on key length
+            u_char derived_key[key_length / 8];
+            const char *key = (const char *)(&derived_key[0]);
+            void *digest = derived_key;
+
+            gnutls_hmac_hd_t hmac_handle;
+            
+            int ret = gnutls_hmac_init(&hmac_handle, algorithm, (const u_char *)password.data(), password.size());
+            if (ret < 0) {
+                throw gnutls_error("Init HMAC failed.");
+            }
+
+            ret = gnutls_hmac(hmac_handle, nullptr, 0); // Use empty input as we just need the key derivation
+            if (ret < 0) {
+                throw gnutls_error("Computing HMAC failed.");
+            }
+
+            gnutls_hmac_output(hmac_handle, digest);
+            
+            gnutls_hmac_deinit(hmac_handle, digest);
+
+            return std::string(key, key_length / 8);
+        }
+
+        gnutls_aes_cipher::gnutls_aes_cipher(const std::string& pw, const u_short key_length)
+        : key(gnutls_derrive_key_sha(pw, key_length)), iv(generate_iv()),
+          algo((key_length == 128) ? GNUTLS_CIPHER_AES_128_CBC :
+               (key_length == 192) ? GNUTLS_CIPHER_AES_192_CBC :
+               (key_length == 256) ? GNUTLS_CIPHER_AES_256_CBC : GNUTLS_CIPHER_UNKNOWN),
+          key_length(key_length) {
+            if (this->algo == GNUTLS_CIPHER_UNKNOWN) {
+                throw gnutls_error("Unsupported key length for AES encryption.");
+            }
+            gnutls_init::init();
+        }
+        
+        const hasher& gnutls_aes_cipher::get_hasher() const {
+            static const gnutls_sha_hasher h;
+            return h;
+        }
+
+        const std::string gnutls_aes_cipher::get_token() const {
+            return this->get_hasher().hash(this->key, this->key_length);
+        }
+
+        // Padding function for PKCS#7
+        std::string pkcs7_pad(const std::string &data, const unsigned int block_size = 16) {
+            // Determine the number of padding bytes needed
+            size_t padding_length = (block_size - (data.size() % block_size));
+            
+            // Create a new string that is a copy of data and appends the required padding bytes
+            std::string padded_data = data;
+            for(unsigned int i=0; i<padding_length; ++i){
+                padded_data.push_back(static_cast<char>(padding_length));
+            }
+            return padded_data;
+        }
+        
+        // Unpadding function for PKCS#7
+        std::string pkcs7_unpad(const std::string &data, const unsigned int block_size = 16) {
+            // Check that the data is a multiple of the block size
+            if(data.size() % block_size != 0){
+                throw gnutls_error("Data length is not a multiple of the block size.");
+            }
+            
+            // Get the padding byte from the end of the string
+            unsigned char padding_length = static_cast<unsigned char>(data.back());
+            
+            // Validate the padding length (must be within range)
+            if(padding_length > data.size() || padding_length == 0){
+                throw gnutls_error("Invalid PKCS#7 padding.");
+            }
+            
+            // Remove the padding bytes
+            return data.substr(0, data.size() - padding_length);
+        }
+
+        std::string gnutls_aes_cipher::encrypt(const std::string& data) const {
+            // Create a cipher handle using the desired key length for AES encryption
+            gnutls_cipher_hd_t cipher_handle;
+
+            gnutls_datum_t _key{.data = (u_char *)this->key.data(), .size = static_cast<u_int>(this->key.size())};
+            gnutls_datum_t _iv{.data = (u_char *)this->iv.data(), .size = static_cast<u_int>(this->iv.size())};
+            
+            try {
+                //TRACE(scope_data) << "encrypt->data: " << std::to_string(data.size()) << " | " << data;
+                int ret = gnutls_cipher_init(&cipher_handle, this->algo, &_key, &_iv);
+                if (ret < 0) {
+                    throw gnutls_error("Error initializing cipher handle.");
+                }
+
+                // Pad the data to a multiple of the block size (16 bytes for AES)
+                const std::string padded_data = pkcs7_pad(data);
+                const size_t padded_data_length = padded_data.size();
+
+                //TRACE(scope_data) << "encrypt->padded_data: " << std::to_string(padded_data.size()) << " | " << padded_data;
+
+                // Allocate memory for the ciphertext
+                // Insert the IV at the beginning of the ciphertext
+                std::string ciphertext;
+                ciphertext.resize(padded_data_length);
+
+                // Encrypt the data
+                ret = gnutls_cipher_encrypt2(cipher_handle, (const u_char *)padded_data.data(),
+                                            padded_data_length, (u_char *)ciphertext.data(), padded_data_length);
+                if (ret < 0) {
+                    throw gnutls_error("Error during encryption: "+std::to_string(ret));
+                }
+
+                //TRACE(scope_data) << "encrypt->ciphertext: " << std::to_string(ciphertext.size()) << " | " << ciphertext;
+                
+                const auto encrypted_data = this->iv + ciphertext;
+
+                //TRACE(scope_data) << "encrypt->encrypted_data: " << std::to_string(encrypted_data.size()) << " | " << encrypted_data;
+
+                // Clean up resources
+                gnutls_cipher_deinit(cipher_handle);
+
+                return encrypted_data;
+            } catch (...) {
+                gnutls_cipher_deinit(cipher_handle);
+                throw;  // Re-throw with original exception message.
+            }
+        }
+
+        std::string gnutls_aes_cipher::decrypt(const std::string& encrypted_data) const {
+            gnutls_cipher_hd_t cipher_handle;
+
+            try {
+                //TRACE(scope_data) << "decrypt->encrypted_data: " << std::to_string(encrypted_data.size()) << " | " << encrypted_data;
+                // 1. Extract IV from prefix of ciphertext
+                if(encrypted_data.size() < this->iv.size()) { throw gnutls_error("Ciphertext must include an IV."); }
+
+                gnutls_datum_t _key{.data = (u_char *)this->key.data(), .size = static_cast<u_int>(this->key.size())};
+                const std::string iv_str = encrypted_data.substr(0, this->iv.size());
+                const gnutls_datum_t _iv = {.data = (u_char *)iv_str.data(), .size = static_cast<u_int>(iv_str.size())};
+                
+                // 3. Set up the Cipher handle for AES decryption
+                int ret = gnutls_cipher_init(&cipher_handle, this->algo, &_key, &_iv);
+                if (ret < 0) {
+                    throw gnutls_error("Error initializing cipher handle.");
+                }
+
+                const auto ciphertext = encrypted_data.substr(_iv.size);
+
+                //TRACE(scope_data) << "decrypt->ciphertext: " << std::to_string(ciphertext.size()) << " | " << ciphertext;
+
+                const size_t padded_data_length = ciphertext.size();
+                std::string padded_data;
+                padded_data.resize(padded_data_length);
+
+                ret = gnutls_cipher_decrypt2(cipher_handle,
+                                        (const u_char *)ciphertext.data(), 
+                                        padded_data_length,
+                                        (u_char *)padded_data.data(),
+                                        padded_data_length);
+                if (ret < 0) {
+                    throw gnutls_error("AES decryption failed.");
+                }
+
+                //TRACE(scope_data) << "decrypt->padded_data: " << std::to_string(padded_data.size()) << " | " << padded_data;
+
+                const auto data = pkcs7_unpad(padded_data);
+
+                //TRACE(scope_data) << "decrypt->data: " << std::to_string(data.size()) << " | " << data;
+
+                // Clean up handle after use
+                gnutls_cipher_deinit(cipher_handle);
+
+                return data;
+            } catch (const std::exception& e) {
+                gnutls_cipher_deinit(cipher_handle);
+                throw;  // Re-throw with original exception message.
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/data/httpd.cpp b/src/data/httpd.cpp
index f4a5712fc0b0974e7f6a4340dfb692dc8dccc1b8..05fddf8949a7f690f83bafecba9eb2bcb7b42b1c 100644
--- a/src/data/httpd.cpp
+++ b/src/data/httpd.cpp
@@ -10,7 +10,6 @@
 #include <filesystem>
 #include <microhttpd.h>
 #include <mutex>
-#include <nlohmann/json_fwd.hpp>
 #include <shared_mutex>
 #include <sstream>
 #include <random>
diff --git a/src/data/lmdb.cpp b/src/data/lmdb.cpp
index c4b38c98aa8d4eb369e8c0cabe7fbf7d89a4dc62..1972bc15a3eb9837984f93522ff017e580145e48 100644
--- a/src/data/lmdb.cpp
+++ b/src/data/lmdb.cpp
@@ -1,8 +1,10 @@
 // Licensed under the terms of the AGPL-3.0-only (GNU AFFERO GENERAL PUBLIC LICENSE version 3)
 // Copyright (C) 2018-2024 by Ralph Alexander Bariz
 
+#include <memory>
 #include <vector>
 
+#include "causal/data/crypto.hpp"
 #include "causal/trace.hpp"
 
 #include "causal/data/lmdb.hpp"
@@ -18,12 +20,13 @@ namespace causal {
             this->check_crypto_token();
         }
 
-        lmdb_junction::lmdb_junction(const std::shared_ptr<const crypto_coder> coder, const std::string path, const std::string name, const mode_t create_mode, const size_t map_size)
-        : coder(std::move(coder)) {
+        /*lmdb_junction::lmdb_junction(std::unique_ptr<const cipher>&& ciph, const std::string path, const std::string name,
+                                     const mode_t create_mode, const size_t map_size)
+        : ciph(std::forward<std::unique_ptr<const cipher>>(ciph)) {
             this->initialize(path, name, create_mode);
             this->open(name, map_size);
             this->check_crypto_token();
-        }
+        }*/
 
         lmdb_junction::~lmdb_junction() {
             std::unique_lock l(this->mtx);
@@ -98,7 +101,7 @@ namespace causal {
 
         void lmdb_junction::check_crypto_token() {
             const std::string key("crypto"),
-                              expected = this->coder ? this->coder->get_token() : std::string("plain");
+                              expected = this->ciph ? this->ciph->get_token() : std::string("plain");
             std::string given;
             auto* txn = this->begin_txn();
             MDB_val idVal = {.mv_size = key.size(), .mv_data = (void*)key.data()}, data;
@@ -208,25 +211,25 @@ namespace causal {
         void lmdb_junction::repel(const junction_space& s, const std::string id) {}
 
         MDB_val lmdb_junction::get_hashed_id(const std::string& id, std::string& buf) const {
-            if(this->coder) {
-                buf = this->coder->get_hasher().hash_sha(id);
+            if(this->ciph) {
+                buf = this->ciph->get_hasher().hash(id);
                 return {.mv_size = buf.size(), .mv_data = (void*)buf.data()};
             } else
                 return {.mv_size = id.size(), .mv_data = (void*)id.data()};
         }
 
         MDB_val lmdb_junction::get_crypt_data(const std::string& data, std::string& buf) const {
-            if(this->coder) {
-                buf = this->coder->encrypt(data);
+            if(this->ciph) {
+                buf = this->ciph->encrypt(data);
                 return {.mv_size = buf.size(), .mv_data = (void*)buf.data()};
             } else
                 return {.mv_size = data.size(), .mv_data = (void*)data.data()};
         }
 
         std::string lmdb_junction::get_decrypt_data(const MDB_val val) const {
-            if(this->coder) {
+            if(this->ciph) {
                 std::string encrypted_data((char*)val.mv_data, val.mv_size);
-                return this->coder->decrypt(encrypted_data);
+                return this->ciph->decrypt(encrypted_data);
             } else
                 return std::string((char*)val.mv_data, val.mv_size);
         }
@@ -356,7 +359,7 @@ namespace causal {
 
         bool lmdb_junction::iterate(std::function<bool(const std::string&, const std::string&)> f) const {
             // id is hashed why restoring it is impossible, however it would pose a security risk so do not even try
-            ASSERT_MSG(scope_lmdb, !this->coder, "cannot iterate encrypted database")
+            ASSERT_MSG(scope_lmdb, !this->ciph, "cannot iterate encrypted database")
             std::shared_lock l(this->mtx);
             auto* txn = this->begin_txn(true);
             MDB_cursor* cur;
diff --git a/src/data/opendht.cpp b/src/data/opendht.cpp
index 5afe0dd486983cf13cf36ca37a0ae1e2667dcb72..c57994990ed1a575092e12e2afb22dc96b4379e8 100644
--- a/src/data/opendht.cpp
+++ b/src/data/opendht.cpp
@@ -11,7 +11,7 @@ namespace causal {
     namespace data {
         INIT_TRACE_SCOPE(scope_opendht, "opendht")
 
-        std::string opendht_hasher::hash_sha(const std::string& data, u_short hash_length) const {
+        std::string opendht_sha_hasher::hash(const std::string& data, u_short hash_length) const {
             dht::Blob d(data.begin(), data.end());
             auto h = dht::crypto::hash(d, hash_length);
             return std::string(h.begin(), h.end());
@@ -30,7 +30,7 @@ namespace causal {
         }
         
         const hasher& opendht_crypto_provider::get_hasher() const {
-            static const opendht_hasher h;
+            static const opendht_sha_hasher h;
             return h;
             //return this->hasher_;
         }
@@ -329,26 +329,25 @@ namespace causal {
             return std::string(data.begin(), data.end());
         }
 
-        opendht_aes_coder::opendht_aes_coder(const std::string& pw, const u_short key_length)
-        : key(dht::crypto::hash(dht::Blob(pw.begin(), pw.end()), key_length)) {}
+        opendht_aes_cipher::opendht_aes_cipher(const std::string& pw, const u_short key_length)
+        : key(dht::crypto::hash(dht::Blob(pw.begin(), pw.end()), key_length / 8)), key_length(key_length) {}
         
-        const hasher& opendht_aes_coder::get_hasher() const {
-            static const opendht_hasher h;
+        const hasher& opendht_aes_cipher::get_hasher() const {
+            static const opendht_sha_hasher h;
             return h;
-            //return this->hasher_;
         }
 
-        const std::string opendht_aes_coder::get_token() const {
-            auto blob = dht::crypto::hash(this->key, 20);
+        const std::string opendht_aes_cipher::get_token() const {
+            auto blob = dht::crypto::hash(this->key, this->key_length / 8);
             return std::string(blob.begin(), blob.end());
         }
 
-        std::string opendht_aes_coder::encrypt(const std::string& data) const {
+        std::string opendht_aes_cipher::encrypt(const std::string& data) const {
             auto e = dht::crypto::aesEncrypt(dht::Blob(data.begin(), data.end()), this->key);
             return std::string(e.begin(), e.end());
         }
 
-        std::string opendht_aes_coder::decrypt(const std::string& encrypted_data) const {
+        std::string opendht_aes_cipher::decrypt(const std::string& encrypted_data) const {
             auto d = dht::crypto::aesDecrypt(dht::Blob(encrypted_data.begin(), encrypted_data.end()), this->key);
             return std::string(d.begin(), d.end());
         }
diff --git a/test/data/httpd.cpp b/test/data/httpd.cpp
index 901caead78e01714d99d052cebe74b7d9c41bd7b..83c3777dd2a18f0984b9c685787b33e73ece5578 100644
--- a/test/data/httpd.cpp
+++ b/test/data/httpd.cpp
@@ -3,7 +3,6 @@
 
 #include <gtest/gtest.h>
 #include <memory>
-#include <nlohmann/json_fwd.hpp>
 
 #include "causal/core/aspect.hpp"
 #include "causal/core/wirks.hpp"
diff --git a/test/data/lmdb.cpp b/test/data/lmdb.cpp
index e6e817a6633d197eb7e7dbb31cd16cce4a5d63ae..0e84285af58ef7e772dfb41a7623422173ae7846 100644
--- a/test/data/lmdb.cpp
+++ b/test/data/lmdb.cpp
@@ -11,8 +11,8 @@
 #include <stdio.h>
 
 #include "causal/data/junction.hpp"
+#include "causal/data/crypto.hpp"
 #include "causal/data/msgpack.hpp"
-#include "causal/data/opendht.hpp"
 #include "causal/data/lmdb.hpp"
 #include "causal/facet/common.hpp"
 #include "causal/facet/spreads.hpp"
@@ -283,14 +283,11 @@ TEST(causal_data_lmdb_junction, round_trip) {
 TEST(causal_data_lmdb_junction, crypted_round_trip) {
     std::string pwd1("password1"), pwd2("password2");
     remove("/tmp/testlmdb_crypted");
-    {   const std::shared_ptr<const cd::crypto_coder> c(
-            new cd::opendht_aes_coder(pwd1)
-        );
-        auto j1 = std::make_shared<cd::lmdb_junction>(c, "/tmp/testlmdb_crypted", "test1");
+    {    auto j1 = std::make_shared<cd::lmdb_junction>(cd::gnutls_aes_cipher(pwd1), "/tmp/testlmdb_crypted", "test1");
         auto s1 = std::make_shared<cd::msgpack_space>(j1);
         s1->register_types<test_other_1, test_other_2>();
         
-        auto j2 = std::make_shared<cd::lmdb_junction>(c, "/tmp/testlmdb_crypted", "test2");
+        auto j2 = std::make_shared<cd::lmdb_junction>(cd::gnutls_aes_cipher(pwd1), "/tmp/testlmdb_crypted", "test2");
         auto s2 = std::make_shared<cd::msgpack_space>(j2);
         s2->register_types<test_other_1, test_other_2>();
 
@@ -301,14 +298,11 @@ TEST(causal_data_lmdb_junction, crypted_round_trip) {
         round_trip_check(*s2, "bar");
     }
 
-    {   const std::shared_ptr<const cd::crypto_coder> c(
-            new cd::opendht_aes_coder(pwd1)
-        );
-        auto j1 = std::make_shared<cd::lmdb_junction>(c, "/tmp/testlmdb_crypted", "test1");
+    {   auto j1 = std::make_shared<cd::lmdb_junction>(cd::gnutls_aes_cipher(pwd1), "/tmp/testlmdb_crypted", "test1");
         auto s1 = std::make_shared<cd::msgpack_space>(j1);
         s1->register_types<test_other_1, test_other_2>();
         
-        auto j2 = std::make_shared<cd::lmdb_junction>(c, "/tmp/testlmdb_crypted", "test2");
+        auto j2 = std::make_shared<cd::lmdb_junction>(cd::gnutls_aes_cipher(pwd1), "/tmp/testlmdb_crypted", "test2");
         auto s2 = std::make_shared<cd::msgpack_space>(j2);
         s2->register_types<test_other_1, test_other_2>();
         
@@ -316,11 +310,9 @@ TEST(causal_data_lmdb_junction, crypted_round_trip) {
         round_trip_check(*s2, "bar");
     }
 
-    {   const std::shared_ptr<const cd::crypto_coder> c(
-            new cd::opendht_aes_coder(pwd2)
-        );
-        EXPECT_THROW(std::make_shared<cd::lmdb_junction>(c, "/tmp/testlmdb_crypted", "test1"), cd::lmdb_crypto_token_mismatch);
-        EXPECT_THROW(std::make_shared<cd::lmdb_junction>(c, "/tmp/testlmdb_crypted", "test2"), cd::lmdb_crypto_token_mismatch);
+    {
+        EXPECT_THROW(std::make_shared<cd::lmdb_junction>(cd::gnutls_aes_cipher(pwd2), "/tmp/testlmdb_crypted", "test1"), cd::lmdb_crypto_token_mismatch);
+        EXPECT_THROW(std::make_shared<cd::lmdb_junction>(cd::gnutls_aes_cipher(pwd2), "/tmp/testlmdb_crypted", "test2"), cd::lmdb_crypto_token_mismatch);
     }
 
     {
@@ -331,10 +323,7 @@ TEST(causal_data_lmdb_junction, crypted_round_trip) {
     {   remove("/tmp/testlmdb_noncrypted");
         std::make_shared<cd::lmdb_junction>("/tmp/testlmdb_noncrypted");
         
-        const std::shared_ptr<const cd::crypto_coder> c(
-            new cd::opendht_aes_coder(pwd1)
-        );
-        EXPECT_THROW(std::make_shared<cd::lmdb_junction>(c, "/tmp/testlmdb_noncrypted"), cd::lmdb_crypto_token_mismatch);
+        EXPECT_THROW(std::make_shared<cd::lmdb_junction>(cd::gnutls_aes_cipher(pwd1), "/tmp/testlmdb_noncrypted"), cd::lmdb_crypto_token_mismatch);
     }
 }
 
diff --git a/test/data/opendht.cpp b/test/data/opendht.cpp
index f24c843155f8127135fad6eccef5e6d72e77c073..4cfb78ebd865c57e554b14a67056955bb8cb4e61 100644
--- a/test/data/opendht.cpp
+++ b/test/data/opendht.cpp
@@ -269,7 +269,7 @@ TEST(causal_data_opendht_crypto, crypto_encrypt_decrypt_msgpack) {
 TEST(causal_data_opendht_crypter, crypto_encrypt_decrypt) {
     std::string msg("message"),
                 pwd("password1");
-    cd::opendht_aes_coder c1(pwd), c2(pwd), c3("password2");
+    cd::opendht_aes_cipher c1(pwd), c2(pwd), c3("password2");
     auto encrypted_msg1 = c1.encrypt(msg);
     auto encrypted_msg2 = c2.encrypt(msg);
     EXPECT_NE(msg, encrypted_msg1);
@@ -290,7 +290,7 @@ TEST(causal_data_opendht_crypter, crypto_encrypt_decrypt_msgpack) {
 
     std::string msg(ss.str()),
                 pwd("password");
-    cd::opendht_aes_coder c1(pwd), c2(pwd);
+    cd::opendht_aes_cipher c1(pwd), c2(pwd);
     auto encrypted_msg1 = c1.encrypt(msg);
     auto encrypted_msg2 = c2.encrypt(msg);
     EXPECT_NE(msg, encrypted_msg1);
@@ -313,9 +313,9 @@ TEST(causal_data_opendht_crypter, crypto_hash) {
     std::string msg("message"),
                 pwd1("password1"),
                 pwd2("password2");
-    cd::opendht_aes_coder c1(pwd1), c2(pwd2);
-    auto hashed_msg1 = c1.get_hasher().hash_sha(msg);
-    auto hashed_msg2 = c2.get_hasher().hash_sha(msg);
+    cd::opendht_aes_cipher c1(pwd1), c2(pwd2);
+    auto hashed_msg1 = c1.get_hasher().hash(msg);
+    auto hashed_msg2 = c2.get_hasher().hash(msg);
     EXPECT_NE(msg, hashed_msg1);
     EXPECT_NE(msg, hashed_msg2);
     EXPECT_EQ(hashed_msg1, hashed_msg2);