From: Remi Gacogne Date: Mon, 18 Dec 2023 15:12:46 +0000 (+0100) Subject: dnsdist: Fall back to libcrypto for authenticated encryption X-Git-Tag: dnsdist-1.9.0-rc1~24^2~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=44850d6e00bc7a7279d83971d5e1b4c67e6ed434;p=thirdparty%2Fpdns.git dnsdist: Fall back to libcrypto for authenticated encryption We used to fall back to plain-text for console communications when libsodium was not available, which was not great. Now that we are also using the authenticated encryption module to secure our QUIC tokens, let's fall back to OpenSSL's Chacha20 Poly 1305 implementation instead. Note that, unfortunately, both implementations are not compatible so the console communication format will be different depending on whether libsodium is available. I believe this is still better than plain-text :) --- diff --git a/.github/actions/spell-check/allow.txt b/.github/actions/spell-check/allow.txt index 6575c877b8..71d60e7dd7 100644 --- a/.github/actions/spell-check/allow.txt +++ b/.github/actions/spell-check/allow.txt @@ -3335,7 +3335,6 @@ sockname sockowner socktype sodbc -sodcrypto sodiumsigners sokolov somedata diff --git a/.not-formatted b/.not-formatted index 0bd94aae3a..af886b0187 100644 --- a/.not-formatted +++ b/.not-formatted @@ -225,8 +225,6 @@ ./pdns/sillyrecords.cc ./pdns/snmp-agent.cc ./pdns/snmp-agent.hh -./pdns/sodcrypto.cc -./pdns/sodcrypto.hh ./pdns/sortlist.cc ./pdns/sortlist.hh ./pdns/speedtest.cc diff --git a/pdns/dnsdist-console.cc b/pdns/dnsdist-console.cc index 6461a92986..b814a3579b 100644 --- a/pdns/dnsdist-console.cc +++ b/pdns/dnsdist-console.cc @@ -43,7 +43,7 @@ #include "dolog.hh" #include "dnsdist.hh" #include "dnsdist-console.hh" -#include "sodcrypto.hh" +#include "dnsdist-crypto.hh" #include "threadname.hh" GlobalStateHolder g_consoleACL; @@ -171,9 +171,9 @@ static bool putMsgLen32(int fd, uint32_t len) } } -static ConsoleCommandResult sendMessageToServer(int fd, const std::string& line, SodiumNonce& readingNonce, SodiumNonce& writingNonce, const bool outputEmptyLine) +static ConsoleCommandResult sendMessageToServer(int fd, const std::string& line, dnsdist::crypto::authenticated::Nonce& readingNonce, dnsdist::crypto::authenticated::Nonce& writingNonce, const bool outputEmptyLine) { - string msg = sodEncryptSym(line, g_consoleKey, writingNonce); + string msg = dnsdist::crypto::authenticated::encryptSym(line, g_consoleKey, writingNonce); const auto msgLen = msg.length(); if (msgLen > std::numeric_limits::max()) { cerr << "Encrypted message is too long to be sent to the server, "<< std::to_string(msgLen) << " > " << std::numeric_limits::max() << endl; @@ -208,7 +208,7 @@ static ConsoleCommandResult sendMessageToServer(int fd, const std::string& line, msg.clear(); msg.resize(len); readn2(fd, msg.data(), len); - msg = sodDecryptSym(msg, g_consoleKey, readingNonce); + msg = dnsdist::crypto::authenticated::decryptSym(msg, g_consoleKey, readingNonce); cout << msg; cout.flush(); @@ -217,7 +217,7 @@ static ConsoleCommandResult sendMessageToServer(int fd, const std::string& line, void doClient(ComboAddress server, const std::string& command) { - if (!sodIsValidKey(g_consoleKey)) { + if (!dnsdist::crypto::authenticated::isValidKey(g_consoleKey)) { cerr << "The currently configured console key is not valid, please configure a valid key using the setKey() directive" << endl; return; } @@ -233,7 +233,7 @@ void doClient(ComboAddress server, const std::string& command) } SConnect(fd.getHandle(), server); setTCPNoDelay(fd.getHandle()); - SodiumNonce theirs, ours, readingNonce, writingNonce; + dnsdist::crypto::authenticated::Nonce theirs, ours, readingNonce, writingNonce; ours.init(); writen2(fd.getHandle(), ours.value.data(), ours.value.size()); @@ -875,7 +875,7 @@ static void controlClientThread(ConsoleConnection&& conn) setTCPNoDelay(conn.getFD()); - SodiumNonce theirs, ours, readingNonce, writingNonce; + dnsdist::crypto::authenticated::Nonce theirs, ours, readingNonce, writingNonce; ours.init(); readn2(conn.getFD(), theirs.value.data(), theirs.value.size()); writen2(conn.getFD(), ours.value.data(), ours.value.size()); @@ -899,7 +899,7 @@ static void controlClientThread(ConsoleConnection&& conn) line.resize(len); readn2(conn.getFD(), line.data(), len); - line = sodDecryptSym(line, g_consoleKey, readingNonce); + line = dnsdist::crypto::authenticated::decryptSym(line, g_consoleKey, readingNonce); string response; try { @@ -990,7 +990,7 @@ static void controlClientThread(ConsoleConnection&& conn) catch (const LuaContext::SyntaxErrorException& e) { response = "Error: " + string(e.what()) + ": "; } - response = sodEncryptSym(response, g_consoleKey, writingNonce); + response = dnsdist::crypto::authenticated::encryptSym(response, g_consoleKey, writingNonce); putMsgLen32(conn.getFD(), response.length()); writen2(conn.getFD(), response.c_str(), response.length()); } @@ -1017,7 +1017,7 @@ void controlThread(int fd, ComboAddress local) while ((sock = SAccept(acceptFD.getHandle(), client)) >= 0) { FDWrapper socket(sock); - if (!sodIsValidKey(g_consoleKey)) { + if (!dnsdist::crypto::authenticated::isValidKey(g_consoleKey)) { vinfolog("Control connection from %s dropped because we don't have a valid key configured, please configure one using setKey()", client.toStringWithPort()); continue; } diff --git a/pdns/dnsdist-lua.cc b/pdns/dnsdist-lua.cc index 9f3dc08bdd..aabff40454 100644 --- a/pdns/dnsdist-lua.cc +++ b/pdns/dnsdist-lua.cc @@ -39,6 +39,7 @@ #include "dnsdist-carbon.hh" #include "dnsdist-concurrent-connections.hh" #include "dnsdist-console.hh" +#include "dnsdist-crypto.hh" #include "dnsdist-dynblocks.hh" #include "dnsdist-discovery.hh" #include "dnsdist-ecs.hh" @@ -61,7 +62,6 @@ #include "doh.hh" #include "doq-common.hh" #include "dolog.hh" -#include "sodcrypto.hh" #include "threadname.hh" #ifdef HAVE_LIBSSL @@ -1106,7 +1106,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) } g_consoleEnabled = true; -#ifdef HAVE_LIBSODIUM +#if defined(HAVE_LIBSODIUM) || defined(HAVE_LIBCRYPTO) if (g_configurationDone && g_consoleKey.empty()) { warnlog("Warning, the console has been enabled via 'controlSocket()' but no key has been set with 'setKey()' so all connections will fail until a key has been set"); } @@ -1136,8 +1136,8 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) luaCtx.writeFunction("addConsoleACL", [](const std::string& netmask) { setLuaSideEffect(); -#ifndef HAVE_LIBSODIUM - warnlog("Allowing remote access to the console while libsodium support has not been enabled is not secure, and will result in cleartext communications"); +#if !defined(HAVE_LIBSODIUM) && !defined(HAVE_LIBCRYPTO) + warnlog("Allowing remote access to the console while neither libsodium not libcrypto support has been enabled is not secure, and will result in cleartext communications"); #endif g_consoleACL.modify([netmask](NetmaskGroup& nmg) { nmg.addMask(netmask); }); @@ -1146,8 +1146,8 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) luaCtx.writeFunction("setConsoleACL", [](LuaTypeOrArrayOf inp) { setLuaSideEffect(); -#ifndef HAVE_LIBSODIUM - warnlog("Allowing remote access to the console while libsodium support has not been enabled is not secure, and will result in cleartext communications"); +#if !defined(HAVE_LIBSODIUM) && !defined(HAVE_LIBCRYPTO) + warnlog("Allowing remote access to the console while neither libsodium nor libcrypto support has not been enabled is not secure, and will result in cleartext communications"); #endif NetmaskGroup nmg; @@ -1164,8 +1164,8 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) luaCtx.writeFunction("showConsoleACL", []() { setLuaNoSideEffect(); -#ifndef HAVE_LIBSODIUM - warnlog("Allowing remote access to the console while libsodium support has not been enabled is not secure, and will result in cleartext communications"); +#if !defined(HAVE_LIBSODIUM) && !defined(HAVE_LIBCRYPTO) + warnlog("Allowing remote access to the console while neither libsodium nor libcrypto support has not been enabled is not secure, and will result in cleartext communications"); #endif auto aclEntries = g_consoleACL.getLocal()->toStringVector(); @@ -1215,15 +1215,15 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) luaCtx.writeFunction("makeKey", []() { setLuaNoSideEffect(); - g_outputBuffer = "setKey(" + newKey() + ")\n"; + g_outputBuffer = "setKey(" + dnsdist::crypto::authenticated::newKey() + ")\n"; }); luaCtx.writeFunction("setKey", [](const std::string& key) { if (!g_configurationDone && !g_consoleKey.empty()) { // this makes sure the commandline -k key prevails over dnsdist.conf return; // but later setKeys() trump the -k value again } -#ifndef HAVE_LIBSODIUM - warnlog("Calling setKey() while libsodium support has not been enabled is not secure, and will result in cleartext communications"); +#if !defined(HAVE_LIBSODIUM) && !defined(HAVE_LIBCRYPTO) + warnlog("Calling setKey() while neither libsodium nor libcrypto support has been enabled is not secure, and will result in cleartext communications"); #endif setLuaSideEffect(); @@ -1242,7 +1242,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) luaCtx.writeFunction("testCrypto", [](boost::optional optTestMsg) { setLuaNoSideEffect(); -#ifdef HAVE_LIBSODIUM +#if defined(HAVE_LIBSODIUM) || defined(HAVE_LIBCRYPTO) try { string testmsg; @@ -1253,17 +1253,17 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) testmsg = "testStringForCryptoTests"; } - SodiumNonce sn, sn2; + dnsdist::crypto::authenticated::Nonce sn, sn2; sn.init(); sn2 = sn; - string encrypted = sodEncryptSym(testmsg, g_consoleKey, sn); - string decrypted = sodDecryptSym(encrypted, g_consoleKey, sn2); + string encrypted = dnsdist::crypto::authenticated::encryptSym(testmsg, g_consoleKey, sn); + string decrypted = dnsdist::crypto::authenticated::decryptSym(encrypted, g_consoleKey, sn2); sn.increment(); sn2.increment(); - encrypted = sodEncryptSym(testmsg, g_consoleKey, sn); - decrypted = sodDecryptSym(encrypted, g_consoleKey, sn2); + encrypted = dnsdist::crypto::authenticated::encryptSym(testmsg, g_consoleKey, sn); + decrypted = dnsdist::crypto::authenticated::decryptSym(encrypted, g_consoleKey, sn2); if (testmsg == decrypted) g_outputBuffer = "Everything is ok!\n"; diff --git a/pdns/dnsdist.cc b/pdns/dnsdist.cc index 3df62e18e2..74ee1a199a 100644 --- a/pdns/dnsdist.cc +++ b/pdns/dnsdist.cc @@ -52,6 +52,7 @@ #include "dnsdist-cache.hh" #include "dnsdist-carbon.hh" #include "dnsdist-console.hh" +#include "dnsdist-crypto.hh" #include "dnsdist-discovery.hh" #include "dnsdist-dnsparser.hh" #include "dnsdist-dynblocks.hh" @@ -80,7 +81,6 @@ #include "gettime.hh" #include "lock.hh" #include "misc.hh" -#include "sodcrypto.hh" #include "sstuff.hh" #include "threadname.hh" @@ -2499,7 +2499,7 @@ static void usage() cout<<"-c,--client Operate as a client, connect to dnsdist. This reads\n"; cout<<" controlSocket from your configuration file, but also\n"; cout<<" accepts an IP:PORT argument\n"; -#ifdef HAVE_LIBSODIUM +#if defined(HAVE_LIBSODIUM) || defined(HAVE_LIBCRYPTO) cout<<"-k,--setkey KEY Use KEY for encrypted communication to dnsdist. This\n"; cout<<" is similar to setting setKey in the configuration file.\n"; cout<<" NOTE: this will leak this key in your shell's history\n"; @@ -2722,14 +2722,14 @@ static void parseParameters(int argc, char** argv, ComboAddress& clientAddress) g_ACL.modify([optstring](NetmaskGroup& nmg) { nmg.addMask(optstring); }); break; case 'k': -#ifdef HAVE_LIBSODIUM +#if defined HAVE_LIBSODIUM || defined(HAVE_LIBCRYPTO) if (B64Decode(string(optarg), g_consoleKey) < 0) { cerr<<"Unable to decode key '"< #include +#include "dnsdist-crypto.hh" + #include "namespaces.hh" #include "noinitvector.hh" #include "misc.hh" #include "base64.hh" -#include "sodcrypto.hh" +namespace dnsdist::crypto::authenticated +{ #ifdef HAVE_LIBSODIUM - string newKey(bool base64Encoded) { std::string key; @@ -44,14 +46,14 @@ string newKey(bool base64Encoded) return "\"" + Base64Encode(key) + "\""; } -bool sodIsValidKey(const std::string& key) +bool isValidKey(const std::string& key) { return key.size() == crypto_secretbox_KEYBYTES; } -std::string sodEncryptSym(const std::string_view& msg, const std::string& key, SodiumNonce& nonce, bool incrementNonce) +std::string encryptSym(const std::string_view& msg, const std::string& key, Nonce& nonce, bool incrementNonce) { - if (!sodIsValidKey(key)) { + if (!isValidKey(key)) { throw std::runtime_error("Invalid encryption key of size " + std::to_string(key.size()) + " (" + std::to_string(crypto_secretbox_KEYBYTES) + " expected), use setKey() to set a valid key"); } @@ -73,7 +75,7 @@ std::string sodEncryptSym(const std::string_view& msg, const std::string& key, S return ciphertext; } -std::string sodDecryptSym(const std::string_view& msg, const std::string& key, SodiumNonce& nonce, bool incrementNonce) +std::string decryptSym(const std::string_view& msg, const std::string& key, Nonce& nonce, bool incrementNonce) { std::string decrypted; @@ -81,7 +83,7 @@ std::string sodDecryptSym(const std::string_view& msg, const std::string& key, S throw std::runtime_error("Could not decrypt message of size " + std::to_string(msg.length())); } - if (!sodIsValidKey(key)) { + if (!isValidKey(key)) { throw std::runtime_error("Invalid decryption key of size " + std::to_string(key.size()) + ", use setKey() to set a valid key"); } @@ -94,7 +96,8 @@ std::string sodDecryptSym(const std::string_view& msg, const std::string& key, S msg.length(), nonce.value.data(), // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - reinterpret_cast(key.data())) != 0) { + reinterpret_cast(key.data())) + != 0) { throw std::runtime_error("Could not decrypt message, please check that the key configured with setKey() is correct"); } @@ -105,19 +108,182 @@ std::string sodDecryptSym(const std::string_view& msg, const std::string& key, S return decrypted; } -void SodiumNonce::init() +void Nonce::init() { randombytes_buf(value.data(), value.size()); } -void SodiumNonce::merge(const SodiumNonce& lower, const SodiumNonce& higher) +#elif defined(HAVE_LIBCRYPTO) +#include +#include + +static constexpr size_t s_CHACHA20_POLY1305_KEY_SIZE = 32U; +static constexpr size_t s_POLY1305_BLOCK_SIZE = 16U; + +string newKey(bool base64Encoded) +{ + std::string key; + key.resize(s_CHACHA20_POLY1305_KEY_SIZE); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + if (RAND_priv_bytes(reinterpret_cast(key.data()), key.size()) != 1) { + throw std::runtime_error("Could not initialize random number generator for cryptographic functions"); + } + if (!base64Encoded) { + return key; + } + return "\"" + Base64Encode(key) + "\""; +} + +bool isValidKey(const std::string& key) +{ + return key.size() == s_CHACHA20_POLY1305_KEY_SIZE; +} + +std::string encryptSym(const std::string_view& msg, const std::string& key, Nonce& nonce, bool incrementNonce) +{ + if (!isValidKey(key)) { + throw std::runtime_error("Invalid encryption key of size " + std::to_string(key.size()) + " (" + std::to_string(s_CHACHA20_POLY1305_KEY_SIZE) + " expected), use setKey() to set a valid key"); + } + + // Each thread gets its own cipher context + static thread_local auto ctx = std::unique_ptr(nullptr, EVP_CIPHER_CTX_free); + + if (!ctx) { + ctx = std::unique_ptr(EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free); + if (!ctx) { + throw std::runtime_error("encryptSym: EVP_CIPHER_CTX_new() could not initialize cipher context"); + } + + if (EVP_EncryptInit_ex(ctx.get(), EVP_chacha20_poly1305(), nullptr, nullptr, nullptr) != 1) { + throw std::runtime_error("encryptSym: EVP_EncryptInit_ex() could not initialize encryption operation"); + } + } + + std::string ciphertext; + /* plus one so we can access the last byte in EncryptFinal which does nothing for this algo */ + ciphertext.resize(s_POLY1305_BLOCK_SIZE + msg.length() + 1); + int outLength{0}; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + if (EVP_EncryptInit_ex(ctx.get(), nullptr, nullptr, reinterpret_cast(key.c_str()), nonce.value.data()) != 1) { + throw std::runtime_error("encryptSym: EVP_EncryptInit_ex() could not initialize encryption key and IV"); + } + + if (!msg.empty()) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + if (EVP_EncryptUpdate(ctx.get(), + reinterpret_cast(&ciphertext.at(s_POLY1305_BLOCK_SIZE)), &outLength, + reinterpret_cast(msg.data()), msg.length()) + != 1) { + throw std::runtime_error("encryptSym: EVP_EncryptUpdate() could not encrypt message"); + } + } + + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + if (EVP_EncryptFinal_ex(ctx.get(), reinterpret_cast(&ciphertext.at(s_POLY1305_BLOCK_SIZE + outLength)), &outLength) != 1) { + throw std::runtime_error("encryptSym: EVP_EncryptFinal_ex() could finalize message encryption"); + ; + } + + /* Get the tag */ + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + if (EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_AEAD_GET_TAG, s_POLY1305_BLOCK_SIZE, ciphertext.data()) != 1) { + throw std::runtime_error("encryptSym: EVP_CIPHER_CTX_ctrl() could not get tag"); + } + + if (incrementNonce) { + nonce.increment(); + } + + ciphertext.resize(ciphertext.size() - 1); + return ciphertext; +} + +std::string decryptSym(const std::string_view& msg, const std::string& key, Nonce& nonce, bool incrementNonce) +{ + if (msg.length() < s_POLY1305_BLOCK_SIZE) { + throw std::runtime_error("Could not decrypt message of size " + std::to_string(msg.length())); + } + + if (!isValidKey(key)) { + throw std::runtime_error("Invalid decryption key of size " + std::to_string(key.size()) + ", use setKey() to set a valid key"); + } + + if (msg.length() == s_POLY1305_BLOCK_SIZE) { + if (incrementNonce) { + nonce.increment(); + } + return std::string(); + } + + // Each thread gets its own cipher context + static thread_local auto ctx = std::unique_ptr(nullptr, EVP_CIPHER_CTX_free); + if (!ctx) { + ctx = std::unique_ptr(EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free); + if (!ctx) { + throw std::runtime_error("decryptSym: EVP_CIPHER_CTX_new() could not initialize cipher context"); + } + + if (EVP_DecryptInit_ex(ctx.get(), EVP_chacha20_poly1305(), nullptr, nullptr, nullptr) != 1) { + throw std::runtime_error("decryptSym: EVP_DecryptInit_ex() could not initialize decryption operation"); + } + } + + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + if (EVP_DecryptInit_ex(ctx.get(), nullptr, nullptr, reinterpret_cast(key.c_str()), nonce.value.data()) != 1) { + throw std::runtime_error("decryptSym: EVP_DecryptInit_ex() could not initialize decryption key and IV"); + } + + const auto tag = msg.substr(0, s_POLY1305_BLOCK_SIZE); + std::string decrypted; + /* plus one so we can access the last byte in DecryptFinal, which does nothing */ + decrypted.resize(msg.length() - s_POLY1305_BLOCK_SIZE + 1); + int outLength{0}; + if (msg.size() > s_POLY1305_BLOCK_SIZE) { + if (!EVP_DecryptUpdate(ctx.get(), + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + reinterpret_cast(decrypted.data()), &outLength, + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + reinterpret_cast(&msg.at(s_POLY1305_BLOCK_SIZE)), msg.size() - s_POLY1305_BLOCK_SIZE)) { + throw std::runtime_error("Could not decrypt message (update failed), please check that the key configured with setKey() is correct"); + } + } + + /* Set expected tag value. Works in OpenSSL 1.0.1d and later */ + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast): sorry, OpenSSL's API is terrible + if (!EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_AEAD_SET_TAG, s_POLY1305_BLOCK_SIZE, const_cast(tag.data()))) { + throw std::runtime_error("Could not decrypt message (invalid tag), please check that the key configured with setKey() is correct"); + } + + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + if (!EVP_DecryptFinal_ex(ctx.get(), reinterpret_cast(&decrypted.at(outLength)), &outLength)) { + throw std::runtime_error("Could not decrypt message (final failed), please check that the key configured with setKey() is correct"); + } + + if (incrementNonce) { + nonce.increment(); + } + + decrypted.resize(decrypted.size() - 1); + return decrypted; +} + +void Nonce::init() +{ + if (RAND_priv_bytes(value.data(), value.size()) != 1) { + throw std::runtime_error("Could not initialize random number generator for cryptographic functions"); + } +} +#endif + +#if defined(HAVE_LIBSODIUM) || defined(HAVE_LIBCRYPTO) +void Nonce::merge(const Nonce& lower, const Nonce& higher) { constexpr size_t halfSize = std::tuple_size{} / 2; memcpy(value.data(), lower.value.data(), halfSize); memcpy(value.data() + halfSize, higher.value.data() + halfSize, halfSize); } -void SodiumNonce::increment() +void Nonce::increment() { // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) auto* ptr = reinterpret_cast(value.data()); @@ -126,23 +292,23 @@ void SodiumNonce::increment() } #else -void SodiumNonce::init() +void Nonce::init() { } -void SodiumNonce::merge(const SodiumNonce& lower, const SodiumNonce& higher) +void Nonce::merge(const Nonce& lower, const Nonce& higher) { } -void SodiumNonce::increment() +void Nonce::increment() { } -std::string sodEncryptSym(const std::string_view& msg, const std::string& key, SodiumNonce& nonce, bool incrementNonce) +std::string encryptSym(const std::string_view& msg, const std::string& key, Nonce& nonce, bool incrementNonce) { return std::string(msg); } -std::string sodDecryptSym(const std::string_view& msg, const std::string& key, SodiumNonce& nonce, bool incrementNonce) +std::string decryptSym(const std::string_view& msg, const std::string& key, Nonce& nonce, bool incrementNonce) { return std::string(msg); } @@ -152,12 +318,13 @@ string newKey(bool base64Encoded) return "\"plaintext\""; } -bool sodIsValidKey(const std::string& key) +bool isValidKey(const std::string& key) { return true; } #endif +} #include diff --git a/pdns/sodcrypto.hh b/pdns/dnsdistdist/dnsdist-crypto.hh similarity index 68% rename from pdns/sodcrypto.hh rename to pdns/dnsdistdist/dnsdist-crypto.hh index 4c22fc81d2..74e1c04696 100644 --- a/pdns/sodcrypto.hh +++ b/pdns/dnsdistdist/dnsdist-crypto.hh @@ -30,37 +30,42 @@ #include #endif -struct SodiumNonce +namespace dnsdist::crypto::authenticated +{ +struct Nonce { - SodiumNonce() = default; - SodiumNonce(const SodiumNonce&) = default; - SodiumNonce(SodiumNonce&&) = default; - SodiumNonce& operator=(const SodiumNonce&) = default; - SodiumNonce& operator=(SodiumNonce&&) = default; - ~SodiumNonce() = default; + Nonce() = default; + Nonce(const Nonce&) = default; + Nonce(Nonce&&) = default; + Nonce& operator=(const Nonce&) = default; + Nonce& operator=(Nonce&&) = default; + ~Nonce() = default; void init(); - void merge(const SodiumNonce& lower, const SodiumNonce& higher); + void merge(const Nonce& lower, const Nonce& higher); void increment(); -#if !defined(HAVE_LIBSODIUM) - std::array value{}; -#else +#if defined(HAVE_LIBSODIUM) std::array value{}; +#elif defined(HAVE_LIBCRYPTO) + // IV is 96 bits + std::array value{}; +#else + std::array value{}; #endif }; -std::string sodEncryptSym(const std::string_view& msg, const std::string& key, SodiumNonce& nonce, bool incrementNonce = true); -std::string sodDecryptSym(const std::string_view& msg, const std::string& key, SodiumNonce& nonce, bool incrementNonce = true); +std::string encryptSym(const std::string_view& msg, const std::string& key, Nonce& nonce, bool incrementNonce = true); +std::string decryptSym(const std::string_view& msg, const std::string& key, Nonce& nonce, bool incrementNonce = true); std::string newKey(bool base64Encoded = true); -bool sodIsValidKey(const std::string& key); +bool isValidKey(const std::string& key); -namespace dnsdist::crypto::authenticated -{ constexpr size_t getEncryptedSize(size_t plainTextSize) { #if defined(HAVE_LIBSODIUM) return plainTextSize + crypto_secretbox_MACBYTES; +#elif defined(HAVE_LIBCRYPTO) + return plainTextSize + 16; #else return plainTextSize; #endif diff --git a/pdns/dnsdistdist/docs/guides/console.rst b/pdns/dnsdistdist/docs/guides/console.rst index 465c7ce445..35e65be958 100644 --- a/pdns/dnsdistdist/docs/guides/console.rst +++ b/pdns/dnsdistdist/docs/guides/console.rst @@ -11,9 +11,9 @@ The console can be enabled with :func:`controlSocket`: controlSocket('192.0.2.53:5199') -Enabling the console without encryption enabled is not recommended. Note that encryption requires building dnsdist with libsodium support enabled. +Enabling the console without encryption enabled is not recommended. Note that encryption requires building dnsdist with either libsodium or libcrypto support enabled. -Once you have a libsodium-enabled dnsdist, the first step to enable encryption is to generate a key with :func:`makeKey`:: +Once you have a console-enabled dnsdist, the first step to enable encryption is to generate a key with :func:`makeKey`:: $ ./dnsdist -l 127.0.0.1:5300 [..] diff --git a/pdns/dnsdistdist/docs/manpages/dnsdist.1.rst b/pdns/dnsdistdist/docs/manpages/dnsdist.1.rst index 8a0eddc6d9..5c7cc002b7 100644 --- a/pdns/dnsdistdist/docs/manpages/dnsdist.1.rst +++ b/pdns/dnsdistdist/docs/manpages/dnsdist.1.rst @@ -58,7 +58,7 @@ Options that is used on the server (set with **setKey()**). Note that this will leak the key into your shell's history and into the systems running process list. Only available when dnsdist is compiled with - libsodium support. + libsodium or libcrypto support. -e, --execute Connect to dnsdist and execute *command*. -h, --help Display a helpful message and exit. -l, --local
Bind to *address*, Supply as many addresses (using multiple diff --git a/pdns/dnsdistdist/doh3.cc b/pdns/dnsdistdist/doh3.cc index e3c14ccee8..c9287778d2 100644 --- a/pdns/dnsdistdist/doh3.cc +++ b/pdns/dnsdistdist/doh3.cc @@ -28,7 +28,6 @@ #include "dolog.hh" #include "iputils.hh" #include "misc.hh" -#include "sodcrypto.hh" #include "sstuff.hh" #include "threadname.hh" #include "base64.hh" diff --git a/pdns/dnsdistdist/doq-common.cc b/pdns/dnsdistdist/doq-common.cc index 4b0b2868f9..5358c1c560 100644 --- a/pdns/dnsdistdist/doq-common.cc +++ b/pdns/dnsdistdist/doq-common.cc @@ -36,12 +36,12 @@ namespace dnsdist::doq { -static const std::string s_quicRetryTokenKey = newKey(false); +static const std::string s_quicRetryTokenKey = dnsdist::crypto::authenticated::newKey(false); PacketBuffer mintToken(const PacketBuffer& dcid, const ComboAddress& peer) { try { - SodiumNonce nonce; + dnsdist::crypto::authenticated::Nonce nonce; nonce.init(); const auto addrBytes = peer.toByteString(); @@ -54,7 +54,7 @@ PacketBuffer mintToken(const PacketBuffer& dcid, const ComboAddress& peer) plainTextToken.insert(plainTextToken.end(), addrBytes.begin(), addrBytes.end()); plainTextToken.insert(plainTextToken.end(), dcid.begin(), dcid.end()); // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - const auto encryptedToken = sodEncryptSym(std::string_view(reinterpret_cast(plainTextToken.data()), plainTextToken.size()), s_quicRetryTokenKey, nonce, false); + const auto encryptedToken = dnsdist::crypto::authenticated::encryptSym(std::string_view(reinterpret_cast(plainTextToken.data()), plainTextToken.size()), s_quicRetryTokenKey, nonce, false); // a bit sad, let's see if we can do better later auto encryptedTokenPacket = PacketBuffer(encryptedToken.begin(), encryptedToken.end()); encryptedTokenPacket.insert(encryptedTokenPacket.begin(), nonce.value.begin(), nonce.value.end()); @@ -88,7 +88,7 @@ std::optional getCID() std::optional validateToken(const PacketBuffer& token, const ComboAddress& peer) { try { - SodiumNonce nonce; + dnsdist::crypto::authenticated::Nonce nonce; auto addrBytes = peer.toByteString(); const uint64_t now = time(nullptr); const auto minimumSize = nonce.value.size() + sizeof(now) + addrBytes.size(); @@ -100,7 +100,7 @@ std::optional validateToken(const PacketBuffer& token, const Combo // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) auto cipher = std::string_view(reinterpret_cast(&token.at(nonce.value.size())), token.size() - nonce.value.size()); - auto plainText = sodDecryptSym(cipher, s_quicRetryTokenKey, nonce, false); + auto plainText = dnsdist::crypto::authenticated::decryptSym(cipher, s_quicRetryTokenKey, nonce, false); if (plainText.size() <= sizeof(now) + addrBytes.size()) { return std::nullopt; diff --git a/pdns/dnsdistdist/doq-common.hh b/pdns/dnsdistdist/doq-common.hh index 7af19ecb79..0b66a6943e 100644 --- a/pdns/dnsdistdist/doq-common.hh +++ b/pdns/dnsdistdist/doq-common.hh @@ -32,9 +32,9 @@ #include "dolog.hh" #include "noinitvector.hh" -#include "sodcrypto.hh" #include "sstuff.hh" #include "libssl.hh" +#include "dnsdist-crypto.hh" namespace dnsdist::doq { @@ -71,7 +71,7 @@ enum class DOQ_Error_Codes : uint64_t DOQ_UNSPECIFIED_ERROR = 5 }; -static constexpr size_t MAX_TOKEN_LEN = dnsdist::crypto::authenticated::getEncryptedSize(std::tuple_size{} /* nonce */ + sizeof(uint64_t) /* TTD */ + 16 /* IPv6 */ + QUICHE_MAX_CONN_ID_LEN); +static constexpr size_t MAX_TOKEN_LEN = dnsdist::crypto::authenticated::getEncryptedSize(std::tuple_size{} /* nonce */ + sizeof(uint64_t) /* TTD */ + 16 /* IPv6 */ + QUICHE_MAX_CONN_ID_LEN); static constexpr size_t MAX_DATAGRAM_SIZE = 1200; static constexpr size_t LOCAL_CONN_ID_LEN = 16; static constexpr std::array DOQ_ALPN{'\x03', 'd', 'o', 'q'}; diff --git a/pdns/dnsdistdist/doq.cc b/pdns/dnsdistdist/doq.cc index 9b626aaf88..faa7b8cf2a 100644 --- a/pdns/dnsdistdist/doq.cc +++ b/pdns/dnsdistdist/doq.cc @@ -28,7 +28,6 @@ #include "dolog.hh" #include "iputils.hh" #include "misc.hh" -#include "sodcrypto.hh" #include "sstuff.hh" #include "threadname.hh"