From ea18b3ca0a2597388689f4e018fb06420a5c9e05 Mon Sep 17 00:00:00 2001 From: Dario Date: Sun, 7 Jun 2020 21:36:17 +0200 Subject: [PATCH] add Crypto/AuthenticatedEncryption to move code from model/User into own class, add app secret to server config --- src/cpp/Crypto/AuthenticatedEncryption.cpp | 137 +++++++++++++++++++++ src/cpp/Crypto/AuthenticatedEncryption.h | 78 ++++++++++++ src/cpp/ServerConfig.cpp | 14 +++ src/cpp/ServerConfig.h | 3 +- 4 files changed, 231 insertions(+), 1 deletion(-) create mode 100644 src/cpp/Crypto/AuthenticatedEncryption.cpp create mode 100644 src/cpp/Crypto/AuthenticatedEncryption.h diff --git a/src/cpp/Crypto/AuthenticatedEncryption.cpp b/src/cpp/Crypto/AuthenticatedEncryption.cpp new file mode 100644 index 000000000..0c2378eb6 --- /dev/null +++ b/src/cpp/Crypto/AuthenticatedEncryption.cpp @@ -0,0 +1,137 @@ +#include "AuthenticatedEncryption.h" + +#include "sodium.h" +#include "../ServerConfig.h" +#include + +AuthenticatedEncryption::AuthenticatedEncryption() + : mOpsLimit(10), mMemLimit(33554432), mAlgo(2), mEncryptionKey(nullptr), mEncryptionKeyHash(0) +{ +} + +AuthenticatedEncryption::AuthenticatedEncryption(unsigned long long opslimit, size_t memlimit, int algo) + : mOpsLimit(opslimit), mMemLimit(memlimit), mAlgo(algo), mEncryptionKey(nullptr), mEncryptionKeyHash(0) +{ + +} + +AuthenticatedEncryption::~AuthenticatedEncryption() +{ + if (mEncryptionKey) { + MemoryManager::getInstance()->releaseMemory(mEncryptionKey); + mEncryptionKey = nullptr; + } +} + +AuthenticatedEncryption::ResultType AuthenticatedEncryption::createKey(const std::string& salt_parameter, const std::string& passwd) +{ + assert(crypto_hash_sha512_BYTES >= crypto_pwhash_SALTBYTES); + + auto mm = MemoryManager::getInstance(); + auto app_secret = ServerConfig::g_CryptoAppSecret; + + std::unique_lock _lock(mWorkingMutex); + + // use hash512 because existing data where calculated with that, but could be also changed to hash256 + auto hash512_salt = mm->getFreeMemory(crypto_hash_sha512_BYTES); // need at least crypto_pwhash_SALTBYTES 16U + + crypto_hash_sha512_state state; + crypto_hash_sha512_init(&state); + //crypto_hash_sha512_update + crypto_hash_sha512_update(&state, (const unsigned char*)salt_parameter.data(), salt_parameter.size()); + crypto_hash_sha512_update(&state, *app_secret, app_secret->size()); + crypto_hash_sha512_final(&state, *hash512_salt); + + + //unsigned char* key = (unsigned char *)malloc(crypto_box_SEEDBYTES); // 32U + //ObfusArray* key = new ObfusArray(crypto_box_SEEDBYTES); + auto mEncryptionKey = mm->getFreeMemory(crypto_box_SEEDBYTES); + //Bin32Bytes* key = mm->get32Bytes(); + + // generate encryption key, should take a bit longer to make brute force attacks hard + if (crypto_pwhash(*mEncryptionKey, mEncryptionKey->size(), passwd.data(), passwd.size(), *hash512_salt, mOpsLimit, mMemLimit, mAlgo) != 0) { + mm->releaseMemory(mEncryptionKey); + mEncryptionKey = nullptr; + + return AUTH_CREATE_ENCRYPTION_KEY_FAILED; + } + + // generate hash from key for compare + assert(sizeof(KeyHashed) >= crypto_shorthash_BYTES); + crypto_shorthash((unsigned char*)&mEncryptionKeyHash, *mEncryptionKey, crypto_box_SEEDBYTES, *ServerConfig::g_ServerCryptoKey); + + return AUTH_ENCRYPT_OK; +} + +AuthenticatedEncryption::ResultType AuthenticatedEncryption::encrypt(const MemoryBin* message, MemoryBin** encryptedMessage) +{ + assert(message && encryptedMessage); + std::shared_lock _lock(mWorkingMutex); + + if (!mEncryptionKey) { + return AUTH_NO_KEY; + } + + size_t message_len = message->size(); + size_t ciphertext_len = crypto_secretbox_MACBYTES + message_len; + + unsigned char nonce[crypto_secretbox_NONCEBYTES]; + // we use a hardcoded value for nonce + // TODO: use a dynamic value, save it along with the other parameters + memset(nonce, 31, crypto_secretbox_NONCEBYTES); + + auto mm = MemoryManager::getInstance(); + auto ciphertext = mm->getFreeMemory(ciphertext_len); + memset(*ciphertext, 0, ciphertext_len); + + if (0 != crypto_secretbox_easy(*ciphertext, *message, message_len, nonce, *mEncryptionKey)) { + mm->releaseMemory(ciphertext); + + return AUTH_ENCRYPT_MESSAGE_FAILED; + } + + *encryptedMessage = ciphertext; + + return AUTH_ENCRYPT_OK; +} + +AuthenticatedEncryption::ResultType AuthenticatedEncryption::decrypt(const MemoryBin* encryptedMessage, MemoryBin** message) +{ + assert(message && encryptedMessage); + std::shared_lock _lock(mWorkingMutex); + + if (!mEncryptionKey) { + return AUTH_NO_KEY; + } + + size_t decryptSize = encryptedMessage->size() - crypto_secretbox_MACBYTES; + //unsigned char* decryptBuffer = (unsigned char*)malloc(decryptSize); + auto mm = MemoryManager::getInstance(); + //ObfusArray* decryptedData = new ObfusArray(decryptSize); + auto decryptedData = mm->getFreeMemory(decryptSize); + unsigned char nonce[crypto_secretbox_NONCEBYTES]; + // we use a hardcoded value for nonce + // TODO: use a dynamic value, save it along with the other parameters + memset(nonce, 31, crypto_secretbox_NONCEBYTES); + + if (crypto_secretbox_open_easy(*decryptedData, *encryptedMessage, encryptedMessage->size(), nonce, *mEncryptionKey)) { + mm->releaseMemory(decryptedData); + return AUTH_DECRYPT_MESSAGE_FAILED; + } + *message = decryptedData; + + return AUTH_DECRYPT_OK; +} + +const char* AuthenticatedEncryption::getErrorMessage(ResultType type) +{ + switch (type) { + case AUTH_ENCRYPT_OK: return "everything is ok"; + //case AUTH_ENCRYPT_SHA2_TO_SMALL: return "libsodium crypto_hash_sha512_BYTES is to small to use as crypto_pwhash_SALTBYTES"; + case AUTH_CREATE_ENCRYPTION_KEY_FAILED: return "error creating encryption key, maybe to much memory requested?"; + case AUTH_NO_KEY: return "no encryption key generated"; + case AUTH_ENCRYPT_MESSAGE_FAILED: return "message encryption failed"; + case AUTH_DECRYPT_MESSAGE_FAILED: return "message decryption failed"; + } + return ""; +} \ No newline at end of file diff --git a/src/cpp/Crypto/AuthenticatedEncryption.h b/src/cpp/Crypto/AuthenticatedEncryption.h new file mode 100644 index 000000000..9474137dd --- /dev/null +++ b/src/cpp/Crypto/AuthenticatedEncryption.h @@ -0,0 +1,78 @@ +#ifndef __GRADIDO_LOGIN_SERVER_CRYPTO_AUTHENTICATED_ENCRYPTION_H +#define __GRADIDO_LOGIN_SERVER_CRYPTO_AUTHENTICATED_ENCRYPTION_H + + +#include "../SingletonManager/MemoryManager.h" + +#include + +/*! + * + * \author: Dario Rekowski + * + * \date: 07-06-2020 + * + * \brief: Wrapper Class for make using libsodium authenticated encryption easy, used for encrypt private keys for user + * + */ + +typedef Poco::UInt64 KeyHashed; + +class AuthenticatedEncryption +{ +public: + + enum ResultType { + AUTH_ENCRYPT_OK, + AUTH_DECRYPT_OK, + AUTH_CREATE_ENCRYPTION_KEY_FAILED, + AUTH_NO_KEY, + AUTH_ENCRYPT_MESSAGE_FAILED, + AUTH_DECRYPT_MESSAGE_FAILED + }; + + //! \brief init with default algorithms parameter + AuthenticatedEncryption(); + //! \brief init with custom algorithms parameter + //! + //! details see in libsodium crypto_pwhash + AuthenticatedEncryption(unsigned long long opslimit, size_t memlimit, int algo); + + ~AuthenticatedEncryption(); + + + inline KeyHashed getKeyHashed() const { std::shared_lock _lock(mWorkingMutex); return mEncryptionKeyHash; } + inline bool operator == (const AuthenticatedEncryption& b) const { + std::shared_lock _lock(mWorkingMutex); + return mEncryptionKeyHash == b.getKeyHashed(); + } + + inline bool hasKey() const { std::shared_lock _lock(mWorkingMutex); return !mEncryptionKey; } + + //! \brief generate encryption key, with default parameter use ca. 300 ms + //! + //! should be call from task, running in g_CryptoCPUScheduler, lock shared mutex for writing + //! \param salt_parameter for example email + //! \return AUTH_CREATE_ENCRYPTION_KEY_FAILED call strerror(errno) for more details + ResultType createKey(const std::string& salt_parameter, const std::string& passwd); + + ResultType encrypt(const MemoryBin* message, MemoryBin** encryptedMessage); + + ResultType decrypt(const MemoryBin* encryptedMessage, MemoryBin** message); + + const char* getErrorMessage(ResultType type); + +protected: + // algorithms parameter + unsigned long long mOpsLimit; + size_t mMemLimit; + int mAlgo; + + // encryption key and hash + MemoryBin* mEncryptionKey; + KeyHashed mEncryptionKeyHash; + + mutable std::shared_mutex mWorkingMutex; +}; + +#endif //__GRADIDO_LOGIN_SERVER_CRYPTO_AUTHENTICATED_ENCRYPTION_H \ No newline at end of file diff --git a/src/cpp/ServerConfig.cpp b/src/cpp/ServerConfig.cpp index 1f161cfdf..ddd80f1ab 100644 --- a/src/cpp/ServerConfig.cpp +++ b/src/cpp/ServerConfig.cpp @@ -3,6 +3,7 @@ #include "Crypto/mnemonic_german2.h" #include "Crypto/mnemonic_bip0039.h" #include "Crypto/DRRandom.h" +#include "lib/DataTypeConverter.h" #include "sodium.h" @@ -51,6 +52,7 @@ namespace ServerConfig { bool g_disableEmail = false; ServerSetupType g_ServerSetupType = SERVER_TYPE_PRODUCTION; std::string g_gRPCRelayServerFullURL; + MemoryBin* g_CryptoAppSecret = nullptr; #ifdef __linux__ #include @@ -217,6 +219,14 @@ namespace ServerConfig { auto serverSetupTypeString = cfg.getString("ServerSetupType", ""); g_ServerSetupType = getServerSetupTypeFromString(serverSetupTypeString); + // app secret for encrypt user private keys + // TODO: encrypt with server admin key + auto app_secret_string = cfg.getString("crypto.app_secret", ""); + if ("" != app_secret_string) { + g_CryptoAppSecret = DataTypeConverter::hexToBin(app_secret_string); + } + //g_CryptoAppSecret + g_gRPCRelayServerFullURL = cfg.getString("grpc.server", ""); return true; @@ -283,6 +293,10 @@ namespace ServerConfig { if (g_CryptoCPUScheduler) { delete g_CryptoCPUScheduler; } + if (g_CryptoAppSecret) { + MemoryManager::getInstance()->releaseMemory(g_CryptoAppSecret); + g_CryptoAppSecret = nullptr; + } } void writeToFile(std::istream& datas, std::string fileName) diff --git a/src/cpp/ServerConfig.h b/src/cpp/ServerConfig.h index fc190a53c..07632b183 100644 --- a/src/cpp/ServerConfig.h +++ b/src/cpp/ServerConfig.h @@ -10,7 +10,7 @@ #include "tasks/CPUSheduler.h" #include "SingletonManager/LanguageManager.h" - +#include "SingletonManager/MemoryManager.h" #define DISABLE_EMAIL @@ -60,6 +60,7 @@ namespace ServerConfig { extern bool g_disableEmail; extern ServerSetupType g_ServerSetupType; extern std::string g_gRPCRelayServerFullURL; + extern MemoryBin* g_CryptoAppSecret; bool loadMnemonicWordLists(); bool initServerCrypto(const Poco::Util::LayeredConfiguration& cfg);