mirror of
https://github.com/despawnerer/freearc
synced 2026-08-23 02:23:04 -04:00
450 lines
16 KiB
C++
450 lines
16 KiB
C++
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
|
||
#define LTC_NO_MODES
|
||
#define LTC_CFB_MODE
|
||
#define LTC_CTR_MODE
|
||
#define LTC_NO_HASHES
|
||
#define LTC_SHA512
|
||
#define LTC_NO_MACS
|
||
#define LTC_HMAC
|
||
#define LTC_NO_MATH
|
||
#define LTC_NO_PK
|
||
#define LTC_MECC
|
||
#define LTC_NO_TEST
|
||
#include "ciphers/aes/aes.c"
|
||
#include "ciphers/blowfish.c"
|
||
#include "ciphers/serpent.c"
|
||
#include "ciphers/twofish/twofish.c"
|
||
#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"
|
||
#include "prngs/fortuna.c"
|
||
}
|
||
#include "../MultiThreading.h"
|
||
|
||
/*-------------------------------------------------*/
|
||
/* Инициализация библиотеки шифрования LibTomCrypt */
|
||
/*-------------------------------------------------*/
|
||
|
||
// Зарегистировать все включённые в программу алгоритмы
|
||
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);
|
||
#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!"));
|
||
#endif
|
||
return 0;
|
||
}
|
||
int call_register_all = register_all();
|
||
|
||
// Размер буфера Fortuna PRNG
|
||
int fortuna_size(void) { return sizeof(prng_state); }
|
||
|
||
/*------------------------------------------------------*/
|
||
/* Обобщённый интерфейс к режимам шифрования (CFB,CTR) */
|
||
/*------------------------------------------------------*/
|
||
|
||
#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();
|
||
}
|
||
}
|
||
|
||
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;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 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();
|
||
}
|
||
}
|
||
|
||
int encrypt(BYTE *buf, int len) {
|
||
if (aesctr) {
|
||
g_AesCtr_Code((UInt32 *)aesctr, buf, len / AES_BLOCK_SIZE);
|
||
return 0;
|
||
}
|
||
switch (mode) {
|
||
case CTR:
|
||
return ctr_encrypt(buf, buf, len, &ctr);
|
||
case CFB:
|
||
return cfb_encrypt(buf, buf, len, &cfb);
|
||
default:
|
||
abort();
|
||
}
|
||
}
|
||
|
||
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();
|
||
}
|
||
}
|
||
|
||
int done() {
|
||
if (aesctr) {
|
||
return 0;
|
||
}
|
||
switch (mode) {
|
||
case CTR:
|
||
return ctr_done(&ctr);
|
||
case CFB:
|
||
return cfb_done(&cfb);
|
||
default:
|
||
abort();
|
||
}
|
||
}
|
||
};
|
||
|
||
// Найти номер режима шифрования по его имени
|
||
int find_mode(char *name) {
|
||
if (strequ(name, "ctr"))
|
||
return 0;
|
||
if (strequ(name, "cfb"))
|
||
return 1;
|
||
else
|
||
return -1;
|
||
}
|
||
|
||
/*-------------------------------------------------*/
|
||
/* Пользовательские функции */
|
||
/*-------------------------------------------------*/
|
||
|
||
// 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);
|
||
}
|
||
|
||
// Зашифровывает или расшифровывает поток данных, в зависимости от значения
|
||
// 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) // выход при ошибке чтения
|
||
{
|
||
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);
|
||
}
|
||
}
|
||
Exit:
|
||
encryptor.done();
|
||
free(Buf0);
|
||
return InSize; // возвратим код ошибки или 0 если всё в порядке
|
||
}
|
||
|
||
/*-------------------------------------------------*/
|
||
/* Реализация класса ENCRYPTION_METHOD */
|
||
/*-------------------------------------------------*/
|
||
|
||
// Конструктор, присваивающий параметрам метода сжатия значения по умолчанию
|
||
ENCRYPTION_METHOD::ENCRYPTION_METHOD() {
|
||
cipher = -1;
|
||
mode = -1;
|
||
numIterations = 1000;
|
||
rounds = 0;
|
||
keySize = -1;
|
||
fixed = FALSE;
|
||
strcpy(key, "");
|
||
strcpy(iv, "");
|
||
strcpy(salt, "");
|
||
strcpy(code, "");
|
||
}
|
||
|
||
// Универсальный метод, отвечает на запросы "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); // Передать остальные вызовы родительской процедуре
|
||
}
|
||
|
||
// Функция распаковки
|
||
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);
|
||
}
|
||
|
||
#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);
|
||
}
|
||
|
||
#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);
|
||
}
|
||
|
||
// Конструирует объект типа 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;
|
||
}
|
||
}
|
||
if (error) {
|
||
delete p;
|
||
return NULL;
|
||
} // Ошибка при парсинге параметров метода
|
||
return p;
|
||
}
|
||
|
||
static int ENCRYPTION_x = AddCompressionMethod(
|
||
parse_ENCRYPTION); // Зарегистрируем парсер метода ENCRYPTION
|