SlunkCrypt/frontend/src/main.c

530 lines
13 KiB
C
Raw Normal View History

/******************************************************************************/
/* MCrypt, by LoRd_MuldeR <MuldeR2@GMX.de> */
/* This work has been released under the CC0 1.0 Universal license! */
/******************************************************************************/
#define _CRT_SECURE_NO_WARNINGS 1
#include <mcrypt.h>
2020-10-13 15:04:59 +02:00
#include "utils.h"
#include "crc.h"
#include <string.h>
#include <time.h>
2020-10-13 15:37:40 +02:00
#include <inttypes.h>
#include <ctype.h>
#include <signal.h>
static volatile int g_interrupted = 0;
static char* read_passphrase(const CHR* const file_name)
{
const size_t buff_size = 1024U;
char *const buffer = (char*) malloc(buff_size * sizeof(char));
if (!buffer)
{
return NULL;
}
FILE *const fin = FOPEN(file_name, T("rb"));
if (!fin)
{
return NULL;
}
do
{
if (!fgets(buffer, (int)buff_size, fin))
{
fclose(fin);
free(buffer);
return NULL;
}
size_t length = strlen(buffer);
while ((length > 0U) && ((buffer[length - 1U] == '\r') || (buffer[length - 1U] == '\n')))
{
buffer[--length] = '\0';
}
}
while (!buffer[0U]);
fclose(fin);
return buffer;
}
static int weak_passphrase(const char *str)
{
int flags[4U] = { 0, 0, 0, 0 };
while (*str)
{
const CHR c = *str++;
if (isalpha(c))
{
flags[isupper(c) ? 0U : 1U] = 1;
}
else
{
flags[isdigit(c) ? 2U : 3U] = 1;
}
}
const int strong = flags[0U] && flags[1U] && flags[2U] && flags[3U];
return !strong;
}
2020-10-13 15:04:59 +02:00
static int open_files(FILE** const fin, FILE** const fout, const CHR* const input, const CHR* const output)
{
2020-10-13 15:04:59 +02:00
*fin = FOPEN(input, T("rb"));
2020-10-13 00:43:57 +02:00
if (!(*fin))
{
2020-10-13 15:04:59 +02:00
FPUTS(T("Error: Failed to open input file for reading!\n\n"), stderr);
return 1;
}
2020-10-13 15:04:59 +02:00
*fout = FOPEN(output, T("wb"));
2020-10-13 00:43:57 +02:00
if (!(*fout))
{
2020-10-13 15:04:59 +02:00
FPUTS(T("Error: Failed to open output file for writing!\n\n"), stderr);
2020-10-13 00:43:57 +02:00
fclose(*fin);
return 1;
}
2020-10-13 00:43:57 +02:00
return 0;
}
2020-10-13 19:33:01 +02:00
static int encrypt(const char* const passphrase, const CHR* const input, const CHR* const output)
2020-10-13 00:43:57 +02:00
{
2020-10-14 13:39:12 +02:00
mcrypt_t ctx = MCRYPT_NULL;
2020-10-13 00:43:57 +02:00
FILE *fin = NULL, *fout = NULL;
2020-10-14 13:14:47 +02:00
int result = 1;
2020-10-13 00:43:57 +02:00
if (open_files(&fin, &fout, input, output) != 0)
{
goto clean_up;;
}
2020-10-13 15:04:59 +02:00
const uint64_t file_size = get_file_size(fin);
if (file_size == UINT64_MAX)
{
FPUTS(T("I/O error: Failed to determine size of input file!\n\n"), stderr);
goto clean_up;
}
else if (file_size < 1LL)
{
FPUTS(T("Error: Input file is empty!\n\n"), stderr);
goto clean_up;
}
uint64_t seed;
if (mcrypt_generate_seed(&seed) != 0)
{
2020-10-13 15:04:59 +02:00
FPUTS(T("MCrypt error: Failed to generate seed!\n\n"), stderr);
2020-10-13 00:43:57 +02:00
goto clean_up;
}
if (fwrite(&seed, sizeof(uint64_t), 1U, fout) < 1U)
{
2020-10-13 15:04:59 +02:00
FPUTS(T("I/O error: Failed to write seed value!\n\n"), stderr);
2020-10-13 00:43:57 +02:00
goto clean_up;
}
2020-10-13 19:33:01 +02:00
ctx = mcrypt_alloc(seed, passphrase);
if (!ctx)
{
2020-10-13 15:04:59 +02:00
FPUTS(T("MCrypt error: Failed to initialize encryption!\n\n"), stderr);
2020-10-13 00:43:57 +02:00
goto clean_up;
}
2020-10-13 19:50:29 +02:00
FPRINTF(stderr, T("Encrypting file contents, please be patient... %5.1f%%"), 0.0);
2020-10-13 15:04:59 +02:00
clock_t clk_now, clk_update = clock();
uint64_t crc_actual = CRC_INITIALIZER, bytes_read = sizeof(uint64_t);
uint8_t buffer[1024U];
2020-10-13 15:04:59 +02:00
while ((!feof(fin)) && (bytes_read < file_size))
{
const size_t count = fread(buffer, sizeof(uint8_t), 1024U, fin);
if (ferror(fin))
{
2020-10-13 15:04:59 +02:00
FPUTS(T("\n\nI/O error: Failed to read input data!\n\n"), stderr);
2020-10-13 00:43:57 +02:00
goto clean_up;
}
if (count > 0U)
{
2020-10-13 15:04:59 +02:00
crc_actual = crc64_update(crc_actual, buffer, count);
bytes_read += count;
2020-10-14 17:57:40 +02:00
if (mcrypt_encrypt_inplace(ctx, buffer, count) != 0)
{
2020-10-13 15:04:59 +02:00
FPUTS(T("\n\nMCrypt error: Failed to encrypt data!\n\n"), stderr);
2020-10-13 00:43:57 +02:00
goto clean_up;
}
if (fwrite(buffer, sizeof(uint8_t), count, fout) < count)
{
2020-10-13 15:04:59 +02:00
FPUTS(T("\n\nI/O error: Failed to write encrypted data!\n\n"), stderr);
2020-10-13 00:43:57 +02:00
goto clean_up;
}
}
2020-10-13 15:04:59 +02:00
if ((clk_now = clock()) - clk_update > CLOCKS_PER_SEC)
{
FPRINTF(stderr, T("\b\b\b\b\b\b%5.1f%%"), (bytes_read / ((double)file_size)) * 100.0);
fflush(stderr);
clk_update = clk_now;
}
if (g_interrupted)
{
FPUTS(T("\n\nProcess interrupted!\n\n"), stderr);
goto clean_up;
}
}
2020-10-13 15:04:59 +02:00
crc_actual = crc64_finish(crc_actual);
if (bytes_read < file_size)
{
FPUTS(T("\n\nI/O error: Input file could not be fully read!\n\n"), stderr);
goto clean_up;
}
2020-10-13 15:04:59 +02:00
FPRINTF(stderr, T("\b\b\b\b\b\b%5.1f%%\n\n"), 100.0);
if (fwrite(&crc_actual, sizeof(uint64_t), 1U, fout) < 1U)
{
2020-10-13 15:04:59 +02:00
FPUTS(T("I/O error: Failed to write CRC checksum!\n\n"), stderr);
2020-10-13 00:43:57 +02:00
goto clean_up;
}
result = 0;
2020-10-13 15:04:59 +02:00
FPUTS(T("All is done.\n\n"), stderr);
2020-10-13 00:43:57 +02:00
fflush(stderr);
clean_up:
if (ctx)
{
mcrypt_free(ctx);
2020-10-13 00:43:57 +02:00
}
if (fout)
{
fclose(fout);
2020-10-13 00:43:57 +02:00
}
if (fin)
{
fclose(fin);
}
2020-10-13 00:43:57 +02:00
return result;
}
2020-10-13 19:33:01 +02:00
static int decrypt(const char* const passphrase, const CHR* const input, const CHR* const output)
{
2020-10-14 13:39:12 +02:00
mcrypt_t ctx = MCRYPT_NULL;
2020-10-13 00:43:57 +02:00
FILE *fin = NULL, *fout = NULL;
2020-10-14 13:14:47 +02:00
int result = 1;
2020-10-13 00:43:57 +02:00
if (open_files(&fin, &fout, input, output) != 0)
{
2020-10-13 00:43:57 +02:00
goto clean_up;
}
2020-10-13 00:43:57 +02:00
const uint64_t file_size = get_file_size(fin);
if (file_size == UINT64_MAX)
{
2020-10-13 15:04:59 +02:00
FPUTS(T("I/O error: Failed to determine size of input file!\n\n"), stderr);
2020-10-13 00:43:57 +02:00
goto clean_up;
}
2020-10-13 15:04:59 +02:00
else if (file_size < 16LL)
{
2020-10-13 15:04:59 +02:00
FPUTS(T("Error: Input file is too small! Truncated?\n\n"), stderr);
2020-10-13 00:43:57 +02:00
goto clean_up;
}
uint64_t seed;
if (fread(&seed, sizeof(uint64_t), 1U, fin) < 1U)
{
2020-10-13 15:04:59 +02:00
FPUTS(T("I/O error: Failed to read seed value!\n\n"), stderr);
2020-10-13 00:43:57 +02:00
goto clean_up;
}
2020-10-13 19:33:01 +02:00
ctx = mcrypt_alloc(seed, passphrase);
if (!ctx)
{
2020-10-13 15:04:59 +02:00
FPUTS(T("MCrypt error: Failed to initialize decryption!\n\n"), stderr);
2020-10-13 00:43:57 +02:00
goto clean_up;
}
2020-10-13 19:50:29 +02:00
FPRINTF(stderr, T("Decrypting file contents, please be patient... %5.1f%%"), 0.0);
2020-10-13 15:04:59 +02:00
clock_t clk_now, clk_update = clock();
uint64_t crc_actual = CRC_INITIALIZER, bytes_read = sizeof(uint64_t);
uint8_t buffer[1024U];
2020-10-13 15:04:59 +02:00
const uint64_t read_limit = file_size - sizeof(uint64_t);
2020-10-13 00:43:57 +02:00
while ((!feof(fin)) && (bytes_read < read_limit))
{
2020-10-13 15:04:59 +02:00
const uint64_t bytes_remaining = read_limit - bytes_read;
const size_t read_len = (bytes_remaining < 1024U) ? ((size_t)bytes_remaining) : 1024U;
const size_t count = fread(buffer, sizeof(uint8_t), read_len, fin);
if (ferror(fin))
{
2020-10-13 15:04:59 +02:00
FPUTS(T("\n\nI/O error: Failed to read encrypted data!\n\n"), stderr);
2020-10-13 00:43:57 +02:00
goto clean_up;
}
if (count > 0U)
{
bytes_read += count;
2020-10-14 17:57:40 +02:00
if (mcrypt_decrypt_inplace(ctx, buffer, count) != 0)
{
2020-10-13 15:04:59 +02:00
FPUTS(T("\n\nMCrypt error: Failed to decrypt data!\n\n"), stderr);
2020-10-13 00:43:57 +02:00
goto clean_up;
}
2020-10-13 15:04:59 +02:00
crc_actual = crc64_update(crc_actual, buffer, count);
if (fwrite(buffer, sizeof(uint8_t), count, fout) < count)
{
2020-10-13 15:04:59 +02:00
FPUTS(T("failed!\n\nI/O error: Failed to write decrypted data!\n\n"), stderr);
2020-10-13 00:43:57 +02:00
goto clean_up;
}
}
2020-10-13 15:04:59 +02:00
if ((clk_now = clock()) - clk_update > CLOCKS_PER_SEC)
{
FPRINTF(stderr, T("\b\b\b\b\b\b%5.1f%%"), (bytes_read / ((double)read_limit)) * 100.0);
fflush(stderr);
clk_update = clk_now;
}
if (g_interrupted)
{
FPUTS(T("\n\nProcess interrupted!\n\n"), stderr);
goto clean_up;
}
}
2020-10-13 15:04:59 +02:00
crc_actual = crc64_finish(crc_actual);
2020-10-13 00:43:57 +02:00
if (bytes_read < read_limit)
{
2020-10-13 15:04:59 +02:00
FPUTS(T("\n\nI/O error: Input file could not be fully read!\n\n"), stderr);
2020-10-13 00:43:57 +02:00
goto clean_up;
}
2020-10-13 15:04:59 +02:00
FPRINTF(stderr, T("\b\b\b\b\b\b%5.1f%%\n\n"), 100.0);
uint64_t crc_expected;
if (fread(&crc_expected, sizeof(uint64_t), 1U, fin) < 1U)
{
2020-10-13 15:04:59 +02:00
FPUTS(T("I/O error: Failed to read CRC checksum!\n\n"), stderr);
2020-10-13 00:43:57 +02:00
goto clean_up;
}
if (crc_actual != crc_expected)
{
2020-10-13 15:37:40 +02:00
FPRINTF(stderr, T("CRC error: Checksum mismatch detected! [expected: 0x%016") T(PRIX64) T(", actual: 0x%016") T(PRIX64) T("]\n\n"), crc_actual, crc_expected);
FPUTS(T("Wrong passphrase or corrupted file?\n\n"), stderr);
2020-10-13 00:43:57 +02:00
goto clean_up;
}
result = 0;
2020-10-13 15:37:40 +02:00
FPUTS(T("CRC checksum is correct.\n\n"), stderr);
2020-10-13 00:43:57 +02:00
fflush(stderr);
clean_up:
if (ctx)
{
mcrypt_free(ctx);
2020-10-13 00:43:57 +02:00
}
if (fout)
{
fclose(fout);
2020-10-13 00:43:57 +02:00
}
if (fin)
{
fclose(fin);
}
2020-10-13 00:43:57 +02:00
return result;
}
2020-10-14 13:14:47 +02:00
static int self_test(void)
{
static const char* const passphrase = "OrpheanBeh0lderScryDoubt!";
2020-10-14 14:02:05 +02:00
static const char* const text_plain = "Would you like me to give you a formula for success? It's quite simple, really: Double your rate of failure. You are thinking of failure as the enemy of success. But it isn't at all. You can be discouraged by failure or you can learn from it, so go ahead and make mistakes. Make all you can. Because remember that's where you will find success.";
2020-10-14 13:14:47 +02:00
const size_t length = strlen(text_plain) + 1U;
int result = 1;
2020-10-14 17:57:40 +02:00
mcrypt_t ctx = MCRYPT_NULL;
2020-10-14 13:14:47 +02:00
FPUTS(T("Self-test is running, please be patient... "), stderr);
uint64_t seed;
if (mcrypt_generate_seed(&seed) != 0)
{
FPUTS(T("error!\n\nWhoops: Failed to generate seed!\n\n"), stderr);
goto clean_up;
}
char *const text_temp = strdup(text_plain);
if (!text_temp)
{
FPUTS(T("error!\n\nWhoops: Failed to allocate text buffer!\n\n"), stderr);
goto clean_up;
}
2020-10-14 17:57:40 +02:00
ctx = mcrypt_alloc(seed, passphrase);
if (!ctx)
2020-10-14 13:14:47 +02:00
{
FPUTS(T("error!\n\nnWhoops: Failed to initialize encoder!\n\n"), stderr);
goto clean_up;
}
2020-10-14 17:57:40 +02:00
if (mcrypt_encrypt_inplace(ctx, (uint8_t*)text_temp, length) != 0)
2020-10-14 13:14:47 +02:00
{
FPUTS(T("error!\n\nWhoops: Failed to encrypt the message!\n\n"), stderr);
goto clean_up;
}
if (strncmp(text_plain, text_temp, length) == 0)
{
FPUTS(T("error!\n\nWhoops: Encrypted message equals the original message!\n\n"), stderr);
goto clean_up;
}
2020-10-14 17:57:40 +02:00
if (mcrypt_reset(ctx, seed, passphrase) != 0)
2020-10-14 13:14:47 +02:00
{
FPUTS(T("error!\n\nWhoops: Failed to initialize decoder!\n\n"), stderr);
goto clean_up;
}
2020-10-14 17:57:40 +02:00
if (mcrypt_decrypt_inplace(ctx, (uint8_t*)text_temp, length) != 0)
2020-10-14 13:14:47 +02:00
{
FPUTS(T("error!\n\nWhoops: Failed to decrypt the message!\n\n"), stderr);
goto clean_up;
}
if (strncmp(text_plain, text_temp, length) != 0)
{
FPUTS(T("error!\n\nWhoops: Decrypted message does *not* match the original message!\n\n"), stderr);
goto clean_up;
}
result = 0;
FPUTS(T("done\n\nCompleted successfully.\n\n"), stderr);
clean_up:
2020-10-14 17:57:40 +02:00
if (ctx)
2020-10-14 13:14:47 +02:00
{
2020-10-14 17:57:40 +02:00
mcrypt_free(ctx);
2020-10-14 13:14:47 +02:00
}
if (text_temp)
{
mcrypt_bzero(text_temp, strlen(text_temp));
free(text_temp);
}
return result;
}
static void sigint_handler(const int sig)
{
g_interrupted = 1;
signal(SIGINT, sigint_handler);
}
2020-10-13 15:04:59 +02:00
int MAIN(int argc, CHR* argv[])
{
2020-10-13 19:33:01 +02:00
init_terminal();
signal(SIGINT, sigint_handler);
2020-10-13 19:50:29 +02:00
FPRINTF(stderr, T("MCrypt Utility (%") T(PRIstr) T("-%") T(PRIstr) T("), by LoRd_MuldeR <MuldeR2@GMX.de>\n"), OS_TYPE, CPU_ARCH);
2020-10-13 19:33:01 +02:00
FPRINTF(stderr, T("Using libMCrypt v%") T(PRIstr) T(" [%") T(PRIstr) T("]\n\n"), LIBMCRYPT_VERSION, LIBMCRYPT_BUILDNO);
2020-10-14 13:14:47 +02:00
if ((argc > 1) && (!STRICMP(argv[1U], T("--self-test"))))
{
return self_test(); /*only self-test!*/
}
2020-10-13 19:33:01 +02:00
const int help_requested = (argc > 1) && ((!STRICMP(argv[1U], T("/?"))) || (!STRICMP(argv[1U], T("--help"))) || (!STRICMP(argv[1U], T("--version"))));
if ((argc < 5) || help_requested)
{
const CHR* const program = get_file_name(argv[0U]);
2020-10-13 19:33:01 +02:00
FPUTS(T("--------------------------------------------------------------------\n"), stderr);
FPUTS(T("This software has been released under the CC0 1.0 Universal license:\n"), stderr);
2020-10-13 19:33:01 +02:00
FPUTS(T("https://creativecommons.org/publicdomain/zero/1.0/legalcode\n"), stderr);
FPUTS(T("--------------------------------------------------------------------\n\n"), stderr);
if (!help_requested)
{
2020-10-13 19:33:01 +02:00
FPUTS(T("Error: Required argument is missing!\n\n"), stderr);
}
2020-10-13 15:04:59 +02:00
FPUTS(T("Usage:\n"), stderr);
FPRINTF(stderr, T(" %") T(PRISTR) T(" --encrypt [@]<passphrase> <input.dat> <output.enc>\n"), program);
FPRINTF(stderr, T(" %") T(PRISTR) T(" --decrypt [@]<passphrase> <input.enc> <output.dat>\n\n"), program);
FPUTS(T("Note: If <passphrase> starts with an '@' symbol, the remainder specifies the\n"), stderr);
FPUTS(T("file to read the passphrase from. Only the *first* line in the file is used!\n"), stderr);
2020-10-13 19:50:29 +02:00
return help_requested ? 0 : 1;
}
2020-10-13 19:33:01 +02:00
const CHR *const command = argv[1U], *const passphrase = argv[2U], *const input_file = argv[3U], *const output_file = argv[4U];
if ((!passphrase[0U]) || ((passphrase[0U] == T('@')) && (!passphrase[1U])))
{
FPUTS(T("Error: The passphrase or passphrase file must not be empty!\n\n"), stderr);
return 1;
}
2020-10-13 19:33:01 +02:00
if ((!input_file[0U]) || (!output_file[0U]))
{
FPUTS(T("Error: The input file and/or output file must not be empty!\n\n"), stderr);
return 1;
}
char *const passphrase_buffer = (passphrase[0U] == T('@')) ? read_passphrase(passphrase + 1U) : CHR_to_utf8(passphrase);
if (!passphrase_buffer)
2020-10-13 19:33:01 +02:00
{
FPUTS(T("Error: Failed to read the passphrase file!\n\n"), stderr);
2020-10-13 19:33:01 +02:00
return 1;
}
if (strlen(passphrase_buffer) < 12U)
{
FPUTS(T("Warning: Using a *short* passphrase; a length of 12 characters or more is recommended!\n\n"), stderr);
}
else if (weak_passphrase(passphrase_buffer))
{
FPUTS(T("Warning: Using a *weak* passphrase; a mix of upper-case letters, lower-case letters, digits and 'special' characters is recommended!\n\n"), stderr);
}
const clock_t clk_start = clock();
int result = -1;
2020-10-13 19:33:01 +02:00
if (!STRICMP(command, T("--encrypt")))
{
result = encrypt(passphrase_buffer, input_file, output_file);
}
2020-10-13 19:33:01 +02:00
else if (!STRICMP(command, T("--decrypt")))
{
result = decrypt(passphrase_buffer, input_file, output_file);
}
else
{
2020-10-13 19:33:01 +02:00
FPRINTF(stderr, T("Error: Command \"%") T(PRISTR) T("\" is unknown!\n\n"), command);
goto exiting;
}
2020-10-13 15:04:59 +02:00
FPUTS(T("--------\n\n"), stderr);
2020-10-13 00:43:57 +02:00
fflush(stderr);
const clock_t clk_end = clock();
2020-10-13 15:04:59 +02:00
FPRINTF(stderr, T("Operation completed after %.1f seconds.\n\n"), (clk_end - clk_start) / ((double)CLOCKS_PER_SEC));
2020-10-13 19:33:01 +02:00
exiting:
if (passphrase_buffer)
2020-10-13 19:50:29 +02:00
{
mcrypt_bzero(passphrase_buffer, strlen(passphrase_buffer));
free(passphrase_buffer);
2020-10-13 19:50:29 +02:00
}
2020-10-13 19:33:01 +02:00
mcrypt_bzero((CHR*)passphrase, STRLEN(passphrase) * sizeof(CHR));
}