freearc/clibs/Compression/_Encryption/C_Encryption.cpp

451 lines
16 KiB
C++
Raw Permalink Normal View History

2023-01-10 10:50:12 +01:00
extern "C" {
#include "7zAES/Aes.c"
#include "7zAES/CpuArch.c"
#include "C_Encryption.h"
#define LTC_NO_CIPHERS
#define LTC_BLOWFISH
#define LTC_RIJNDAEL
#define ENCRYPT_ONLY
#define LTC_TWOFISH
#define LTC_SERPENT
2023-01-10 10:50:12 +01:00
#define LTC_NO_MODES
#define LTC_CFB_MODE
#define LTC_CTR_MODE
2023-01-10 10:50:12 +01:00
#define LTC_NO_HASHES
#define LTC_SHA512
2023-01-10 10:50:12 +01:00
#define LTC_NO_MACS
#define LTC_HMAC
2023-01-10 10:50:12 +01:00
#define LTC_NO_MATH
#define LTC_NO_PK
#define LTC_MECC
2023-01-10 10:50:12 +01:00
#define LTC_NO_TEST
#include "ciphers/aes/aes.c"
#include "ciphers/blowfish.c"
#include "ciphers/serpent.c"
#include "ciphers/twofish/twofish.c"
2023-01-10 10:50:12 +01:00
#include "crypt/crypt_argchk.c"
#include "crypt/crypt_cipher_descriptor.c"
#include "crypt/crypt_cipher_is_valid.c"
#include "crypt/crypt_find_cipher.c"
#include "crypt/crypt_find_hash.c"
#include "crypt/crypt_find_prng.c"
#include "crypt/crypt_hash_descriptor.c"
#include "crypt/crypt_hash_is_valid.c"
#include "crypt/crypt_prng_descriptor.c"
#include "crypt/crypt_prng_is_valid.c"
#include "crypt/crypt_register_cipher.c"
#include "crypt/crypt_register_hash.c"
#include "crypt/crypt_register_prng.c"
#include "hashes/helper/hash_memory.c"
#include "hashes/sha2/sha512.c"
#include "mac/hmac/hmac_done.c"
#include "mac/hmac/hmac_init.c"
#include "mac/hmac/hmac_memory.c"
#include "mac/hmac/hmac_process.c"
#include "misc/error_to_string.c"
#include "misc/pkcs5/pkcs_5_2.c"
#include "misc/zeromem.c"
#include "modes/cfb/cfb_decrypt.c"
#include "modes/cfb/cfb_done.c"
#include "modes/cfb/cfb_encrypt.c"
#include "modes/cfb/cfb_start.c"
#include "modes/ctr/ctr_decrypt.c"
#include "modes/ctr/ctr_done.c"
#include "modes/ctr/ctr_encrypt.c"
#include "modes/ctr/ctr_start.c"
2023-01-10 10:50:12 +01:00
#include "prngs/fortuna.c"
}
#include "../MultiThreading.h"
/*-------------------------------------------------*/
/* Инициализация библиотеки шифрования LibTomCrypt */
2023-01-10 10:50:12 +01:00
/*-------------------------------------------------*/
// Зарегистировать все включённые в программу алгоритмы
int register_all() {
register_cipher(&aes_enc_desc);
register_cipher(&blowfish_desc);
register_cipher(&serpent_desc);
register_cipher(&twofish_desc);
// register_hash (&sha1_desc);
register_hash(&sha512_desc);
2023-01-10 10:50:12 +01:00
#ifndef LTC_NO_TEST
CHECK(FREEARC_ERRCODE_INTERNAL, blowfish_test() == CRYPT_OK,
(s, "blowfish_test failed!"));
// CHECK (FREEARC_ERRCODE_INTERNAL, rijndael_test()==CRYPT_OK,
// (s,"rijndael_test failed!"));
CHECK(FREEARC_ERRCODE_INTERNAL, serpent_test() == CRYPT_OK,
(s, "serpent_test failed!"));
CHECK(FREEARC_ERRCODE_INTERNAL, twofish_test() == CRYPT_OK,
(s, "twofish_test failed!"));
// CHECK (FREEARC_ERRCODE_INTERNAL, sha1_test ()==CRYPT_OK, (s,"sha1_test
// failed!"));
CHECK(FREEARC_ERRCODE_INTERNAL, sha512_test() == CRYPT_OK,
(s, "sha512_test failed!"));
// CHECK (FREEARC_ERRCODE_INTERNAL, hmac_test ()==CRYPT_OK, (s,"hmac_test
// failed!")); CHECK (FREEARC_ERRCODE_INTERNAL, ctr_test ()==CRYPT_OK,
// (s,"ctr_test failed!")); CHECK (FREEARC_ERRCODE_INTERNAL, cfb_test
// ()==CRYPT_OK, (s,"cfb_test failed!"));
2023-01-10 10:50:12 +01:00
#endif
return 0;
2023-01-10 10:50:12 +01:00
}
int call_register_all = register_all();
// Размер буфера Fortuna PRNG
int fortuna_size(void) { return sizeof(prng_state); }
2023-01-10 10:50:12 +01:00
/*------------------------------------------------------*/
/* Обобщённый интерфейс к режимам шифрования (CFB,CTR) */
2023-01-10 10:50:12 +01:00
/*------------------------------------------------------*/
#define roundup_ptr(p, n) ((p) + (n)-ptrdiff_t(p) % (n))
struct EncryptionMode {
enum { CTR, CFB } mode;
union {
symmetric_CTR ctr;
symmetric_CFB cfb;
char aesbuf[AES_NUM_IVMRK_WORDS * sizeof(UInt32) +
AES_BLOCK_SIZE]; // buffer for g_AesCtr_Code
};
char *aesctr;
EncryptionMode(int _mode) { mode = (_mode == 0 ? CTR : CFB); }
char *name() {
switch (mode) {
case CTR:
return "ctr";
case CFB:
return "cfb";
default:
abort();
2023-01-10 10:50:12 +01:00
}
}
int start(int cipher, BYTE *iv, BYTE *key, int keysize, int rounds) {
// 7-zip code implements only AES-CTR with default number of rounds
if (strequ(cipher_descriptor[cipher].name, "aes") && mode == CTR &&
rounds == 0) {
// Init the AES-CTR library
static Mutex exclusive_access;
{
Lock _(exclusive_access);
static volatile bool initialized = false;
if (!initialized) {
initialized = true;
AesGenTables();
if (CPU_Is_Aes_Supported()) {
FARPROC Fast_AesCtr_Code = LoadFromDLL("Fast_AesCtr_Code");
if (Fast_AesCtr_Code)
g_AesCtr_Code = (AES_CODE_FUNC)Fast_AesCtr_Code;
}
2023-01-10 10:50:12 +01:00
}
}
2023-01-10 10:50:12 +01:00
// aesctr[] should be aligned to 16-byte boundary
aesctr = roundup_ptr(aesbuf, AES_BLOCK_SIZE);
// Store in aesctr[] InitVector-1 (since 7-zip code does *pre*increment of
// counter)
memcpy(aesctr, iv, AES_BLOCK_SIZE);
for (int i = 0; i < AES_BLOCK_SIZE; i++)
if (aesctr[i]--)
break;
// Calculate and store in aesctr[] number of rounds and roundKeys
Aes_SetKey_Enc((UInt32 *)(aesctr + AES_BLOCK_SIZE), key, keysize);
return 0;
}
aesctr = NULL;
switch (mode) {
case CTR:
return ctr_start(cipher, iv, key, keysize, rounds,
CTR_COUNTER_LITTLE_ENDIAN, &ctr);
case CFB:
return cfb_start(cipher, iv, key, keysize, rounds, &cfb);
default:
abort();
2023-01-10 10:50:12 +01:00
}
}
2023-01-10 10:50:12 +01:00
int encrypt(BYTE *buf, int len) {
if (aesctr) {
g_AesCtr_Code((UInt32 *)aesctr, buf, len / AES_BLOCK_SIZE);
return 0;
2023-01-10 10:50:12 +01:00
}
switch (mode) {
case CTR:
return ctr_encrypt(buf, buf, len, &ctr);
case CFB:
return cfb_encrypt(buf, buf, len, &cfb);
default:
abort();
}
}
2023-01-10 10:50:12 +01:00
int decrypt(BYTE *buf, int len) {
if (aesctr) {
g_AesCtr_Code((UInt32 *)aesctr, buf, len / AES_BLOCK_SIZE);
return 0;
}
switch (mode) {
case CTR:
return ctr_decrypt(buf, buf, len, &ctr);
case CFB:
return cfb_decrypt(buf, buf, len, &cfb);
default:
abort();
2023-01-10 10:50:12 +01:00
}
}
2023-01-10 10:50:12 +01:00
int done() {
if (aesctr) {
return 0;
2023-01-10 10:50:12 +01:00
}
switch (mode) {
case CTR:
return ctr_done(&ctr);
case CFB:
return cfb_done(&cfb);
default:
abort();
}
}
2023-01-10 10:50:12 +01:00
};
// Найти номер режима шифрования по его имени
int find_mode(char *name) {
if (strequ(name, "ctr"))
return 0;
if (strequ(name, "cfb"))
return 1;
else
return -1;
2023-01-10 10:50:12 +01:00
}
/*-------------------------------------------------*/
/* Пользовательские функции */
2023-01-10 10:50:12 +01:00
/*-------------------------------------------------*/
// Generate key from password and salt using numIterations of sha-512 hashing
// (PKCS5#2)
void Pbkdf2Hmac(const BYTE *pwd, int pwdSize, const BYTE *salt, int saltSize,
int numIterations, BYTE *key, int keySize) {
int hash = find_hash("sha512");
unsigned long ulKeySize = keySize;
pkcs_5_alg2(pwd, pwdSize, salt, saltSize, numIterations, hash, key,
&ulKeySize);
2023-01-10 10:50:12 +01:00
}
// Зашифровывает или расшифровывает поток данных, в зависимости от значения
// DoEncryption
int docrypt(enum TEncrypt DoEncryption, int cipher, int mode, BYTE *key,
int keysize, int rounds, BYTE *iv, CALLBACK_FUNC *callback,
void *auxdata) {
EncryptionMode encryptor(mode);
encryptor.start(cipher, iv, key, keysize, rounds);
int InSize = FREEARC_ERRCODE_NOT_ENOUGH_MEMORY; // количество прочитанных байт
// или код ошибки
int RemainderSize = 0; // необработанный остаток предыдущего блока (всегда 0 в
// нынешней реализации)
BYTE *Buf0 =
(BYTE *)malloc(LARGE_BUFFER_SIZE +
AES_BLOCK_SIZE); // место для данных, с учётом требований к
// выравниванию адреса буфера
if (!Buf0)
goto Exit; // выход при нехватке памяти
{
BYTE *Buf = roundup_ptr(Buf0, AES_BLOCK_SIZE);
while ((InSize = callback("read", Buf + RemainderSize,
LARGE_BUFFER_SIZE - RemainderSize, auxdata)) >=
0) // выход при ошибке чтения
2023-01-10 10:50:12 +01:00
{
bool LastBlock = (InSize == 0); // True for the last block that should be
// processed in the special way
if ((InSize += RemainderSize) == 0)
break; // break if there's no more data
int OutSize =
encryptor.aesctr == NULL ? InSize // process all input data unless
// optimized AES-CTR code used
: LastBlock
? roundUp(InSize,
AES_BLOCK_SIZE) // process more data if these are
// the last bytes in input stream
: roundDown(InSize, AES_BLOCK_SIZE),
x; // process less data and move the rest to the next processing cycle
DoEncryption == ENCRYPT ? encryptor.encrypt(Buf, OutSize)
: encryptor.decrypt(Buf, OutSize);
if (OutSize > InSize)
OutSize = InSize;
if ((x = callback("write", Buf, OutSize, auxdata)) < 0) {
InSize = x;
break;
} // break on write rror
if (LastBlock) {
InSize = 0;
break;
} // break if the last block
RemainderSize = InSize - OutSize;
// Перенесём необработанный остаток данных в начало буфера
if (RemainderSize > 0)
memmove(Buf, Buf + OutSize, RemainderSize);
}
}
2023-01-10 10:50:12 +01:00
Exit:
encryptor.done();
free(Buf0);
return InSize; // возвратим код ошибки или 0 если всё в порядке
2023-01-10 10:50:12 +01:00
}
/*-------------------------------------------------*/
/* Реализация класса ENCRYPTION_METHOD */
2023-01-10 10:50:12 +01:00
/*-------------------------------------------------*/
// Конструктор, присваивающий параметрам метода сжатия значения по умолчанию
ENCRYPTION_METHOD::ENCRYPTION_METHOD() {
cipher = -1;
mode = -1;
numIterations = 1000;
rounds = 0;
keySize = -1;
fixed = FALSE;
strcpy(key, "");
strcpy(iv, "");
strcpy(salt, "");
strcpy(code, "");
2023-01-10 10:50:12 +01:00
}
// Универсальный метод, отвечает на запросы "encryption?", "KeySize" и "IVSize"
int ENCRYPTION_METHOD::doit(char *what, int param, void *data,
CALLBACK_FUNC *callback) {
if (strequ(what, "encryption?"))
return 1; // Да, это алгоритм шифрования
else if (strequ(what, "keySize"))
return keySize; // Возвращает размер ключа, используемого в данном методе
// сжатия
else if (strequ(what, "ivSize"))
return ivSize; // Возвращает размер InitVector, используемого в данном
// методе сжатия
else if (strequ(what, "numIterations"))
return numIterations; // Возвращает количество итераций, используемых при
// генерации ключа по password+salt
else
return COMPRESSION_METHOD::doit(
what, param, data,
callback); // Передать остальные вызовы родительской процедуре
2023-01-10 10:50:12 +01:00
}
// Функция распаковки
int ENCRYPTION_METHOD::decompress(CALLBACK_FUNC *callback, void *auxdata) {
BYTE key_bytes[MAXKEYSIZE];
fixed ? decode16(key, key_bytes) : buggy_decode16(key, key_bytes);
BYTE iv_bytes[MAXKEYSIZE];
fixed ? decode16(iv, iv_bytes) : buggy_decode16(iv, iv_bytes);
return docrypt(DECRYPT, cipher, mode, key_bytes, strlen(key) / 2, rounds,
iv_bytes, callback, auxdata);
2023-01-10 10:50:12 +01:00
}
#ifndef FREEARC_DECOMPRESS_ONLY
// Функция упаковки
int ENCRYPTION_METHOD::compress(CALLBACK_FUNC *callback, void *auxdata) {
BYTE key_bytes[MAXKEYSIZE];
fixed ? decode16(key, key_bytes) : buggy_decode16(key, key_bytes);
BYTE iv_bytes[MAXKEYSIZE];
fixed ? decode16(iv, iv_bytes) : buggy_decode16(iv, iv_bytes);
return docrypt(ENCRYPT, cipher, mode, key_bytes, strlen(key) / 2, rounds,
iv_bytes, callback, auxdata);
2023-01-10 10:50:12 +01:00
}
#endif // !defined (FREEARC_DECOMPRESS_ONLY)
// Записать в buf[MAX_METHOD_STRLEN] строку, описывающую метод сжатия и его
// параметры (функция, обратная к parse_ENCRYPTION)
void ENCRYPTION_METHOD::ShowCompressionMethod(char *buf, bool purify) {
sprintf(buf, "%s-%d/%s%s:n%d:r%d%s%s%s%s%s%s%s%s",
cipher_descriptor[cipher].name, keySize * 8,
EncryptionMode(mode).name(), fixed ? ":f" : "", numIterations, rounds,
*key ? ":k" : "", key, *iv ? ":i" : "", iv, *salt ? ":s" : "", salt,
*code ? ":c" : "", code);
2023-01-10 10:50:12 +01:00
}
// Конструирует объект типа ENCRYPTION_METHOD с заданными параметрами упаковки
// или возвращает NULL, если это другой метод сжатия или допущена ошибка в
// параметрах
COMPRESSION_METHOD *parse_ENCRYPTION(char **parameters) {
int error = 0; // Признак того, что при разборе параметров произошла ошибка
// Делаем локальную копию, поскольку split портит строку
char local_method[MAX_METHOD_STRLEN];
strncopy(local_method, parameters[0], MAX_METHOD_STRLEN);
// Разбиваем строку метода на максимум 2 части, разделённые знаком '/'
// Это метод и режим шифрования (например, "aes/cfb")
char *parts[3];
split(local_method, '/', parts, 3);
int mode = parts[1] ? find_mode(parts[1]) : 0;
// Разбиваем строку метода на максимум 2 части, разделённые знаком '-'
// После '-' может быть указан размер ключа в битах (например, "aes-128")
split(local_method, '-', parts, 3);
int cipher = find_cipher(parts[0]);
int keySize = parts[1] ? parseInt(parts[1], &error) / 8 : 0;
if (mode < 0 || cipher < 0 || error)
return NULL; // Это не метод ENCRYPTION
ENCRYPTION_METHOD *p = new ENCRYPTION_METHOD;
p->cipher = cipher;
p->mode = mode;
p->keySize = keySize ? keySize : cipher_descriptor[cipher].max_key_length;
p->ivSize = cipher_descriptor[cipher].block_length;
// Переберём все параметры метода (или выйдем раньше при возникновении ошибки
// при разборе очередного параметра)
while (*++parameters && !error) {
char *param = *parameters;
if (strequ(param, "f")) {
p->fixed = TRUE;
continue;
}
switch (*param) { // Параметры, содержащие значения
case 'k':
strncopy(p->key, param + 1, sizeof(p->key));
continue;
case 'i':
strncopy(p->iv, param + 1, sizeof(p->iv));
continue;
case 's':
strncopy(p->salt, param + 1, sizeof(p->salt));
continue;
case 'c':
strncopy(p->code, param + 1, sizeof(p->code));
continue;
case 'n':
p->numIterations = parseInt(param + 1, &error);
continue;
case 'r':
p->rounds = parseInt(param + 1, &error);
continue;
default:
error = 1;
continue;
2023-01-10 10:50:12 +01:00
}
}
if (error) {
delete p;
return NULL;
} // Ошибка при парсинге параметров метода
return p;
2023-01-10 10:50:12 +01:00
}
static int ENCRYPTION_x = AddCompressionMethod(
parse_ENCRYPTION); // Зарегистрируем парсер метода ENCRYPTION