1 /******************************************************************************/
2 /* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de> */
3 /* This work has been released under the CC0 1.0 Universal license! */
4 /******************************************************************************/
6 #define _CRT_SECURE_NO_WARNINGS 1
8 #include <slunkcrypt.h>
18 #define BUFFER_SIZE 4096U
20 #define SLUNK_MODE_HELP 0
21 #define SLUNK_MODE_VERS 1
22 #define SLUNK_MODE_ENCR 2
23 #define SLUNK_MODE_DECR 3
24 #define SLUNK_MODE_TEST 4
26 static const CHR *const ENVV_PASSWD_NAME = T("SLUNK_PASSPHRASE");
28 // ==========================================================================
29 // Auxiliary functions
30 // ==========================================================================
32 static int parse_mode(const CHR* const command)
34 if ((!STRICMP(command, T("-h"))) || (!STRICMP(command, T("/?"))) || (!STRICMP(command, T("--help"))))
36 return SLUNK_MODE_HELP;
38 else if ((!STRICMP(command, T("-v"))) || (!STRICMP(command, T("--version"))))
40 return SLUNK_MODE_VERS;
42 else if ((!STRICMP(command, T("-e"))) || (!STRICMP(command, T("--encrypt"))))
44 return SLUNK_MODE_ENCR;
46 else if ((!STRICMP(command, T("-d"))) || (!STRICMP(command, T("--decrypt"))))
48 return SLUNK_MODE_DECR;
50 else if ((!STRICMP(command, T("-t"))) || (!STRICMP(command, T("--self-test"))))
52 return SLUNK_MODE_TEST;
56 FPRINTF(stderr, T("Error: The specified command \"%") T(PRISTR) T("\" is unknown!\n\n"), command);
61 static void print_manpage(const CHR *const program)
63 FPUTS(T("====================================================================\n"), stderr);
64 FPUTS(T("This software has been released under the CC0 1.0 Universal license:\n"), stderr);
65 FPUTS(T("https://creativecommons.org/publicdomain/zero/1.0/legalcode\n"), stderr);
66 FPUTS(T("====================================================================\n\n"), stderr);
67 FPUTS(T("Usage:\n"), stderr);
68 FPRINTF(stderr, T(" %") T(PRISTR) T(" --encrypt [[@][:]<passphrase>] <input.txt> <output.enc>\n"), program);
69 FPRINTF(stderr, T(" %") T(PRISTR) T(" --decrypt [[@][:]<passphrase>] <input.enc> <output.txt>\n\n"), program);
72 static char *read_passphrase(const CHR* const file_name)
74 const size_t max_len = SLUNKCRYPT_PWDLEN_MAX + 2U;
75 char *buffer = (char*) malloc(max_len * sizeof(char));
81 const int use_stdin = (STRICMP(file_name, T("-")) == 0);
82 FILE *const file = use_stdin ? stdin : FOPEN(file_name, T("rb"));
91 if (!fgets(buffer, (int)max_len, file))
97 size_t length = strlen(buffer);
98 while ((length > 0U) && ((buffer[length - 1U] == '\r') || (buffer[length - 1U] == '\n')))
100 buffer[--length] = '\0';
107 if ((!use_stdin) && file)
115 static int weak_passphrase(const char *str)
117 int flags[4U] = { 0, 0, 0, 0 };
120 const CHR c = *str++;
123 flags[isupper(c) ? 0U : 1U] = 1;
127 flags[isdigit(c) ? 2U : 3U] = 1;
130 const int strong = flags[0U] && flags[1U] && flags[2U] && flags[3U];
134 static int open_files(FILE **const file_in, FILE **const file_out, const CHR* const input_path, const CHR* const output_path)
136 *file_in = FOPEN(input_path, T("rb"));
139 FPUTS(T("Error: Failed to open input file for reading!\n\n"), stderr);
143 *file_out = FOPEN(output_path, T("wb"));
146 FPUTS(T("Error: Failed to open output file for writing!\n\n"), stderr);
154 static void sigint_handler(const int sig)
158 g_slunkcrypt_abort_flag = 1;
162 // ==========================================================================
164 // ==========================================================================
166 static int encrypt(const char* const passphrase, const CHR* const input_path, const CHR* const output_path)
168 slunkcrypt_t ctx = SLUNKCRYPT_NULL;
169 FILE * file_in = NULL, * file_out = NULL;
170 int result = EXIT_FAILURE;
172 if (open_files(&file_in, &file_out, input_path, output_path) != EXIT_SUCCESS)
177 const uint64_t file_size = get_file_size(file_in);
178 if (file_size == UINT64_MAX)
180 FPUTS(T("I/O error: Failed to determine size of input file!\n\n"), stderr);
183 else if (file_size < 1U)
185 FPUTS(T("Error: Input file is empty or an unsupported type!\n\n"), stderr);
189 FPUTS(T("Encrypting file contents, please be patient... "), stderr);
193 if (slunkcrypt_generate_salt(&salt) != SLUNKCRYPT_SUCCESS)
195 FPUTS(T("\n\nSlunkCrypt error: Failed to generate salt!\n\n"), stderr);
199 if (fwrite(&salt, sizeof(uint64_t), 1U, file_out) < 1U)
201 FPUTS(T("\n\nI/O error: Failed to write salt value!\n\n"), stderr);
205 ctx = slunkcrypt_alloc(salt, (const uint8_t*)passphrase, strlen(passphrase));
208 FPUTS(g_slunkcrypt_abort_flag ? T("\n\nProcess interrupted!\n\n") : T("\n\nSlunkCrypt error: Failed to initialize encryption!\n\n"), stderr);
212 clock_t clk_now, clk_update = clock();
213 uint64_t crc_actual = CRC_INITIALIZER, bytes_read = 0U;
214 uint8_t buffer[BUFFER_SIZE];
216 FPRINTF(stderr, T("%5.1f%% "), 0.0);
219 while (bytes_read < file_size)
221 const uint64_t bytes_remaining = file_size - bytes_read;
222 const size_t request_len = (bytes_remaining < BUFFER_SIZE) ? ((size_t)bytes_remaining) : BUFFER_SIZE;
223 const size_t count = fread(buffer, sizeof(uint8_t), request_len, file_in);
226 crc_actual = crc64_update(crc_actual, buffer, count);
228 const int status = slunkcrypt_encrypt_inplace(ctx, buffer, count);
229 if (status != SLUNKCRYPT_SUCCESS)
231 FPUTS((status == SLUNKCRYPT_ABORTED) ? T("\n\nProcess interrupted!\n\n") : T("\n\nSlunkCrypt error: Failed to encrypt data!\n\n"), stderr);
234 if (fwrite(buffer, sizeof(uint8_t), count, file_out) < count)
236 FPUTS(T("\n\nI/O error: Failed to write encrypted data!\n\n"), stderr);
240 if (count < request_len)
244 if ((clk_now = clock()) - clk_update > CLOCKS_PER_SEC)
246 FPRINTF(stderr, T("\b\b\b\b\b\b\b%5.1f%% "), (bytes_read / ((double)file_size)) * 100.0);
248 clk_update = clk_now;
254 FPUTS(T("\n\nI/O error: Failed to read input data!\n\n"), stderr);
258 if (bytes_read < file_size)
260 FPUTS(T("\n\nI/O error: Input file could not be fully read!\n\n"), stderr);
264 crc_actual = crc64_finish(crc_actual);
266 const int status = slunkcrypt_encrypt_inplace(ctx, (uint8_t*)&crc_actual, sizeof(uint64_t));
267 if (status != SLUNKCRYPT_SUCCESS)
269 FPUTS((status == SLUNKCRYPT_ABORTED) ? T("\n\nProcess interrupted!\n\n") : T("\n\nSlunkCrypt error: Failed to encrypt checksum!\n\n"), stderr);
273 if (fwrite(&crc_actual, sizeof(uint64_t), 1U, file_out) < 1U)
275 FPUTS(T("\n\nI/O error: Failed to write CRC checksum!\n\n"), stderr);
279 FPRINTF(stderr, T("\b\b\b\b\b\b\b%5.1f%%\n\n"), 100.0);
282 result = EXIT_SUCCESS;
284 FPUTS(T("All is done.\n\n"), stderr);
291 slunkcrypt_free(ctx);
307 // ==========================================================================
309 // ==========================================================================
311 static int decrypt(const char* const passphrase, const CHR* const input_path, const CHR* const output_path)
313 slunkcrypt_t ctx = SLUNKCRYPT_NULL;
314 FILE *file_in = NULL, *file_out = NULL;
315 int result = EXIT_FAILURE;
317 if (open_files(&file_in, &file_out, input_path, output_path) != EXIT_SUCCESS)
322 const uint64_t file_size = get_file_size(file_in);
323 if (file_size == UINT64_MAX)
325 FPUTS(T("I/O error: Failed to determine size of input file!\n\n"), stderr);
328 else if (file_size < 16U)
330 FPUTS(T("Error: Input file is too small! Truncated?\n\n"), stderr);
334 FPUTS(T("Decrypting file contents, please be patient... "), stderr);
338 if (fread(&salt, sizeof(uint64_t), 1U, file_in) < 1U)
340 FPUTS(T("\n\nI/O error: Failed to read salt value!\n\n"), stderr);
344 ctx = slunkcrypt_alloc(salt, (const uint8_t*)passphrase, strlen(passphrase));
347 FPUTS(g_slunkcrypt_abort_flag ? T("\n\nProcess interrupted!\n\n") : T("\n\nSlunkCrypt error: Failed to initialize decryption!\n\n"), stderr);
351 clock_t clk_now, clk_update = clock();
352 uint64_t crc_actual = CRC_INITIALIZER, bytes_read = sizeof(uint64_t);
353 uint8_t buffer[BUFFER_SIZE];
354 const uint64_t read_limit = file_size - sizeof(uint64_t);
356 FPRINTF(stderr, T("%5.1f%% "), 0.0);
359 while (bytes_read < read_limit)
361 const uint64_t bytes_remaining = read_limit - bytes_read;
362 const size_t request_len = (bytes_remaining < BUFFER_SIZE) ? ((size_t)bytes_remaining) : BUFFER_SIZE;
363 const size_t count = fread(buffer, sizeof(uint8_t), request_len, file_in);
367 const int status = slunkcrypt_decrypt_inplace(ctx, buffer, count);
368 if (status != SLUNKCRYPT_SUCCESS)
370 FPUTS((status == SLUNKCRYPT_ABORTED) ? T("\n\nProcess interrupted!\n\n") : T("\n\nSlunkCrypt error: Failed to decrypt data!\n\n"), stderr);
373 crc_actual = crc64_update(crc_actual, buffer, count);
374 if (fwrite(buffer, sizeof(uint8_t), count, file_out) < count)
376 FPUTS(T("failed!\n\nI/O error: Failed to write decrypted data!\n\n"), stderr);
380 if (count < request_len)
384 if ((clk_now = clock()) - clk_update > CLOCKS_PER_SEC)
386 FPRINTF(stderr, T("\b\b\b\b\b\b\b%5.1f%% "), (bytes_read / ((double)read_limit)) * 100.0);
388 clk_update = clk_now;
394 FPUTS(T("\n\nI/O error: Failed to read input data!\n\n"), stderr);
398 if (bytes_read < read_limit)
400 FPUTS(T("\n\nI/O error: Input file could not be fully read!\n\n"), stderr);
404 crc_actual = crc64_finish(crc_actual);
406 uint64_t crc_expected;
407 if (fread(&crc_expected, sizeof(uint64_t), 1U, file_in) < 1U)
409 FPUTS(T("\n\nI/O error: Failed to read CRC checksum!\n\n"), stderr);
413 const int status = slunkcrypt_decrypt_inplace(ctx, (uint8_t*)&crc_expected, sizeof(uint64_t));
414 if (status != SLUNKCRYPT_SUCCESS)
416 FPUTS((status == SLUNKCRYPT_ABORTED) ? T("\n\nProcess interrupted!\n\n") : T("\n\nSlunkCrypt error: Failed to decrypt checksum!\n\n"), stderr);
420 FPRINTF(stderr, T("\b\b\b\b\b\b\b%5.1f%%\n\n"), 100.0);
423 if (crc_actual != crc_expected)
425 FPRINTF(stderr, T("CRC error: Checksum mismatch detected! [expected: 0x%016") T(PRIX64) T(", actual: 0x%016") T(PRIX64) T("]\n\n"), crc_expected, crc_actual);
426 FPUTS(T("Wrong passphrase or corrupted file?\n\n"), stderr);
430 result = EXIT_SUCCESS;
432 FPUTS(T("CRC checksum is correct.\n\n"), stderr);
439 slunkcrypt_free(ctx);
455 // ==========================================================================
457 // ==========================================================================
459 static int run_test_case(const char *const message, const uint64_t checksum)
461 static const char* const passphrase = "OrpheanBeh0lderScry!Doubt";
463 int status, result = EXIT_FAILURE;
464 const size_t length = strlen(message) + 1U;
465 slunkcrypt_t ctx = SLUNKCRYPT_NULL;
468 if (slunkcrypt_generate_salt(&salt) != SLUNKCRYPT_SUCCESS)
470 FPUTS(T("\n\nWhoops: Failed to generate salt!\n\n"), stderr);
474 char* const text_temp = strdup(message);
477 FPUTS(T("\n\nWhoops: Failed to allocate text buffer!\n\n"), stderr);
481 const uint64_t crc_original = crc64_finish(crc64_update(CRC_INITIALIZER, (uint8_t*)text_temp, length));
482 if (crc_original != checksum)
484 FPRINTF(stderr, T("\n\nWhoops: Checksum mismatch detected! [expected: 0x%016") T(PRIX64) T(", actual: 0x%016") T(PRIX64) T("]\n\n"), checksum, crc_original);
488 ctx = slunkcrypt_alloc(salt, (const uint8_t*)passphrase, strlen(passphrase));
491 FPUTS(g_slunkcrypt_abort_flag ? T("\n\nProcess interrupted!\n\n") : T("\n\nWhoops: Failed to initialize encoder!\n\n"), stderr);
495 status = slunkcrypt_encrypt_inplace(ctx, (uint8_t*)text_temp, length);
496 if (status != SLUNKCRYPT_SUCCESS)
498 FPUTS((status == SLUNKCRYPT_ABORTED) ? T("\n\nProcess interrupted!\n\n") : T("\n\nWhoops: Failed to encrypt the message!\n\n"), stderr);
502 if (strncmp(message, text_temp, length) == 0)
504 FPUTS(T("\n\nWhoops: Encrypted message equals the original message!\n\n"), stderr);
508 status = slunkcrypt_reset(ctx, salt, (const uint8_t*)passphrase, strlen(passphrase));
509 if (status != SLUNKCRYPT_SUCCESS)
511 FPUTS((status == SLUNKCRYPT_ABORTED) ? T("\n\nProcess interrupted!\n\n") : T("\n\nWhoops: Failed to initialize decoder!\n\n"), stderr);
515 status = slunkcrypt_decrypt_inplace(ctx, (uint8_t*)text_temp, length);
516 if (status != SLUNKCRYPT_SUCCESS)
518 FPUTS((status == SLUNKCRYPT_ABORTED) ? T("\n\nProcess interrupted!\n\n") : T("\n\nWhoops: Failed to decrypt the message!\n\n"), stderr);
522 if (memcmp(message, text_temp, length * sizeof(char)) != 0)
524 FPUTS(T("\n\nWhoops: Decrypted message does *not* match the original message!\n\n"), stderr);
528 const uint64_t crc_decrypted = crc64_finish(crc64_update(CRC_INITIALIZER, (uint8_t*)text_temp, length));
529 if (crc_decrypted != crc_original)
531 FPRINTF(stderr, T("\n\nWhoops: Checksum mismatch detected! [expected: 0x%016") T(PRIX64) T(", actual: 0x%016") T(PRIX64) T("]\n\n"), crc_original, crc_decrypted);
535 result = EXIT_SUCCESS;
541 slunkcrypt_free(ctx);
546 slunkcrypt_bzero(text_temp, strlen(text_temp));
553 static int run_self_test(void)
555 static const size_t total = 32U;
556 const char* const test_data[] = { TEST_DATA_0, TEST_DATA_1, TEST_DATA_2, TEST_DATA_3, NULL };
557 const uint64_t test_chck[] = { TEST_CHCK_0, TEST_CHCK_1, TEST_CHCK_2, TEST_CHCK_3, 0x00 };
559 size_t completed = 0U;
560 FPRINTF(stderr, T("Self-test is running, please be patient... %2u/%2u "), (unsigned int)completed, (unsigned int)total);
563 for (size_t i = 0U; i < 8U; ++i)
565 for (size_t j = 0U; test_data[j]; ++j)
567 FPRINTF(stderr, T("\b\b\b\b\b\b%2u/%2u "), (unsigned int)++completed, (unsigned int)total);
569 if (run_test_case(test_data[j], test_chck[j]) != EXIT_SUCCESS)
576 FPRINTF(stderr, T("\b\b\b\b\b\b%2u/%2u\n\nCompleted successfully.\n\n"), (unsigned int)total, (unsigned int)total);
582 // ==========================================================================
584 // ==========================================================================
586 int MAIN(const int argc, CHR *const argv[])
589 setup_signal_handler(SIGINT, sigint_handler);
590 int result = EXIT_FAILURE;
591 char *passphrase_buffer = NULL;
593 FPRINTF(stderr, T("SlunkCrypt Utility (%") T(PRIstr) T("-%") T(PRIstr) T("), by LoRd_MuldeR <MuldeR2@GMX.de>\n"), OS_TYPE, CPU_ARCH);
594 FPRINTF(stderr, T("Using libSlunkCrypt v%u.%u.%u [%") T(PRIstr) T("]\n\n"), SLUNKCRYPT_VERSION_MAJOR, SLUNKCRYPT_VERSION_MINOR, SLUNKCRYPT_VERSION_PATCH, SLUNKCRYPT_BUILD);
596 slunkcrypt_startup();
598 /* ----------------------------------------------------- */
599 /* Parse arguments */
600 /* ----------------------------------------------------- */
604 FPRINTF(stderr, T("Error: Nothing to do. Please type '%") T(PRISTR) T(" --help' for details!\n\n"), get_file_name(argv[0U]));
608 const int mode = parse_mode(argv[1U]);
611 case SLUNK_MODE_HELP:
612 print_manpage(get_file_name(argv[0U]));
613 case SLUNK_MODE_VERS:
614 result = EXIT_SUCCESS;
616 case SLUNK_MODE_TEST:
617 result = run_self_test();
623 FPRINTF(stderr, T("Error: Required argument is missing. Please type '%") T(PRISTR) T(" --help' for details!\n\n"), get_file_name(argv[0U]));
627 const CHR* const passphrase = (argc > 4) ? argv[2U] : GETENV(ENVV_PASSWD_NAME);
628 const CHR* const input_file = argv[(argc > 4) ? 3U : 2U], * const output_file = argv[(argc > 4) ? 4U : 3U];
630 if ((!passphrase) || (!passphrase[0U]) || (((passphrase[0U] == T('@')) || (passphrase[0U] == T(':'))) && (!passphrase[1U])))
632 FPUTS(T("Error: The specified passphrase must not be empty!\n\n"), stderr);
636 if ((!input_file[0U]) || (!output_file[0U]))
638 FPUTS(T("Error: The input file and/or output file must not be empty!\n\n"), stderr);
642 /* ----------------------------------------------------- */
643 /* Initialize passphrase */
644 /* ----------------------------------------------------- */
646 passphrase_buffer = (passphrase[0U] == T('@')) ? read_passphrase(passphrase + 1U) : CHR_to_utf8((passphrase[0U] == T(':')) ? (passphrase + 1U) : passphrase);
647 if (!passphrase_buffer)
649 FPUTS(T("Error: Failed to read the passphrase!\n\n"), stderr);
653 slunkcrypt_bzero((CHR*)passphrase, STRLEN(passphrase) * sizeof(CHR));
655 const size_t passphrase_len = strlen(passphrase_buffer);
656 if (passphrase_len < SLUNKCRYPT_PWDLEN_MIN)
658 FPRINTF(stderr, T("Error: Passphrase must be at least %") T(PRIu64) T(" characters in length!\n\n"), (uint64_t)SLUNKCRYPT_PWDLEN_MIN);
661 else if (passphrase_len > SLUNKCRYPT_PWDLEN_MAX)
663 FPRINTF(stderr, T("Error: Passphrase must be at most %") T(PRIu64) T(" characters in length!\n\n"), (uint64_t)SLUNKCRYPT_PWDLEN_MAX);
667 if (passphrase_len < 12U)
669 FPUTS(T("Warning: Using a *short* passphrase; a length of 12 characters or more is recommended!\n\n"), stderr);
671 else if (weak_passphrase(passphrase_buffer))
673 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);
676 /* ----------------------------------------------------- */
677 /* Encrypt or decrypt */
678 /* ----------------------------------------------------- */
680 const clock_t clk_start = clock();
684 case SLUNK_MODE_ENCR:
685 result = encrypt(passphrase_buffer, input_file, output_file);
687 case SLUNK_MODE_DECR:
688 result = decrypt(passphrase_buffer, input_file, output_file);
691 FPUTS(T("Unexpected mode encountered!\n\n"), stderr);
694 if (!g_slunkcrypt_abort_flag)
696 FPUTS(T("--------\n\n"), stderr);
698 const clock_t clk_end = clock();
699 FPRINTF(stderr, T("Operation completed after %.1f seconds.\n\n"), (clk_end - clk_start) / ((double)CLOCKS_PER_SEC));
702 /* ----------------------------------------------------- */
704 /* ----------------------------------------------------- */
708 if (passphrase_buffer)
710 slunkcrypt_bzero(passphrase_buffer, strlen(passphrase_buffer));
711 free(passphrase_buffer);
714 slunkcrypt_cleanup();
718 #if defined(_WIN32) && defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
719 void __wgetmainargs(int*, wchar_t***, wchar_t***, int, int*);
722 wchar_t** enpv, ** argv;
724 __wgetmainargs(&argc, &argv, &enpv, 1, &si);
725 return wmain(argc, argv);