Skip to content

Commit 1af4051

Browse files
committed
- added new (experimental) Crypto::CCryptoManager() (#255)
- 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)
1 parent b25662f commit 1af4051

File tree

12 files changed

+393
-110
lines changed

12 files changed

+393
-110
lines changed

DependentExtensions/Swig/DLL_Swig/dll_csharp_bindings.vcxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,7 @@
586586
</ItemGroup>
587587
<ItemGroup>
588588
<ClCompile Include="..\..\..\bindings\csharp\wrapper\slikenet_wrapper.cpp" />
589+
<ClCompile Include="..\..\..\Source\src\crypto\cryptomanager.cpp" />
589590
<ClCompile Include="..\..\..\Source\src\crypto\factory.cpp" />
590591
<ClCompile Include="..\..\..\Source\src\crypto\fileencrypter.cpp" />
591592
<ClCompile Include="..\..\..\Source\src\crypto\securestring.cpp" />
@@ -697,6 +698,7 @@
697698
</ItemGroup>
698699
<ItemGroup>
699700
<ClInclude Include="..\..\..\bindings\csharp\wrapper\slikenet_wrapper.h" />
701+
<ClInclude Include="..\..\..\Source\include\slikenet\crypto\cryptomanager.h" />
700702
<ClInclude Include="..\..\..\Source\include\slikenet\crypto\factory.h" />
701703
<ClInclude Include="..\..\..\Source\include\slikenet\crypto\fileencrypter.h" />
702704
<ClInclude Include="..\..\..\Source\include\slikenet\crypto\ifileencrypter.h" />

DependentExtensions/Swig/DLL_Swig/dll_csharp_bindings.vcxproj.filters

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,9 @@
328328
<ClCompile Include="..\..\..\Source\src\crypto\fileencrypter.cpp">
329329
<Filter>Source Files\crypto</Filter>
330330
</ClCompile>
331+
<ClCompile Include="..\..\..\Source\src\crypto\cryptomanager.cpp">
332+
<Filter>Source Files\crypto</Filter>
333+
</ClCompile>
331334
</ItemGroup>
332335
<ItemGroup>
333336
<ClInclude Include="..\..\..\Source\include\slikenet\_FindFirst.h">
@@ -798,6 +801,9 @@
798801
<ClInclude Include="..\..\..\Source\include\slikenet\crypto\ifileencrypter.h">
799802
<Filter>Header Files\crypto</Filter>
800803
</ClInclude>
804+
<ClInclude Include="..\..\..\Source\include\slikenet\crypto\cryptomanager.h">
805+
<Filter>Header Files\crypto</Filter>
806+
</ClInclude>
801807
</ItemGroup>
802808
<ItemGroup>
803809
<Filter Include="Header Files">

Lib/DLL/DLL.vcxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,7 @@
495495
</Link>
496496
</ItemDefinitionGroup>
497497
<ItemGroup>
498+
<ClCompile Include="..\..\Source\src\crypto\cryptomanager.cpp" />
498499
<ClCompile Include="..\..\Source\src\crypto\factory.cpp" />
499500
<ClCompile Include="..\..\Source\src\crypto\fileencrypter.cpp" />
500501
<ClCompile Include="..\..\Source\src\crypto\securestring.cpp" />
@@ -612,6 +613,7 @@
612613
<ClCompile Include="..\..\Source\src\WSAStartupSingleton.cpp" />
613614
</ItemGroup>
614615
<ItemGroup>
616+
<ClInclude Include="..\..\Source\include\slikenet\crypto\cryptomanager.h" />
615617
<ClInclude Include="..\..\Source\include\slikenet\crypto\factory.h" />
616618
<ClInclude Include="..\..\Source\include\slikenet\crypto\fileencrypter.h" />
617619
<ClInclude Include="..\..\Source\include\slikenet\crypto\ifileencrypter.h" />

Lib/DLL/DLL.vcxproj.filters

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,9 @@
362362
<ClCompile Include="..\..\Source\src\crypto\fileencrypter.cpp">
363363
<Filter>Source Files\crypto</Filter>
364364
</ClCompile>
365+
<ClCompile Include="..\..\Source\src\crypto\cryptomanager.cpp">
366+
<Filter>Source Files\crypto</Filter>
367+
</ClCompile>
365368
</ItemGroup>
366369
<ItemGroup>
367370
<ClInclude Include="..\..\Source\include\slikenet\_FindFirst.h">
@@ -838,5 +841,8 @@
838841
<ClInclude Include="..\..\Source\include\slikenet\crypto\ifileencrypter.h">
839842
<Filter>Header Files\crypto</Filter>
840843
</ClInclude>
844+
<ClInclude Include="..\..\Source\include\slikenet\crypto\cryptomanager.h">
845+
<Filter>Header Files\crypto</Filter>
846+
</ClInclude>
841847
</ItemGroup>
842848
</Project>

Lib/LibStatic/LibStatic.vcxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,7 @@
413413
</Lib>
414414
</ItemDefinitionGroup>
415415
<ItemGroup>
416+
<ClCompile Include="..\..\Source\src\crypto\cryptomanager.cpp" />
416417
<ClCompile Include="..\..\Source\src\crypto\factory.cpp" />
417418
<ClCompile Include="..\..\Source\src\crypto\fileencrypter.cpp" />
418419
<ClCompile Include="..\..\Source\src\crypto\securestring.cpp" />
@@ -530,6 +531,7 @@
530531
<ClCompile Include="..\..\Source\src\WSAStartupSingleton.cpp" />
531532
</ItemGroup>
532533
<ItemGroup>
534+
<ClInclude Include="..\..\Source\include\slikenet\crypto\cryptomanager.h" />
533535
<ClInclude Include="..\..\Source\include\slikenet\crypto\factory.h" />
534536
<ClInclude Include="..\..\Source\include\slikenet\crypto\fileencrypter.h" />
535537
<ClInclude Include="..\..\Source\include\slikenet\crypto\ifileencrypter.h" />

Lib/LibStatic/LibStatic.vcxproj.filters

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,9 @@
362362
<ClCompile Include="..\..\Source\src\crypto\factory.cpp">
363363
<Filter>Source Files\crypto</Filter>
364364
</ClCompile>
365+
<ClCompile Include="..\..\Source\src\crypto\cryptomanager.cpp">
366+
<Filter>Source Files\crypto</Filter>
367+
</ClCompile>
365368
</ItemGroup>
366369
<ItemGroup>
367370
<ClInclude Include="..\..\Source\include\slikenet\_FindFirst.h">
@@ -838,5 +841,8 @@
838841
<ClInclude Include="..\..\Source\include\slikenet\crypto\ifileencrypter.h">
839842
<Filter>Header Files\crypto</Filter>
840843
</ClInclude>
844+
<ClInclude Include="..\..\Source\include\slikenet\crypto\cryptomanager.h">
845+
<Filter>Header Files\crypto</Filter>
846+
</ClInclude>
841847
</ItemGroup>
842848
</Project>

Source/include/slikenet/WindowsIncludes.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* of patent rights can be found in the RakNet Patents.txt file in the same directory.
88
*
99
*
10-
* Modified work: Copyright (c) 2016-2018, SLikeSoft UG (haftungsbeschränkt)
10+
* Modified work: Copyright (c) 2016-2019, SLikeSoft UG (haftungsbeschränkt)
1111
*
1212
* This source code was modified by SLikeSoft. Modifications are licensed under the MIT-style
1313
* license found in the license.txt file in the root directory of this source tree.
@@ -21,7 +21,6 @@
2121
#elif defined (_WIN32)
2222
#include <winsock2.h>
2323
#include <windows.h>
24-
#include <Wincrypt.h> // used for CryptProtectMemory, CryptUnprotectMemory, CRYPTPROTECTMEMORY_BLOCK_SIZE
2524
#include <ws2tcpip.h>
2625
#include <IPHlpApi.h> // used for GetAdaptersAddresses()
2726
#pragma comment(lib, "IPHLPAPI.lib") // used for GetAdaptersAddresses()
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright (c) 2019, SLikeSoft UG (haftungsbeschränkt)
3+
*
4+
* This source code is licensed under the MIT-style license found in the license.txt
5+
* file in the root directory of this source tree.
6+
*/
7+
#pragma once
8+
9+
#include <openssl/evp.h> // used for EVP_xxxx
10+
11+
namespace SLNet
12+
{
13+
namespace Experimental
14+
{
15+
namespace Crypto
16+
{
17+
class CCryptoManager
18+
{
19+
private:
20+
// class members
21+
// note: using distinct contexts for encryption/decryption to prevent potential for race conditions
22+
// #med - consider moving to SessionEncrypter class
23+
static EVP_CIPHER_CTX m_decryptionContext;
24+
static EVP_CIPHER_CTX m_encryptionContext;
25+
static unsigned char m_initializationVector[EVP_MAX_IV_LENGTH];
26+
static unsigned char m_sessionKey[EVP_MAX_KEY_LENGTH];
27+
static bool m_Initialized;
28+
29+
public:
30+
// initialization
31+
static bool Initialize();
32+
33+
public:
34+
// session encryption
35+
static bool EncryptSessionData(const unsigned char* plaintext, size_t dataLength, unsigned char* outBuffer, size_t& inOutBufferSize);
36+
static bool DecryptSessionData(const unsigned char* encryptedtext, size_t dataLength, unsigned char* outBuffer, size_t& inOutBufferSize);
37+
static bool GetRequiredEncryptionBufferSize(size_t& encryptionDataByteLength);
38+
39+
public:
40+
// secure memory management methods
41+
// #med - consider moving to separate class (SecureMemory/MemoryManager)
42+
static void* AllocateSecureMemory(size_t size);
43+
static void FreeSecureMemory(void* pointer, size_t size);
44+
static void SecureClearMemory(void* pointer, size_t dataSize);
45+
};
46+
}
47+
}
48+
}

Source/include/slikenet/crypto/securestring.h

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, SLikeSoft UG (haftungsbeschränkt)
2+
* Copyright (c) 2018-2019, SLikeSoft UG (haftungsbeschränkt)
33
*
44
* This source code is licensed under the MIT-style license found in the license.txt
55
* file in the root directory of this source tree.
@@ -24,10 +24,12 @@ namespace SLNet
2424
private:
2525
bool m_UTF8Mode;
2626
bool m_wasFlushed;
27-
size_t m_EncryptedBufferSize;
28-
size_t m_numBufferUsed;
29-
size_t m_UnencryptedBufferSize;
30-
char* m_EncryptedMemory;
27+
size_t m_EncryptedBufferSize; // size of the buffer for the encrypted string
28+
size_t m_numBufferSize; // size of the actual supported string buffer (excluding the trailing \0-terminator)
29+
size_t m_numBufferUsed; // size of the available buffer currently used
30+
size_t m_numEncryptedBufferUsed; // size of the encrypted buffer which is used and contains the encrypted data
31+
size_t m_UnencryptedBufferSize; // size of the buffer allocated to retrieve the decrypted string
32+
unsigned char* m_EncryptedMemory;
3133
char* m_UnencryptedBuffer;
3234

3335
// constructor / destructor
@@ -37,7 +39,7 @@ namespace SLNet
3739

3840
// container methods
3941
public:
40-
bool AddChar(char* character);
42+
size_t AddChar(char* character);
4143
bool RemoveLastChar();
4244
void Reset();
4345

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
/*
2+
* Copyright (c) 2019, SLikeSoft UG (haftungsbeschränkt)
3+
*
4+
* This source code is licensed under the MIT-style license found in the license.txt
5+
* file in the root directory of this source tree.
6+
*/
7+
#include "slikenet/crypto/cryptomanager.h"
8+
9+
#include "slikenet/assert.h" // used for RakAssert
10+
#include <limits> // used for std::numeric_limits<>
11+
12+
// prevent max/min macros getting defined (breaking numeric_limits<>::max() / ::min() usage) through the indirect windows.h include in the OpenSSL includes
13+
#ifdef _WIN32
14+
#define NOMINMAX
15+
#endif
16+
17+
#include <openssl/err.h> // used for ERR_xxxx
18+
#include <openssl/evp.h> // used for EVP_xxxx, OpenSSL_add_all_algorithms()
19+
#include <openssl/rand.h> // used for RAND_xxxx
20+
21+
namespace SLNet
22+
{
23+
namespace Experimental
24+
{
25+
namespace Crypto
26+
{
27+
bool CCryptoManager::Initialize()
28+
{
29+
if (m_Initialized) {
30+
return true; // already initialized
31+
}
32+
33+
ERR_load_crypto_strings();
34+
OpenSSL_add_all_algorithms();
35+
36+
// #high - replace with EGADS
37+
RAND_screen();
38+
39+
if (RAND_bytes(m_sessionKey, EVP_MAX_KEY_LENGTH) == 0) {
40+
return false; // failed to initialize the random session key
41+
}
42+
if (RAND_pseudo_bytes(m_initializationVector, EVP_MAX_IV_LENGTH) == 0) {
43+
return false; // failed to initialize the initialization vector
44+
}
45+
46+
m_Initialized = true;
47+
return true;
48+
}
49+
50+
bool CCryptoManager::EncryptSessionData(const unsigned char* plaintext, size_t dataLength, unsigned char* outBuffer, size_t& inOutBufferSize)
51+
{
52+
if (!Initialize()) {
53+
return false; // CryptoManager failed to initialize
54+
}
55+
56+
size_t requiredBufferSize = dataLength;
57+
if (!GetRequiredEncryptionBufferSize(requiredBufferSize)) {
58+
return false; // dataLength too large (integer overflow)
59+
}
60+
if (inOutBufferSize < requiredBufferSize) {
61+
return false; // out buffer (potentially) too small
62+
}
63+
64+
// #high - review usage of the CBC mode here --- not the best nowadays
65+
// #med - add engine support to use HW-acceleration
66+
if (EVP_EncryptInit_ex(&m_encryptionContext, EVP_aes_256_cbc(), nullptr, m_sessionKey, m_initializationVector) == 0) {
67+
return false; // failed to initialize the encryption context
68+
}
69+
70+
int bytesWritten1;
71+
// note: static_cast<> safe here, since GetRequiredEncrpytionBufferSize()-check ensured dataLength is <= int::max()
72+
if (EVP_EncryptUpdate(&m_encryptionContext, outBuffer, &bytesWritten1, plaintext, static_cast<int>(dataLength)) == 0) {
73+
return false; // encryption failed
74+
}
75+
RakAssert(static_cast<size_t>(bytesWritten1) <= inOutBufferSize);
76+
int bytesWritten2;
77+
if (EVP_EncryptFinal_ex(&m_encryptionContext, outBuffer + bytesWritten1, &bytesWritten2) == 0) {
78+
return false; // failed final encryption step
79+
}
80+
RakAssert(static_cast<size_t>(bytesWritten1) + static_cast<size_t>(bytesWritten2) <= inOutBufferSize);
81+
82+
inOutBufferSize = static_cast<size_t>(bytesWritten1) + static_cast<size_t>(bytesWritten2);
83+
return true;
84+
}
85+
86+
bool CCryptoManager::DecryptSessionData(const unsigned char* encryptedtext, size_t dataLength, unsigned char* outBuffer, size_t& inOutBufferSize)
87+
{
88+
if (!Initialize()) {
89+
return false; // CryptoManager failed to initialize
90+
}
91+
92+
// #med - extend support for inOutBufferSize > int::max()
93+
if (inOutBufferSize > static_cast<size_t>(std::numeric_limits<int>::max())) {
94+
// 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
95+
// being limited to int::max() through their returned written bytes values. Due to the next check (inOutBufferSize < dataLength) it's implicitly ensured that
96+
// dataLength doesn't exceed the limit either.
97+
return false; // specified length exceeds max supported size
98+
}
99+
100+
if (inOutBufferSize < dataLength) {
101+
// prevent potential buffer overflow, even though it's possible that the encryptedtext is padded and hence the effectively required
102+
// inOutBufferSize is less than the provided dataLength, since we cannot determine this before running the actual decryption - so consider
103+
// this an invalid call, if the provided inOutBufferSize is smaller than the incoming encrypted text's length
104+
return false;
105+
}
106+
107+
// #high - review usage of the CBC mode here --- not the best nowadays
108+
// #med - add engine support to use HW-acceleration
109+
if (EVP_DecryptInit_ex(&m_decryptionContext, EVP_aes_256_cbc(), nullptr, m_sessionKey, m_initializationVector) == 0) {
110+
return false; // failed to initialize the decryption context
111+
}
112+
113+
int bytesWritten1;
114+
// static cast safe due to size-check above
115+
if (EVP_DecryptUpdate(&m_decryptionContext, outBuffer, &bytesWritten1, encryptedtext, static_cast<int>(dataLength)) == 0) {
116+
return false; // decryption failed
117+
}
118+
RakAssert(static_cast<size_t>(bytesWritten1) <= inOutBufferSize);
119+
int bytesWritten2;
120+
if (EVP_DecryptFinal_ex(&m_decryptionContext, outBuffer, &bytesWritten2) == 0) {
121+
return false; // failed final decryption step
122+
}
123+
RakAssert(static_cast<size_t>(bytesWritten1) + static_cast<size_t>(bytesWritten2) <= inOutBufferSize);
124+
125+
inOutBufferSize = static_cast<size_t>(bytesWritten1) + static_cast<size_t>(bytesWritten2);
126+
return true;
127+
}
128+
129+
bool CCryptoManager::GetRequiredEncryptionBufferSize(size_t& encryptionDataByteLength)
130+
{
131+
// note: not using EVP_CIPHER_CTX_block_size() here, since otherwise we would have to make sure that the encryption context was initialized already
132+
const int blockSize = EVP_CIPHER_block_size(EVP_aes_256_cbc());
133+
134+
// 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)
135+
// Hence, by definition the required encryption buffer size is dataLength + blockSize*2 -1.
136+
// 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
137+
// on the design level to prevent possible incompatibilities with future OpenSSL changes.
138+
139+
if (encryptionDataByteLength + blockSize * 2 - 1 < encryptionDataByteLength) {
140+
return false; // prevent integer overflow
141+
}
142+
143+
encryptionDataByteLength = encryptionDataByteLength + blockSize * 2 - 1;
144+
145+
// #med - extend support for datalength > int::max()
146+
// verify that the specified length doesn't exceed the max supported data length
147+
return encryptionDataByteLength <= static_cast<size_t>(std::numeric_limits<int>::max());
148+
}
149+
150+
void* CCryptoManager::AllocateSecureMemory(size_t size)
151+
{
152+
// #high - route through memory manager (aka: same as OP_NEW_ARRAY)
153+
return OPENSSL_malloc(size);
154+
}
155+
156+
void CCryptoManager::FreeSecureMemory(void* pointer, size_t size)
157+
{
158+
// make sure the memory is cleared before it's freed again
159+
SecureClearMemory(pointer, size);
160+
161+
// #high - route through memory manager (aka: same as OP_NEW_ARRAY)
162+
return OPENSSL_free(pointer);
163+
}
164+
165+
void CCryptoManager::SecureClearMemory(void* pointer, size_t size)
166+
{
167+
OPENSSL_cleanse(pointer, size);
168+
}
169+
170+
// initialization list
171+
EVP_CIPHER_CTX CCryptoManager::m_decryptionContext;
172+
EVP_CIPHER_CTX CCryptoManager::m_encryptionContext;
173+
unsigned char CCryptoManager::m_sessionKey[EVP_MAX_KEY_LENGTH];
174+
unsigned char CCryptoManager::m_initializationVector[EVP_MAX_IV_LENGTH];
175+
bool CCryptoManager::m_Initialized = false;
176+
}
177+
}
178+
}

0 commit comments

Comments
 (0)