Added asynchronous key generation to network library

Replaced malloc() calls with new[] calls to make code more consistent
Fixed memory leaks
KeyData is now generated as a pointer
This commit is contained in:
Gabriel Tofvesson 2017-10-16 20:37:06 +02:00
parent 79d518f97e
commit 04ef028372
5 changed files with 132 additions and 44 deletions

View File

@ -72,9 +72,7 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<TargetExt>64.lib</TargetExt>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<TargetExt>.lib</TargetExt>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
@ -130,6 +128,8 @@
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalLibraryDirectories>$(SolutionDir)libs\win_crypto++\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>cryptlib.lib;winsqlite3.lib;shlwapi.lib;Crypt32.lib;Ws2_32.lib;Mswsock.lib;AdvApi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<Lib>
<AdditionalLibraryDirectories>$(SolutionDir)libs\win_crypto++\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>

View File

@ -7,6 +7,21 @@
namespace Crypto {
static unsigned long x = 123456789, y = 362436069, z = 521288629;
unsigned long xorshf96(void) { //period 2^96-1
unsigned long t;
x ^= x << 16;
x ^= x >> 5;
x ^= x << 1;
t = x;
x = y;
y = z;
z = t ^ x ^ y;
return z;
}
namespace AES {
// -------- AES START --------
@ -38,7 +53,7 @@ namespace Crypto {
(*resultingSize) = t;
char* cipher = (char*)malloc(t);
char* cipher = (char*)new char[t];
memcpy(cipher, ciphertext.c_str(), t);
return cipher;
@ -61,11 +76,11 @@ namespace Crypto {
*resultSize = decryptedtext.size();
char* c = (char*)malloc(*resultSize);
char* c = (char*)new char[*resultSize];
//memset(c, 0, decryptedtext.size());
memcpy(c, decryptedtext.c_str(), decryptedtext.size());
decryptedtext.~basic_string();
//decryptedtext.~basic_string();
return c;
}
@ -74,9 +89,15 @@ namespace Crypto {
Payload aes_auto_encrypt(void* msg, ulong_64b size) {
char* message = (char*)msg;
Payload p;
srand(time(NULL));
p.key = (char*)malloc(sizeof(AES_KEY));
// Generate random number 'n stuff
for (volatile unsigned int i = ((unsigned int)rand())%4096; i > 0; --i) xorshf96();
time_t t = (time_t) xorshf96();
if (sizeof(long) < sizeof(time_t)) t ^= xorshf96() << ((sizeof(time_t) / 2) + (sizeof(time_t)%2)); // Fill ALL the bits!
srand(time(NULL)+(signed long long)t);
p.key = (char*)new char[sizeof(AES_KEY)];
AES_KEY k = (AES_KEY)rand();
p.keySize = sizeof(AES_KEY);
@ -143,8 +164,8 @@ namespace Crypto {
namespace RSA {
// -------- RSA START --------
KeyData rsa_gen_keys() {
KeyData k;
KeyData* rsa_gen_keys() {
KeyData* k = new KeyData();
CryptoPP::InvertibleRSAFunction params;
CryptoPP::RandomPool rng;
@ -153,8 +174,8 @@ namespace Crypto {
rng.IncorporateEntropy((const byte*)&t, sizeof(t) * 8);
params.GenerateRandomWithKeySize(rng, 3072);
k.privKey = CryptoPP::RSA::PrivateKey(params);
k.publKey = CryptoPP::RSA::PublicKey(params);
k->privKey = CryptoPP::RSA::PrivateKey(params);
k->publKey = CryptoPP::RSA::PublicKey(params);
return k;
}
@ -164,7 +185,7 @@ namespace Crypto {
//func.DEREncodePublicKey(queue);
byte* shortened = (byte*)malloc(*rSize=queue.TotalBytesRetrievable());
byte* shortened = (byte*)new byte[*rSize=queue.TotalBytesRetrievable()];
memset(shortened, 0, *rSize);
std::vector<byte> spk;
@ -191,9 +212,10 @@ namespace Crypto {
*resultingSize = cipher.size();
char* c = (char*)malloc(cipher.size());
memset(c, 0, cipher.size());
char* c = (char*)new char[cipher.size()];
//memset(c, 0, cipher.size());
memcpy(c, cipher.c_str(), cipher.size());
return c;
}
@ -210,9 +232,11 @@ namespace Crypto {
*resultingSize = clear.size();
char* c = (char*)malloc(clear.size());
memset(c, 0, clear.size());
char* c = (char*)new char[clear.size()];
//memset(c, 0, clear.size());
memcpy(c, clear.c_str(), clear.size());
return c;
}
// -------- RSA END --------
@ -220,14 +244,21 @@ namespace Crypto {
char* full_auto_encrypt(void* msg, ulong_64b mSize, CryptoPP::RSA::PublicKey& pk, ulong_64b* rSize) {
AES::Payload p = AES::aes_auto_encrypt(msg, mSize);
p.key = RSA::rsa_encrypt(p.key, p.keySize, pk, &p.keySize);
char *c = RSA::rsa_encrypt(p.key, p.keySize, pk, &p.keySize);
delete[] p.key;
p.key = c;
return p.serialize(rSize);
}
char* full_auto_decrypt(void* msg, CryptoPP::RSA::PrivateKey& pk, ulong_64b* rSize) {
ulong_64b size;
AES::Payload p = AES::deserializePayload(msg, &size);
p.key = RSA::rsa_decrypt(p.key, p.keySize, pk, &p.keySize);
return AES::aes_auto_decrypt(p, rSize);
char *c = RSA::rsa_decrypt(p.key, p.keySize, pk, &p.keySize);
delete[] p.key;
p.key = c;
c = AES::aes_auto_decrypt(p, rSize);
delete[] p.key;
delete[] p.ldPayload;
return c;
}
}

View File

@ -56,7 +56,7 @@ namespace Crypto {
char* serializeKey(CryptoPP::RSA::PublicKey&, ulong_64b* rSize);
KeyData rsa_gen_keys();
KeyData* rsa_gen_keys();
char* rsa_encrypt(void* message, ulong_64b size, CryptoPP::RSA::PublicKey& pubKey, ulong_64b* resultingSize);
char* rsa_decrypt(void* message, ulong_64b size, CryptoPP::RSA::PrivateKey& privKey, ulong_64b* resultingSize);
}

View File

@ -1,6 +1,7 @@
#include "Net.h"
#include "Support.h"
#include <future>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
@ -9,6 +10,26 @@
namespace IO {
AsyncKeys::AsyncKeys() {
gen = std::async(std::launch::async, [this]() {
Crypto::RSA::KeyData *data = Crypto::RSA::rsa_gen_keys();
done = true;
return data;
});
done = suppressDelete = false;
}
AsyncKeys::AsyncKeys(Crypto::RSA::KeyData* predef) {
done = suppressDelete = true;
keys = predef;
}
AsyncKeys::~AsyncKeys() { if (!suppressDelete) delete keys; }
Crypto::RSA::KeyData* AsyncKeys::get() {
if (!done) keys = gen.get();
return keys;
}
bool cryptoLevelsAreCompatible(CryptoLevel l1, CryptoLevel l2) {
return !(((l1 == CryptoLevel::None) && (l2 == CryptoLevel::Force)) || ((l2 == CryptoLevel::None) && (l1 == CryptoLevel::Force)));
}
@ -45,7 +66,7 @@ namespace IO {
void NetClient::sharedSetup(bool setupKeys) {
if (setupKeys && (preferEncrypted != CryptoLevel::None)) keys = Crypto::RSA::rsa_gen_keys();
if (setupKeys && (preferEncrypted != CryptoLevel::None)) keyData = new AsyncKeys();
packets = new std::vector<Packet>();
sparse = new std::vector<char>();
outPacketBuf = new std::vector<Packet>();
@ -106,17 +127,26 @@ namespace IO {
sharedSetup(setupKeys);
}
NetClient::NetClient(char* ipAddr, char* port, Crypto::RSA::KeyData& keys, CryptoLevel level) : NetClient(ipAddr, port, level, false) { this->keys = keys; }
NetClient::NetClient(char* ipAddr, char* port, AsyncKeys *keyData, CryptoLevel level) : NetClient(ipAddr, port, level, false) { this->keyData = keyData; }
NetClient::NetClient(SOCKET wrap, bool noThread, Crypto::RSA::KeyData& keys, CryptoLevel preferEncrypted, bool startNegotiate) :
NetClient::NetClient(SOCKET wrap, bool noThread, AsyncKeys &keyData, CryptoLevel preferEncrypted, bool startNegotiate) :
preferEncrypted(preferEncrypted), startNegotiate(startNegotiate)
{
_socket = wrap;
this->noThread = noThread;
sharedSetup(true);
this->keyData = new AsyncKeys(keyData.get());
sharedSetup(false);
}
NetClient::~NetClient() {
delete keyData;
for (std::pair<char*, std::pair<ulong_64b, char*>*> *p : associatedData) {
delete[] p->first;
delete[] p->second->second;
delete p->second;
delete p;
}
associatedData.clear();
packets->clear();
delete packets;
sparse->clear();
@ -144,6 +174,7 @@ namespace IO {
else if (i == 0) --wIdx;
}
commTime = time(nullptr);
delete[] c;
return true;
}
@ -155,14 +186,24 @@ namespace IO {
char* c = new char[sizeof(ulong_64b)];
memcpy(c, &size, sizeof(ulong_64b));
for (ulong_64b wIdx = 0; wIdx < sizeof(ulong_64b); ++wIdx) {
if ((i = send(_socket, c + wIdx, 1, 0)) == SOCKET_ERROR) return false;
if ((i = send(_socket, c + wIdx, 1, 0)) == SOCKET_ERROR) {
delete[] message;
delete[] c;
return false;
}
else if (i == 0) --wIdx;
}
for (ulong_64b wIdx = 0; wIdx < size; ++wIdx) {
if ((i = send(_socket, message + wIdx, 1, 0)) == SOCKET_ERROR) return false;
if ((i = send(_socket, message + wIdx, 1, 0)) == SOCKET_ERROR) {
delete[] message;
delete[] c;
return false;
}
else if (i == 0) --wIdx;
}
commTime = time(nullptr);
if(autoDelete) delete[] message;
delete[] c;
return true;
}
bool NetClient::write(void* message, ulong_64b size) {
@ -234,7 +275,7 @@ namespace IO {
delete[] size;
p.message = readSparse(sparse, p.size);
if (encrypted) p.message = Crypto::full_auto_decrypt(p.message, keys.privKey, &p.size);
if (encrypted) p.message = Crypto::full_auto_decrypt(p.message, keyData->get()->privKey, &p.size);
p.packetUID = p.message[0];
if (p.packetUID != expectedNextPUID) continue; // Detect packet replay/mismatch
@ -267,7 +308,7 @@ namespace IO {
}
else {
ulong_64b size;
char* c = Crypto::RSA::serializeKey(keys.publKey, &size);
char* c = Crypto::RSA::serializeKey(keyData->get()->publKey, &size);
_write(c, size); // This shouldn't be encrypted
delete[] c;
}
@ -428,7 +469,7 @@ namespace IO {
if (_open) throw new std::exception();
else break;
}
NetClient* cli = new NetClient(client, true, keys, this->pref, false);
NetClient* cli = new NetClient(client, true, *keyData, this->pref, false);
clients->push_back(cli);
for (ulong_64b t = 0; t < handlers->size(); ++t)
if (handlers->at(t)(cli))
@ -455,21 +496,23 @@ namespace IO {
}
NetServer::NetServer(char* port, std::function<bool(NetClient*)> f, CryptoLevel pref) : pref(pref) {
if (pref != CryptoLevel::None) keys = Crypto::RSA::rsa_gen_keys();
if (pref != CryptoLevel::None) keyData = new AsyncKeys();
sharedSetup(port, f);
}
NetServer::NetServer(char* port, std::function<bool(NetClient*)> f, Crypto::RSA::KeyData& keys, CryptoLevel level) : pref(level) {
this->keys = keys;
NetServer::NetServer(char* port, std::function<bool(NetClient*)> f, AsyncKeys &keyData, CryptoLevel level) : pref(level) {
this->keyData = new AsyncKeys(keyData.get());
sharedSetup(port, f);
}
NetServer::~NetServer() {
delete keyData;
if (_open) close();
handlers->clear();
delete handlers;
clients->clear();
for (NetClient *cli : *clients) delete cli;
clients->clear();
delete clients;
onDestroy();
}

View File

@ -26,10 +26,24 @@
#include <vector>
#include <thread>
#include <functional>
#include <future>
namespace IO {
class AsyncKeys {
private:
std::future<Crypto::RSA::KeyData*> gen;
Crypto::RSA::KeyData* keys;
volatile bool done;
bool suppressDelete;
public:
AsyncKeys();
AsyncKeys(Crypto::RSA::KeyData* predef);
~AsyncKeys();
Crypto::RSA::KeyData* get();
};
enum CryptoLevel { None, Prefer, Force };
struct Packet {
@ -51,25 +65,24 @@ namespace IO {
volatile bool _open; // Whether or not connection is open
bool canWrite; // Whether or not writing to peer is possible
bool noThread; // Whether or not reading incoming data should be / is being done in a separate thread
//char rBuf[BUFSIZE]; // Recieve buffer
std::vector<char> rBuf;
CryptoLevel preferEncrypted = CryptoLevel::None;// Whether or not the socket should attempt to request an encrypted channel
bool encrypted = false; // Whether or not negotiation determined the use of an encrypted channel
bool firstMessage = true; // Whether or not negotiation has yet ocurred
ulong_64b fm_neg_size;
bool fm_neg_hasLevel = false;
bool fm_neg_hasSize = false;
bool startNegotiate = false;
ulong_64b fm_neg_size; // First message negotiation size
bool fm_neg_hasLevel = false; // First message has crypto level
bool fm_neg_hasSize = false; // Got negotiation size from first message
bool startNegotiate = false; // Whether or not to initiate negotiation
char expectedNextPUID = 0;
char remotePUID = 0;
std::vector<char>* sparse;
std::vector<Packet>* outPacketBuf;
Crypto::RSA::KeyData keys; // Client's keysets (if using encryption)
AsyncKeys *keyData; // Client's keysets (if using encryption)
CryptoPP::RSAFunction pK; // Remote host's public key (if using encryption)
NetClient(char*, char*, CryptoLevel, bool); // Underlying setup for regular constructors
NetClient(SOCKET, bool, CryptoLevel, bool);// Special setup constructor
NetClient(SOCKET, bool, Crypto::RSA::KeyData&, CryptoLevel = CryptoLevel::None, bool = false);// Create wrapper for existing socket
NetClient(SOCKET, bool, AsyncKeys&, CryptoLevel = CryptoLevel::None, bool = false);// Create wrapper for existing socket
void sharedSetup(bool); // Setup function for all constructor
bool _write(char*, ulong_64b); // Internal write function. Doesn't do any of the fancy auto encryption: just raw write...
bool writeBufferedPackets(); // Flushes and deletes buffer
@ -85,9 +98,10 @@ namespace IO {
std::function<void()> onDestroy; // Event handler called when NetClient object is destroyed
public:
bool autoPing = true; // Whether or not client should actively check connection state
bool autoDelete = false;
time_t commTime; // Latest time a transaction occurred
NetClient(char* ipAddr, char* port, CryptoLevel = CryptoLevel::None);// Standard constructor for creating connection
NetClient(char* ipAddr, char* port, Crypto::RSA::KeyData&, CryptoLevel);// Standard constructor for creating connection with predefined keys
NetClient(char* ipAddr, char* port, AsyncKeys*, CryptoLevel);// Standard constructor for creating connection with predefined keys
~NetClient();
bool close();
void closeWrite();
@ -113,7 +127,7 @@ namespace IO {
friend class NetClient;
private:
CryptoLevel pref;
Crypto::RSA::KeyData keys; // Server's keysets (if using encryption)
AsyncKeys *keyData; // Server's keysets (if using encryption)
std::function<void()> onDestroy;
volatile bool _open;
@ -126,7 +140,7 @@ namespace IO {
public:
std::function<bool(NetClient*)> timeoutHandler;
NetServer(char* port, std::function<bool(NetClient*)> = nullptr, CryptoLevel = CryptoLevel::None);
NetServer(char* port, std::function<bool(NetClient*)>, Crypto::RSA::KeyData&, CryptoLevel);
NetServer(char* port, std::function<bool(NetClient*)>, AsyncKeys&, CryptoLevel);
~NetServer();
bool isOpen();
CryptoLevel getCryptoPreference();