mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-16 14:16:09 -06:00
288 lines
7.6 KiB
C++
288 lines
7.6 KiB
C++
#include <Core/Core.h>
|
||
|
||
#undef _WIN32
|
||
#include <openssl/rand.h>
|
||
#include "AESStream.h"
|
||
|
||
void AESInit();
|
||
|
||
//
|
||
// Код исходит из того, что AES_BLOCK_SIZE == 2^n
|
||
// Code has an assumption AES_BLOCK_SIZE == 2^n bytes
|
||
//----------------------------------------------------------------------------------------------
|
||
String AESRandomString(int length)
|
||
{
|
||
AESInit();
|
||
|
||
static bool sslRandInit = false;
|
||
if (!sslRandInit)
|
||
{
|
||
sslRandInit = true;
|
||
}
|
||
|
||
StringBuffer ident(length);
|
||
RAND_bytes((unsigned char *) ~ident, length);
|
||
return ident;
|
||
}
|
||
|
||
String AESPadString(const String &s, int l)
|
||
{
|
||
AESInit();
|
||
|
||
int addL = s.GetLength() % l;
|
||
if (!addL)
|
||
return s;
|
||
|
||
String outS(s);
|
||
outS << AESRandomString(l - addL);
|
||
return outS;
|
||
}
|
||
|
||
dword rdtsc()
|
||
{
|
||
dword _out;
|
||
#ifndef flagGCC
|
||
__asm
|
||
{
|
||
rdtsc
|
||
mov _out, eax
|
||
}
|
||
#else
|
||
_out = Random();
|
||
#endif
|
||
return _out;
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------------
|
||
bool aesInitDone = false;
|
||
void AESInit()
|
||
{
|
||
if (aesInitDone)
|
||
return;
|
||
aesInitDone = true;
|
||
|
||
if (RAND_status())
|
||
return;
|
||
|
||
qword qw = (((qword) GetTickCount()) << 16) ^ ((qword) (GetSysTime().Get())) ^ Random();
|
||
RAND_seed(&qw, sizeof(qw));
|
||
|
||
#ifdef PLATFORM_WIN32
|
||
RAND_screen();
|
||
#endif
|
||
|
||
while (!RAND_status())
|
||
{
|
||
dword dw = Random();
|
||
RAND_seed(&dw, sizeof(dword));
|
||
dw = rdtsc();
|
||
RAND_seed(&dw, sizeof(dword));
|
||
|
||
static int hops = 0;
|
||
if (hops > 1000000)
|
||
throw "AESInit: Random generator init error";//Ошибка инициализации генератора случайных чисел";
|
||
}
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------------
|
||
AESEncoderStream::AESEncoderStream(qword _size, const String &_key) throw (const char *)
|
||
: size (_size)
|
||
, blocks (_size / AES_BLOCK_SIZE)
|
||
, blocksDone (0)
|
||
, iv0 (AESRandomString(AES_BLOCK_SIZE))
|
||
, encodedHeader (false)
|
||
{
|
||
AESInit();
|
||
|
||
*((dword *) ~iv0) ^= (Random() ^ rdtsc());
|
||
*(((dword *) ~iv0) + 1) ^= (Random() ^ GetTickCount());
|
||
if (_size & (AES_BLOCK_SIZE-1))
|
||
++blocks;
|
||
|
||
iv.Cat(iv0);
|
||
|
||
if (_key.GetLength() != 16 && _key.GetLength() != 24 && _key.GetLength() != 32)
|
||
throw "AESEncoderStream::ctor: Key must be 16, 24 or 32 bytes long";//Неверная длина ключа. Нужно 16, 24 или 32 байт";
|
||
if (AES_set_encrypt_key((const unsigned char *) ~_key, _key.GetLength() << 3, &key))
|
||
throw "AESEncoderStream::ctor: Could not setup AES key. OpenSSL problem?";//Не установился ключ для AES. Проблема в OpenSSL?";
|
||
}
|
||
|
||
int AESEncoderStream::AddData(const String &s)
|
||
{
|
||
int sl = s.GetLength();
|
||
|
||
if (blocksDone == blocks)
|
||
return 0;
|
||
|
||
qword sizeLeft = size - blocksDone*AES_BLOCK_SIZE;
|
||
if (((qword) s.GetLength()) <= sizeLeft)
|
||
data << s;
|
||
else
|
||
{
|
||
sl = (int) sizeLeft;
|
||
data << s.Left(sl);
|
||
}
|
||
|
||
if (blocksDone*AES_BLOCK_SIZE+data.GetLength() == size)
|
||
{
|
||
//padding last bytes to fit into AES_BLOCK_SIZE
|
||
//последние данные всегда добавляются шумом до размера, кратного блоку
|
||
int extraLength = size % AES_BLOCK_SIZE;
|
||
if (extraLength)
|
||
data << AESRandomString(AES_BLOCK_SIZE - extraLength);
|
||
}
|
||
return sl;
|
||
}
|
||
|
||
String AESEncoderStream::GetHeader()
|
||
{
|
||
if (encodedHeader)
|
||
return "";
|
||
|
||
String header;
|
||
|
||
dword magic1, magic2;
|
||
String magicS = AESRandomString(sizeof(qword));
|
||
memcpy(&magic1, ~magicS, sizeof(dword));
|
||
memcpy(&magic2, (~magicS) + sizeof(dword), sizeof(dword));
|
||
dword magicDelta = AES_CONTAINER_DWORD_HEADER - (magic1 + magic2);
|
||
magic2 += magicDelta;
|
||
header.Cat((const char *)&magic1, sizeof(dword));
|
||
header.Cat((const char *)&magic2, sizeof(dword));
|
||
|
||
header << iv0;
|
||
|
||
qword sze = (qword) size;
|
||
header.Cat((const char *) &sze, sizeof(sze));
|
||
header = AESPadString(header, AES_BLOCK_SIZE);
|
||
|
||
StringBuffer outBuffer(header.GetLength());
|
||
|
||
for (int offs = 0; offs+AES_BLOCK_SIZE <= header.GetLength(); offs += AES_BLOCK_SIZE)
|
||
AES_ecb_encrypt(((const unsigned char *) ~header)+offs, ((unsigned char *) ~outBuffer)+offs, &key, true);
|
||
|
||
encodedHeader = true;
|
||
return outBuffer;
|
||
}
|
||
|
||
String AESEncoderStream::GetEncryptedData()
|
||
{
|
||
if (blocksDone == blocks)
|
||
return "";
|
||
|
||
qword overallBlocks = (qword) (data.GetLength()/AES_BLOCK_SIZE);
|
||
int doBlocks = (int) min(overallBlocks, blocks-blocksDone);
|
||
if (!doBlocks)
|
||
return "";
|
||
|
||
int doSize = doBlocks*AES_BLOCK_SIZE;
|
||
StringBuffer outData(doSize);
|
||
|
||
AES_cbc_encrypt((const unsigned char *) ~data, (unsigned char *) ~outData, (const unsigned long) doSize, &key, (unsigned char *) ~iv, true);
|
||
if (doSize < data.GetLength())
|
||
data.Remove(0, doSize);
|
||
else
|
||
data.Clear();
|
||
blocksDone += doBlocks;
|
||
|
||
if (encodedHeader)
|
||
return outData;
|
||
else
|
||
return (GetHeader()+String(outData));
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------------
|
||
AESDecoderStream::AESDecoderStream(const String &_key) throw (const char *)
|
||
: size (0L)
|
||
, sizeDone (0L)
|
||
, iv (AES_BLOCK_SIZE)
|
||
, decodedHeader (false)
|
||
{
|
||
AESInit();
|
||
|
||
if (_key.GetLength() != 16 && _key.GetLength() != 24 && _key.GetLength() != 32)
|
||
throw "AESDecoderStream::ctor: Key must be 16, 24 or 32 bytes long";//Неверная длина ключа. Нужно 16, 24 или 32 байт";
|
||
if (AES_set_decrypt_key((const unsigned char *) ~_key, _key.GetLength() << 3, &key))
|
||
throw "AESDecoderStream::ctor: Could not setup AES key. OpenSSL problem?";//Не установился ключ для AES. Проблема в OpenSSL?";
|
||
}
|
||
|
||
int AESDecoderStream::AddData(const String &s)
|
||
{
|
||
if (!size)
|
||
{
|
||
data << s;
|
||
int io = data.GetLength();
|
||
return s.GetLength();
|
||
}
|
||
|
||
qword sz = size & ~((qword) (AES_BLOCK_SIZE-1));
|
||
if (size & (AES_BLOCK_SIZE-1))
|
||
sz += AES_BLOCK_SIZE;
|
||
|
||
qword dl = (qword)data.GetLength();
|
||
if (dl >= sz)
|
||
return 0;
|
||
|
||
qword sl = (qword) s.GetLength();
|
||
if (dl+sl < sz)
|
||
data << s;
|
||
else
|
||
{
|
||
sl = sz - dl;
|
||
data << s.Left((int) sl);
|
||
}
|
||
|
||
return (int) sl;
|
||
}
|
||
|
||
String AESDecoderStream::GetDecryptedData() throw (const char *)
|
||
{
|
||
#pragma pack(push,1)
|
||
struct AES_CONTAINER_HEADER
|
||
{
|
||
dword m1;
|
||
dword m2;
|
||
Upp::byte iv[AES_BLOCK_SIZE];
|
||
qword size;
|
||
} header;
|
||
#pragma pack(pop)
|
||
|
||
if (!decodedHeader)
|
||
{
|
||
if (data.GetLength() < sizeof(header))
|
||
return "";
|
||
|
||
for (int offs = 0; offs+AES_BLOCK_SIZE <= sizeof(header); offs += AES_BLOCK_SIZE)
|
||
AES_ecb_encrypt(((const unsigned char *) ~data)+offs, ((unsigned char *) &header)+offs, &key, false);
|
||
|
||
if (header.m1+header.m2 != AES_CONTAINER_DWORD_HEADER)
|
||
throw "Wrong key!";//"Ключ шифрования неверный";
|
||
|
||
memcpy(~iv, &(header.iv[0]), AES_BLOCK_SIZE);
|
||
size = header.size;
|
||
|
||
data.Remove(0, sizeof(header));
|
||
decodedHeader = true;
|
||
}
|
||
|
||
dword doSize = data.GetLength();
|
||
qword sizeLeft = size-sizeDone;
|
||
if (((qword) doSize) > sizeLeft)
|
||
doSize = (dword) sizeLeft;
|
||
if (!doSize)
|
||
return "";
|
||
|
||
dword extSize = doSize & (~(dword) (AES_BLOCK_SIZE-1));
|
||
if (extSize && ((dword) data.GetLength()) >= doSize+AES_BLOCK_SIZE)
|
||
doSize += AES_BLOCK_SIZE;
|
||
|
||
StringBuffer outData(doSize);
|
||
|
||
AES_cbc_encrypt((const unsigned char *) ~data, (unsigned char *) ~outData, (const unsigned long) doSize, &key, (unsigned char *) ~iv, false);
|
||
data.Remove(0, doSize);
|
||
sizeDone += doSize;
|
||
return outData;
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------------
|
||
const dword AES_CONTAINER_DWORD_HEADER = 0x377FEA9F;
|