#include "SecretKeyCryptography.h" #include "sodium.h" #include "../ServerConfig.h" #include #include "../lib/Profiler.h" SecretKeyCryptography::SecretKeyCryptography() : mOpsLimit(10), mMemLimit(33554432), mAlgo(2), mEncryptionKey(nullptr), mEncryptionKeyHash(0) { } SecretKeyCryptography::SecretKeyCryptography(unsigned long long opslimit, size_t memlimit, int algo) : mOpsLimit(opslimit), mMemLimit(memlimit), mAlgo(algo), mEncryptionKey(nullptr), mEncryptionKeyHash(0) { } SecretKeyCryptography::~SecretKeyCryptography() { if (mEncryptionKey) { MemoryManager::getInstance()->releaseMemory(mEncryptionKey); mEncryptionKey = nullptr; } } SecretKeyCryptography::ResultType SecretKeyCryptography::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; assert(app_secret); Profiler timeUsed; std::unique_lock _lock(mWorkingMutex); #ifndef _TEST_BUILD if (timeUsed.millis() > 10) { printf("[SecretKeyCryptography::createKey] wait %s on getting lock\n", timeUsed.string().data()); timeUsed.reset(); } #endif // 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); #ifndef _TEST_BUILD if (timeUsed.millis() > 200) { printf("[SecretKeyCryptography::createKey] %s calculating sha512\n", timeUsed.string().data()); timeUsed.reset(); } #endif //unsigned char* key = (unsigned char *)malloc(crypto_box_SEEDBYTES); // 32U //ObfusArray* key = new ObfusArray(crypto_box_SEEDBYTES); if (!mEncryptionKey) { 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; } #ifndef _TEST_BUILD if (timeUsed.millis() > 400) { printf("[SecretKeyCryptography::createKey] %s calculating pwd hash\n", timeUsed.string().data()); } #endif // generate hash from key for compare assert(sizeof(KeyHashed) >= crypto_shorthash_BYTES); assert(ServerConfig::g_ServerCryptoKey); crypto_shorthash((unsigned char*)&mEncryptionKeyHash, *mEncryptionKey, crypto_box_SEEDBYTES, *ServerConfig::g_ServerCryptoKey); return AUTH_CREATE_ENCRYPTION_KEY_SUCCEED; } SecretKeyCryptography::ResultType SecretKeyCryptography::encrypt(const MemoryBin* message, MemoryBin** encryptedMessage) const { 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; } SecretKeyCryptography::ResultType SecretKeyCryptography::decrypt(const unsigned char* encryptedMessage, size_t encryptedMessageSize, MemoryBin** message) const { assert(message); std::shared_lock _lock(mWorkingMutex); if (!mEncryptionKey) { return AUTH_NO_KEY; } size_t decryptSize = encryptedMessageSize - 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, encryptedMessageSize, nonce, *mEncryptionKey)) { mm->releaseMemory(decryptedData); return AUTH_DECRYPT_MESSAGE_FAILED; } *message = decryptedData; return AUTH_DECRYPT_OK; } const char* SecretKeyCryptography::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 ""; }