Skip to content

Commit

Permalink
- added new (experimental) Crypto::CCryptoManager() (#255)
Browse files Browse the repository at this point in the history
   - supports session based data encryption/decryption
   - support secure memory handling
- corrections related to new (experimental) Crypto::CSecureString() class (#255)
   - rewrote the implementation to use Crypto::CCryptoManager's session data encryption feature (which makes the CSecureString() class compatible with Linux and OSX)
   - changed AddChar() to return the number of written characters (useful for UTF-8-mode)
   - added missing integer overflow check
   - fixed off-by-one checks (incl. off-by-one checks in AddChar() in UTF-8 mode)
   - several compile error/warning fixes
   - added documentation
   - whitespaces
- updated changelog entries (mainly for 0.1.3) (facebookarchive#130, #220, #223, #249, #225, #226, #227, #228, #236, #238, #242, #258, #259, #261, #262, #263, #264, #265, #269)
  • Loading branch information
Luke1410 committed Sep 2, 2019
1 parent b25662f commit 1af4051
Show file tree
Hide file tree
Showing 12 changed files with 393 additions and 110 deletions.
2 changes: 2 additions & 0 deletions DependentExtensions/Swig/DLL_Swig/dll_csharp_bindings.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,7 @@
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\..\bindings\csharp\wrapper\slikenet_wrapper.cpp" />
<ClCompile Include="..\..\..\Source\src\crypto\cryptomanager.cpp" />
<ClCompile Include="..\..\..\Source\src\crypto\factory.cpp" />
<ClCompile Include="..\..\..\Source\src\crypto\fileencrypter.cpp" />
<ClCompile Include="..\..\..\Source\src\crypto\securestring.cpp" />
Expand Down Expand Up @@ -697,6 +698,7 @@
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\bindings\csharp\wrapper\slikenet_wrapper.h" />
<ClInclude Include="..\..\..\Source\include\slikenet\crypto\cryptomanager.h" />
<ClInclude Include="..\..\..\Source\include\slikenet\crypto\factory.h" />
<ClInclude Include="..\..\..\Source\include\slikenet\crypto\fileencrypter.h" />
<ClInclude Include="..\..\..\Source\include\slikenet\crypto\ifileencrypter.h" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,9 @@
<ClCompile Include="..\..\..\Source\src\crypto\fileencrypter.cpp">
<Filter>Source Files\crypto</Filter>
</ClCompile>
<ClCompile Include="..\..\..\Source\src\crypto\cryptomanager.cpp">
<Filter>Source Files\crypto</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\Source\include\slikenet\_FindFirst.h">
Expand Down Expand Up @@ -798,6 +801,9 @@
<ClInclude Include="..\..\..\Source\include\slikenet\crypto\ifileencrypter.h">
<Filter>Header Files\crypto</Filter>
</ClInclude>
<ClInclude Include="..\..\..\Source\include\slikenet\crypto\cryptomanager.h">
<Filter>Header Files\crypto</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="Header Files">
Expand Down
2 changes: 2 additions & 0 deletions Lib/DLL/DLL.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,7 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\Source\src\crypto\cryptomanager.cpp" />
<ClCompile Include="..\..\Source\src\crypto\factory.cpp" />
<ClCompile Include="..\..\Source\src\crypto\fileencrypter.cpp" />
<ClCompile Include="..\..\Source\src\crypto\securestring.cpp" />
Expand Down Expand Up @@ -612,6 +613,7 @@
<ClCompile Include="..\..\Source\src\WSAStartupSingleton.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\Source\include\slikenet\crypto\cryptomanager.h" />
<ClInclude Include="..\..\Source\include\slikenet\crypto\factory.h" />
<ClInclude Include="..\..\Source\include\slikenet\crypto\fileencrypter.h" />
<ClInclude Include="..\..\Source\include\slikenet\crypto\ifileencrypter.h" />
Expand Down
6 changes: 6 additions & 0 deletions Lib/DLL/DLL.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,9 @@
<ClCompile Include="..\..\Source\src\crypto\fileencrypter.cpp">
<Filter>Source Files\crypto</Filter>
</ClCompile>
<ClCompile Include="..\..\Source\src\crypto\cryptomanager.cpp">
<Filter>Source Files\crypto</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\Source\include\slikenet\_FindFirst.h">
Expand Down Expand Up @@ -838,5 +841,8 @@
<ClInclude Include="..\..\Source\include\slikenet\crypto\ifileencrypter.h">
<Filter>Header Files\crypto</Filter>
</ClInclude>
<ClInclude Include="..\..\Source\include\slikenet\crypto\cryptomanager.h">
<Filter>Header Files\crypto</Filter>
</ClInclude>
</ItemGroup>
</Project>
2 changes: 2 additions & 0 deletions Lib/LibStatic/LibStatic.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,7 @@
</Lib>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\Source\src\crypto\cryptomanager.cpp" />
<ClCompile Include="..\..\Source\src\crypto\factory.cpp" />
<ClCompile Include="..\..\Source\src\crypto\fileencrypter.cpp" />
<ClCompile Include="..\..\Source\src\crypto\securestring.cpp" />
Expand Down Expand Up @@ -530,6 +531,7 @@
<ClCompile Include="..\..\Source\src\WSAStartupSingleton.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\Source\include\slikenet\crypto\cryptomanager.h" />
<ClInclude Include="..\..\Source\include\slikenet\crypto\factory.h" />
<ClInclude Include="..\..\Source\include\slikenet\crypto\fileencrypter.h" />
<ClInclude Include="..\..\Source\include\slikenet\crypto\ifileencrypter.h" />
Expand Down
6 changes: 6 additions & 0 deletions Lib/LibStatic/LibStatic.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,9 @@
<ClCompile Include="..\..\Source\src\crypto\factory.cpp">
<Filter>Source Files\crypto</Filter>
</ClCompile>
<ClCompile Include="..\..\Source\src\crypto\cryptomanager.cpp">
<Filter>Source Files\crypto</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\Source\include\slikenet\_FindFirst.h">
Expand Down Expand Up @@ -838,5 +841,8 @@
<ClInclude Include="..\..\Source\include\slikenet\crypto\ifileencrypter.h">
<Filter>Header Files\crypto</Filter>
</ClInclude>
<ClInclude Include="..\..\Source\include\slikenet\crypto\cryptomanager.h">
<Filter>Header Files\crypto</Filter>
</ClInclude>
</ItemGroup>
</Project>
3 changes: 1 addition & 2 deletions Source/include/slikenet/WindowsIncludes.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* of patent rights can be found in the RakNet Patents.txt file in the same directory.
*
*
* Modified work: Copyright (c) 2016-2018, SLikeSoft UG (haftungsbeschränkt)
* Modified work: Copyright (c) 2016-2019, SLikeSoft UG (haftungsbeschränkt)
*
* This source code was modified by SLikeSoft. Modifications are licensed under the MIT-style
* license found in the license.txt file in the root directory of this source tree.
Expand All @@ -21,7 +21,6 @@
#elif defined (_WIN32)
#include <winsock2.h>
#include <windows.h>
#include <Wincrypt.h> // used for CryptProtectMemory, CryptUnprotectMemory, CRYPTPROTECTMEMORY_BLOCK_SIZE
#include <ws2tcpip.h>
#include <IPHlpApi.h> // used for GetAdaptersAddresses()
#pragma comment(lib, "IPHLPAPI.lib") // used for GetAdaptersAddresses()
Expand Down
48 changes: 48 additions & 0 deletions Source/include/slikenet/crypto/cryptomanager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright (c) 2019, SLikeSoft UG (haftungsbeschränkt)
*
* This source code is licensed under the MIT-style license found in the license.txt
* file in the root directory of this source tree.
*/
#pragma once

#include <openssl/evp.h> // used for EVP_xxxx

namespace SLNet
{
namespace Experimental
{
namespace Crypto
{
class CCryptoManager
{
private:
// class members
// note: using distinct contexts for encryption/decryption to prevent potential for race conditions
// #med - consider moving to SessionEncrypter class
static EVP_CIPHER_CTX m_decryptionContext;
static EVP_CIPHER_CTX m_encryptionContext;
static unsigned char m_initializationVector[EVP_MAX_IV_LENGTH];
static unsigned char m_sessionKey[EVP_MAX_KEY_LENGTH];
static bool m_Initialized;

public:
// initialization
static bool Initialize();

public:
// session encryption
static bool EncryptSessionData(const unsigned char* plaintext, size_t dataLength, unsigned char* outBuffer, size_t& inOutBufferSize);
static bool DecryptSessionData(const unsigned char* encryptedtext, size_t dataLength, unsigned char* outBuffer, size_t& inOutBufferSize);
static bool GetRequiredEncryptionBufferSize(size_t& encryptionDataByteLength);

public:
// secure memory management methods
// #med - consider moving to separate class (SecureMemory/MemoryManager)
static void* AllocateSecureMemory(size_t size);
static void FreeSecureMemory(void* pointer, size_t size);
static void SecureClearMemory(void* pointer, size_t dataSize);
};
}
}
}
14 changes: 8 additions & 6 deletions Source/include/slikenet/crypto/securestring.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, SLikeSoft UG (haftungsbeschränkt)
* Copyright (c) 2018-2019, SLikeSoft UG (haftungsbeschränkt)
*
* This source code is licensed under the MIT-style license found in the license.txt
* file in the root directory of this source tree.
Expand All @@ -24,10 +24,12 @@ namespace SLNet
private:
bool m_UTF8Mode;
bool m_wasFlushed;
size_t m_EncryptedBufferSize;
size_t m_numBufferUsed;
size_t m_UnencryptedBufferSize;
char* m_EncryptedMemory;
size_t m_EncryptedBufferSize; // size of the buffer for the encrypted string
size_t m_numBufferSize; // size of the actual supported string buffer (excluding the trailing \0-terminator)
size_t m_numBufferUsed; // size of the available buffer currently used
size_t m_numEncryptedBufferUsed; // size of the encrypted buffer which is used and contains the encrypted data
size_t m_UnencryptedBufferSize; // size of the buffer allocated to retrieve the decrypted string
unsigned char* m_EncryptedMemory;
char* m_UnencryptedBuffer;

// constructor / destructor
Expand All @@ -37,7 +39,7 @@ namespace SLNet

// container methods
public:
bool AddChar(char* character);
size_t AddChar(char* character);
bool RemoveLastChar();
void Reset();

Expand Down
178 changes: 178 additions & 0 deletions Source/src/crypto/cryptomanager.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
/*
* Copyright (c) 2019, SLikeSoft UG (haftungsbeschränkt)
*
* This source code is licensed under the MIT-style license found in the license.txt
* file in the root directory of this source tree.
*/
#include "slikenet/crypto/cryptomanager.h"

#include "slikenet/assert.h" // used for RakAssert
#include <limits> // used for std::numeric_limits<>

// prevent max/min macros getting defined (breaking numeric_limits<>::max() / ::min() usage) through the indirect windows.h include in the OpenSSL includes
#ifdef _WIN32
#define NOMINMAX
#endif

#include <openssl/err.h> // used for ERR_xxxx
#include <openssl/evp.h> // used for EVP_xxxx, OpenSSL_add_all_algorithms()
#include <openssl/rand.h> // used for RAND_xxxx

namespace SLNet
{
namespace Experimental
{
namespace Crypto
{
bool CCryptoManager::Initialize()
{
if (m_Initialized) {
return true; // already initialized
}

ERR_load_crypto_strings();
OpenSSL_add_all_algorithms();

// #high - replace with EGADS
RAND_screen();

if (RAND_bytes(m_sessionKey, EVP_MAX_KEY_LENGTH) == 0) {
return false; // failed to initialize the random session key
}
if (RAND_pseudo_bytes(m_initializationVector, EVP_MAX_IV_LENGTH) == 0) {
return false; // failed to initialize the initialization vector
}

m_Initialized = true;
return true;
}

bool CCryptoManager::EncryptSessionData(const unsigned char* plaintext, size_t dataLength, unsigned char* outBuffer, size_t& inOutBufferSize)
{
if (!Initialize()) {
return false; // CryptoManager failed to initialize
}

size_t requiredBufferSize = dataLength;
if (!GetRequiredEncryptionBufferSize(requiredBufferSize)) {
return false; // dataLength too large (integer overflow)
}
if (inOutBufferSize < requiredBufferSize) {
return false; // out buffer (potentially) too small
}

// #high - review usage of the CBC mode here --- not the best nowadays
// #med - add engine support to use HW-acceleration
if (EVP_EncryptInit_ex(&m_encryptionContext, EVP_aes_256_cbc(), nullptr, m_sessionKey, m_initializationVector) == 0) {
return false; // failed to initialize the encryption context
}

int bytesWritten1;
// note: static_cast<> safe here, since GetRequiredEncrpytionBufferSize()-check ensured dataLength is <= int::max()
if (EVP_EncryptUpdate(&m_encryptionContext, outBuffer, &bytesWritten1, plaintext, static_cast<int>(dataLength)) == 0) {
return false; // encryption failed
}
RakAssert(static_cast<size_t>(bytesWritten1) <= inOutBufferSize);
int bytesWritten2;
if (EVP_EncryptFinal_ex(&m_encryptionContext, outBuffer + bytesWritten1, &bytesWritten2) == 0) {
return false; // failed final encryption step
}
RakAssert(static_cast<size_t>(bytesWritten1) + static_cast<size_t>(bytesWritten2) <= inOutBufferSize);

inOutBufferSize = static_cast<size_t>(bytesWritten1) + static_cast<size_t>(bytesWritten2);
return true;
}

bool CCryptoManager::DecryptSessionData(const unsigned char* encryptedtext, size_t dataLength, unsigned char* outBuffer, size_t& inOutBufferSize)
{
if (!Initialize()) {
return false; // CryptoManager failed to initialize
}

// #med - extend support for inOutBufferSize > int::max()
if (inOutBufferSize > static_cast<size_t>(std::numeric_limits<int>::max())) {
// note: We check the inOutBufferSize here rather than the dataLength due to the indirect size limitation due to the EVP_DecryptUpdate()/EVP_DecryptFinal_ex() calls
// being limited to int::max() through their returned written bytes values. Due to the next check (inOutBufferSize < dataLength) it's implicitly ensured that
// dataLength doesn't exceed the limit either.
return false; // specified length exceeds max supported size
}

if (inOutBufferSize < dataLength) {
// prevent potential buffer overflow, even though it's possible that the encryptedtext is padded and hence the effectively required
// inOutBufferSize is less than the provided dataLength, since we cannot determine this before running the actual decryption - so consider
// this an invalid call, if the provided inOutBufferSize is smaller than the incoming encrypted text's length
return false;
}

// #high - review usage of the CBC mode here --- not the best nowadays
// #med - add engine support to use HW-acceleration
if (EVP_DecryptInit_ex(&m_decryptionContext, EVP_aes_256_cbc(), nullptr, m_sessionKey, m_initializationVector) == 0) {
return false; // failed to initialize the decryption context
}

int bytesWritten1;
// static cast safe due to size-check above
if (EVP_DecryptUpdate(&m_decryptionContext, outBuffer, &bytesWritten1, encryptedtext, static_cast<int>(dataLength)) == 0) {
return false; // decryption failed
}
RakAssert(static_cast<size_t>(bytesWritten1) <= inOutBufferSize);
int bytesWritten2;
if (EVP_DecryptFinal_ex(&m_decryptionContext, outBuffer, &bytesWritten2) == 0) {
return false; // failed final decryption step
}
RakAssert(static_cast<size_t>(bytesWritten1) + static_cast<size_t>(bytesWritten2) <= inOutBufferSize);

inOutBufferSize = static_cast<size_t>(bytesWritten1) + static_cast<size_t>(bytesWritten2);
return true;
}

bool CCryptoManager::GetRequiredEncryptionBufferSize(size_t& encryptionDataByteLength)
{
// note: not using EVP_CIPHER_CTX_block_size() here, since otherwise we would have to make sure that the encryption context was initialized already
const int blockSize = EVP_CIPHER_block_size(EVP_aes_256_cbc());

// EVP_EncryptUpdate() can write up to dataLength + blockSize - 1. The final EVP_EncryptFinal_ex() call can write up to blockSize. (reference: OpenSSL 1.0.2 documentation)
// Hence, by definition the required encryption buffer size is dataLength + blockSize*2 -1.
// Note: Practically the limit should actually never exceed dataLength + blockSize due to the encryption we use (AES 256 / CBC). However, we want to be safe
// on the design level to prevent possible incompatibilities with future OpenSSL changes.

if (encryptionDataByteLength + blockSize * 2 - 1 < encryptionDataByteLength) {
return false; // prevent integer overflow
}

encryptionDataByteLength = encryptionDataByteLength + blockSize * 2 - 1;

// #med - extend support for datalength > int::max()
// verify that the specified length doesn't exceed the max supported data length
return encryptionDataByteLength <= static_cast<size_t>(std::numeric_limits<int>::max());
}

void* CCryptoManager::AllocateSecureMemory(size_t size)
{
// #high - route through memory manager (aka: same as OP_NEW_ARRAY)
return OPENSSL_malloc(size);
}

void CCryptoManager::FreeSecureMemory(void* pointer, size_t size)
{
// make sure the memory is cleared before it's freed again
SecureClearMemory(pointer, size);

// #high - route through memory manager (aka: same as OP_NEW_ARRAY)
return OPENSSL_free(pointer);
}

void CCryptoManager::SecureClearMemory(void* pointer, size_t size)
{
OPENSSL_cleanse(pointer, size);
}

// initialization list
EVP_CIPHER_CTX CCryptoManager::m_decryptionContext;
EVP_CIPHER_CTX CCryptoManager::m_encryptionContext;
unsigned char CCryptoManager::m_sessionKey[EVP_MAX_KEY_LENGTH];
unsigned char CCryptoManager::m_initializationVector[EVP_MAX_IV_LENGTH];
bool CCryptoManager::m_Initialized = false;
}
}
}
Loading

0 comments on commit 1af4051

Please sign in to comment.