OSDN Git Service

C++ wrapper has been implemented.
[slunkcrypt/SlunkCrypt.git] / frontend / src / main.c
1 /******************************************************************************/
2 /* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de>                                */
3 /* This work has been released under the CC0 1.0 Universal license!           */
4 /******************************************************************************/
5
6 #define _CRT_SECURE_NO_WARNINGS 1
7
8 #include <slunkcrypt.h>
9 #include "utils.h"
10 #include "crc.h"
11 #include "test.h"
12 #include <string.h>
13 #include <time.h>
14 #include <inttypes.h>
15 #include <ctype.h>
16 #include <signal.h>
17
18 #define BUFFER_SIZE 4096U
19
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
25
26 static const CHR *const ENVV_PASSWD_NAME = T("SLUNK_PASSPHRASE");
27
28 // ==========================================================================
29 // Auxiliary functions
30 // ==========================================================================
31
32 static int parse_mode(const CHR* const command)
33 {
34         if ((!STRICMP(command, T("-h"))) || (!STRICMP(command, T("/?"))) || (!STRICMP(command, T("--help"))))
35         {
36                 return SLUNK_MODE_HELP;
37         }
38         else if ((!STRICMP(command, T("-v"))) || (!STRICMP(command, T("--version"))))
39         {
40                 return SLUNK_MODE_VERS;
41         }
42         else if ((!STRICMP(command, T("-e"))) || (!STRICMP(command, T("--encrypt"))))
43         {
44                 return SLUNK_MODE_ENCR;
45         }
46         else if ((!STRICMP(command, T("-d"))) || (!STRICMP(command, T("--decrypt"))))
47         {
48                 return SLUNK_MODE_DECR;
49         }
50         else if ((!STRICMP(command, T("-t"))) || (!STRICMP(command, T("--self-test"))))
51         {
52                 return SLUNK_MODE_TEST;
53         }
54         else
55         {
56                 FPRINTF(stderr, T("Error: The specified command \"%") T(PRISTR) T("\" is unknown!\n\n"), command);
57                 exit(EXIT_FAILURE);
58         }
59 }
60
61 static void print_manpage(const CHR *const program)
62 {
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);
70 }
71
72 static char *read_passphrase(const CHR* const file_name)
73 {
74         const size_t max_len = SLUNKCRYPT_PWDLEN_MAX + 2U;
75         char *buffer = (char*) malloc(max_len * sizeof(char));
76         if (!buffer)
77         {
78                 return NULL;
79         }
80
81         const int use_stdin = (STRICMP(file_name, T("-")) == 0);
82         FILE *const file = use_stdin ? stdin : FOPEN(file_name, T("rb"));
83         if (!file)
84         {
85                 free(buffer);
86                 return NULL;
87         }
88
89         do
90         {
91                 if (!fgets(buffer, (int)max_len, file))
92                 {
93                         free(buffer);
94                         buffer = NULL;
95                         goto finish;
96                 }
97                 size_t length = strlen(buffer);
98                 while ((length > 0U) && ((buffer[length - 1U] == '\r') || (buffer[length - 1U] == '\n')))
99                 {
100                         buffer[--length] = '\0';
101                 }
102         }
103         while (!buffer[0U]);
104
105 finish:
106
107         if ((!use_stdin) && file)
108         {
109                 fclose(file);
110         }
111
112         return buffer;
113 }
114
115 static int weak_passphrase(const char *str)
116 {
117         int flags[4U] = { 0, 0, 0, 0 };
118         while (*str)
119         {
120                 const CHR c = *str++;
121                 if (isalpha(c))
122                 {
123                         flags[isupper(c) ? 0U : 1U] = 1;
124                 }
125                 else 
126                 {
127                         flags[isdigit(c) ? 2U : 3U] = 1;
128                 }
129         }
130         const int strong = flags[0U] && flags[1U] && flags[2U] && flags[3U];
131         return !strong;
132 }
133
134 static int open_files(FILE **const file_in, FILE **const file_out, const CHR* const input_path, const CHR* const output_path)
135 {
136         *file_in = FOPEN(input_path, T("rb"));
137         if (!(*file_in))
138         {
139                 FPUTS(T("Error: Failed to open input file for reading!\n\n"), stderr);
140                 return EXIT_FAILURE;
141         }
142
143         *file_out = FOPEN(output_path, T("wb"));
144         if (!(*file_out))
145         {
146                 FPUTS(T("Error: Failed to open output file for writing!\n\n"), stderr);
147                 fclose(*file_out);
148                 return EXIT_FAILURE;
149         }
150
151         return EXIT_SUCCESS;
152 }
153
154 static void sigint_handler(const int sig)
155 {
156         if (sig == SIGINT)
157         {
158                 g_slunkcrypt_abort_flag = 1;
159         }
160 }
161
162 // ==========================================================================
163 // Encrypt
164 // ==========================================================================
165
166 static int encrypt(const char* const passphrase, const CHR* const input_path, const CHR* const output_path)
167 {
168         slunkcrypt_t ctx = SLUNKCRYPT_NULL;
169         FILE * file_in = NULL, * file_out = NULL;
170         int result = EXIT_FAILURE;
171
172         if (open_files(&file_in, &file_out, input_path, output_path) != EXIT_SUCCESS)
173         {
174                 goto clean_up;;
175         }
176
177         const uint64_t file_size = get_file_size(file_in);
178         if (file_size == UINT64_MAX)
179         {
180                 FPUTS(T("I/O error: Failed to determine size of input file!\n\n"), stderr);
181                 goto clean_up;
182         }
183         else if (file_size < 1U)
184         {
185                 FPUTS(T("Error: Input file is empty or an unsupported type!\n\n"), stderr);
186                 goto clean_up;
187         }
188
189         FPUTS(T("Encrypting file contents, please be patient... "), stderr);
190         fflush(stderr);
191
192         uint64_t salt;
193         if (slunkcrypt_generate_salt(&salt) != SLUNKCRYPT_SUCCESS)
194         {
195                 FPUTS(T("\n\nSlunkCrypt error: Failed to generate salt!\n\n"), stderr);
196                 goto  clean_up;
197         }
198
199         if (fwrite(&salt, sizeof(uint64_t), 1U, file_out) < 1U)
200         {
201                 FPUTS(T("\n\nI/O error: Failed to write salt value!\n\n"), stderr);
202                 goto clean_up;
203         }
204
205         ctx = slunkcrypt_alloc(salt, (const uint8_t*)passphrase, strlen(passphrase));
206         if (!ctx)
207         {
208                 FPUTS(g_slunkcrypt_abort_flag ? T("\n\nProcess interrupted!\n\n") : T("\n\nSlunkCrypt error: Failed to initialize encryption!\n\n"), stderr);
209                 goto clean_up;
210         }
211
212         clock_t clk_now, clk_update = clock();
213         uint64_t crc_actual = CRC_INITIALIZER, bytes_read = 0U;
214         uint8_t buffer[BUFFER_SIZE];
215
216         FPRINTF(stderr, T("%5.1f%% "), 0.0);
217         fflush(stderr);
218
219         while (bytes_read < file_size)
220         {
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);
224                 if (count > 0U)
225                 {
226                         crc_actual = crc64_update(crc_actual, buffer, count);
227                         bytes_read += count;
228                         const int status = slunkcrypt_encrypt_inplace(ctx, buffer, count);
229                         if (status != SLUNKCRYPT_SUCCESS)
230                         {
231                                 FPUTS((status == SLUNKCRYPT_ABORTED) ? T("\n\nProcess interrupted!\n\n") : T("\n\nSlunkCrypt error: Failed to encrypt data!\n\n"), stderr);
232                                 goto  clean_up;
233                         }
234                         if (fwrite(buffer, sizeof(uint8_t), count, file_out) < count)
235                         {
236                                 FPUTS(T("\n\nI/O error: Failed to write encrypted data!\n\n"), stderr);
237                                 goto clean_up;
238                         }
239                 }
240                 if (count < request_len)
241                 {
242                         break; /*EOF*/
243                 }
244                 if ((clk_now = clock()) - clk_update > CLOCKS_PER_SEC)
245                 {
246                         FPRINTF(stderr, T("\b\b\b\b\b\b\b%5.1f%% "), (bytes_read / ((double)file_size)) * 100.0);
247                         fflush(stderr);
248                         clk_update = clk_now;
249                 }
250         }
251
252         if (ferror(file_in))
253         {
254                 FPUTS(T("\n\nI/O error: Failed to read input data!\n\n"), stderr);
255                 goto clean_up;
256         }
257
258         if (bytes_read < file_size)
259         {
260                 FPUTS(T("\n\nI/O error: Input file could not be fully read!\n\n"), stderr);
261                 goto clean_up;
262         }
263
264         crc_actual = crc64_finish(crc_actual);
265
266         const int status = slunkcrypt_encrypt_inplace(ctx, (uint8_t*)&crc_actual, sizeof(uint64_t));
267         if (status != SLUNKCRYPT_SUCCESS)
268         {
269                 FPUTS((status == SLUNKCRYPT_ABORTED) ? T("\n\nProcess interrupted!\n\n") : T("\n\nSlunkCrypt error: Failed to encrypt checksum!\n\n"), stderr);
270                 goto  clean_up;
271         }
272
273         if (fwrite(&crc_actual, sizeof(uint64_t), 1U, file_out) < 1U)
274         {
275                 FPUTS(T("\n\nI/O error: Failed to write CRC checksum!\n\n"), stderr);
276                 goto clean_up;
277         }
278
279         FPRINTF(stderr, T("\b\b\b\b\b\b\b%5.1f%%\n\n"), 100.0);
280         fflush(stderr);
281
282         result = EXIT_SUCCESS;
283
284         FPUTS(T("All is done.\n\n"), stderr);
285         fflush(stderr);
286
287 clean_up:
288
289         if (ctx)
290         {
291                 slunkcrypt_free(ctx);
292         }
293
294         if (file_out)
295         {
296                 fclose(file_out);
297         }
298
299         if (file_in)
300         {
301                 fclose(file_in);
302         }
303
304         return result;
305 }
306
307 // ==========================================================================
308 // Decrypt
309 // ==========================================================================
310
311 static int decrypt(const char* const passphrase, const CHR* const input_path, const CHR* const output_path)
312 {
313         slunkcrypt_t ctx = SLUNKCRYPT_NULL;
314         FILE *file_in = NULL, *file_out = NULL;
315         int result = EXIT_FAILURE;
316
317         if (open_files(&file_in, &file_out, input_path, output_path) != EXIT_SUCCESS)
318         {
319                 goto clean_up;
320         }
321
322         const uint64_t file_size = get_file_size(file_in);
323         if (file_size == UINT64_MAX)
324         {
325                 FPUTS(T("I/O error: Failed to determine size of input file!\n\n"), stderr);
326                 goto clean_up;
327         }
328         else if (file_size < 16U)
329         {
330                 FPUTS(T("Error: Input file is too small! Truncated?\n\n"), stderr);
331                 goto clean_up;
332         }
333
334         FPUTS(T("Decrypting file contents, please be patient... "), stderr);
335         fflush(stderr);
336
337         uint64_t salt;
338         if (fread(&salt, sizeof(uint64_t), 1U, file_in) < 1U)
339         {
340                 FPUTS(T("\n\nI/O error: Failed to read salt value!\n\n"), stderr);
341                 goto clean_up;
342         }
343
344         ctx = slunkcrypt_alloc(salt, (const uint8_t*)passphrase, strlen(passphrase));
345         if (!ctx)
346         {
347                 FPUTS(g_slunkcrypt_abort_flag ? T("\n\nProcess interrupted!\n\n") : T("\n\nSlunkCrypt error: Failed to initialize decryption!\n\n"), stderr);
348                 goto clean_up;
349         }
350         
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);
355
356         FPRINTF(stderr, T("%5.1f%% "), 0.0);
357         fflush(stderr);
358
359         while (bytes_read < read_limit)
360         {
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);
364                 if (count > 0U)
365                 {
366                         bytes_read += count;
367                         const int status = slunkcrypt_decrypt_inplace(ctx, buffer, count);
368                         if (status != SLUNKCRYPT_SUCCESS)
369                         {
370                                 FPUTS((status == SLUNKCRYPT_ABORTED) ? T("\n\nProcess interrupted!\n\n") : T("\n\nSlunkCrypt error: Failed to decrypt data!\n\n"), stderr);
371                                 goto clean_up;
372                         }
373                         crc_actual = crc64_update(crc_actual, buffer, count);
374                         if (fwrite(buffer, sizeof(uint8_t), count, file_out) < count)
375                         {
376                                 FPUTS(T("failed!\n\nI/O error: Failed to write decrypted data!\n\n"), stderr);
377                                 goto clean_up;
378                         }
379                 }
380                 if (count < request_len)
381                 {
382                         break; /*EOF*/
383                 }
384                 if ((clk_now = clock()) - clk_update > CLOCKS_PER_SEC)
385                 {
386                         FPRINTF(stderr, T("\b\b\b\b\b\b\b%5.1f%% "), (bytes_read / ((double)read_limit)) * 100.0);
387                         fflush(stderr);
388                         clk_update = clk_now;
389                 }
390         }
391
392         if (ferror(file_in))
393         {
394                 FPUTS(T("\n\nI/O error: Failed to read input data!\n\n"), stderr);
395                 goto clean_up;
396         }
397
398         if (bytes_read < read_limit)
399         {
400                 FPUTS(T("\n\nI/O error: Input file could not be fully read!\n\n"), stderr);
401                 goto clean_up;
402         }
403
404         crc_actual = crc64_finish(crc_actual);
405
406         uint64_t crc_expected;
407         if (fread(&crc_expected, sizeof(uint64_t), 1U, file_in) < 1U)
408         {
409                 FPUTS(T("\n\nI/O error: Failed to read CRC checksum!\n\n"), stderr);
410                 goto clean_up;
411         }
412
413         const int status = slunkcrypt_decrypt_inplace(ctx, (uint8_t*)&crc_expected, sizeof(uint64_t));
414         if (status != SLUNKCRYPT_SUCCESS)
415         {
416                 FPUTS((status == SLUNKCRYPT_ABORTED) ? T("\n\nProcess interrupted!\n\n") : T("\n\nSlunkCrypt error: Failed to decrypt checksum!\n\n"), stderr);
417                 goto  clean_up;
418         }
419
420         FPRINTF(stderr, T("\b\b\b\b\b\b\b%5.1f%%\n\n"), 100.0);
421         fflush(stderr);
422
423         if (crc_actual != crc_expected)
424         {
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);
427                 goto clean_up;
428         }
429
430         result = EXIT_SUCCESS;
431
432         FPUTS(T("CRC checksum is correct.\n\n"), stderr);
433         fflush(stderr);
434
435 clean_up:
436
437         if (ctx)
438         {
439                 slunkcrypt_free(ctx);
440         }
441
442         if (file_out)
443         {
444                 fclose(file_out);
445         }
446
447         if (file_in)
448         {
449                 fclose(file_in);
450         }
451
452         return result;
453 }
454
455 // ==========================================================================
456 // Self-test
457 // ==========================================================================
458
459 static int run_test_case(const char *const message, const uint64_t checksum)
460 {
461         static const char* const passphrase = "OrpheanBeh0lderScry!Doubt";
462
463         int status, result = EXIT_FAILURE;
464         const size_t length = strlen(message) + 1U;
465         slunkcrypt_t ctx = SLUNKCRYPT_NULL;
466
467         uint64_t salt;
468         if (slunkcrypt_generate_salt(&salt) != SLUNKCRYPT_SUCCESS)
469         {
470                 FPUTS(T("\n\nWhoops: Failed to generate salt!\n\n"), stderr);
471                 return EXIT_FAILURE;
472         }
473
474         char* const text_temp = strdup(message);
475         if (!text_temp)
476         {
477                 FPUTS(T("\n\nWhoops: Failed to allocate text buffer!\n\n"), stderr);
478                 goto clean_up;
479         }
480
481         const uint64_t crc_original = crc64_finish(crc64_update(CRC_INITIALIZER, (uint8_t*)text_temp, length));
482         if (crc_original != checksum)
483         {
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);
485                 goto clean_up;
486         }
487
488         ctx = slunkcrypt_alloc(salt, (const uint8_t*)passphrase, strlen(passphrase));
489         if (!ctx)
490         {
491                 FPUTS(g_slunkcrypt_abort_flag ? T("\n\nProcess interrupted!\n\n") : T("\n\nWhoops: Failed to initialize encoder!\n\n"), stderr);
492                 goto clean_up;
493         }
494
495         status = slunkcrypt_encrypt_inplace(ctx, (uint8_t*)text_temp, length);
496         if (status != SLUNKCRYPT_SUCCESS)
497         {
498                 FPUTS((status == SLUNKCRYPT_ABORTED) ? T("\n\nProcess interrupted!\n\n") : T("\n\nWhoops: Failed to encrypt the message!\n\n"), stderr);
499                 goto clean_up;
500         }
501
502         if (strncmp(message, text_temp, length) == 0)
503         {
504                 FPUTS(T("\n\nWhoops: Encrypted message equals the original message!\n\n"), stderr);
505                 goto clean_up;
506         }
507
508         status = slunkcrypt_reset(ctx, salt, (const uint8_t*)passphrase, strlen(passphrase));
509         if (status != SLUNKCRYPT_SUCCESS)
510         {
511                 FPUTS((status == SLUNKCRYPT_ABORTED) ? T("\n\nProcess interrupted!\n\n") : T("\n\nWhoops: Failed to initialize decoder!\n\n"), stderr);
512                 goto clean_up;
513         }
514
515         status = slunkcrypt_decrypt_inplace(ctx, (uint8_t*)text_temp, length);
516         if (status != SLUNKCRYPT_SUCCESS)
517         {
518                 FPUTS((status == SLUNKCRYPT_ABORTED) ? T("\n\nProcess interrupted!\n\n") : T("\n\nWhoops: Failed to decrypt the message!\n\n"), stderr);
519                 goto clean_up;
520         }
521
522         if (memcmp(message, text_temp, length * sizeof(char)) != 0)
523         {
524                 FPUTS(T("\n\nWhoops: Decrypted message does *not* match the original message!\n\n"), stderr);
525                 goto clean_up;
526         }
527
528         const uint64_t crc_decrypted = crc64_finish(crc64_update(CRC_INITIALIZER, (uint8_t*)text_temp, length));
529         if (crc_decrypted != crc_original)
530         {
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);
532                 goto clean_up;
533         }
534
535         result = EXIT_SUCCESS;
536
537 clean_up:
538
539         if (ctx)
540         {
541                 slunkcrypt_free(ctx);
542         }
543
544         if (text_temp)
545         {
546                 slunkcrypt_bzero(text_temp, strlen(text_temp));
547                 free(text_temp);
548         }
549
550         return result;
551 }
552
553 static int run_self_test(void)
554 {
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 };
558
559         size_t completed = 0U;
560         FPRINTF(stderr, T("Self-test is running, please be patient... %2u/%2u "), (unsigned int)completed, (unsigned int)total);
561         fflush(stderr);
562
563         for (size_t i = 0U; i < 8U; ++i)
564         {
565                 for (size_t j = 0U; test_data[j]; ++j)
566                 {
567                         FPRINTF(stderr, T("\b\b\b\b\b\b%2u/%2u "), (unsigned int)++completed, (unsigned int)total);
568                         fflush(stderr);
569                         if (run_test_case(test_data[j], test_chck[j]) != EXIT_SUCCESS)
570                         {
571                                 return EXIT_FAILURE;
572                         }
573                 }
574         }
575
576         FPRINTF(stderr, T("\b\b\b\b\b\b%2u/%2u\n\nCompleted successfully.\n\n"), (unsigned int)total, (unsigned int)total);
577         fflush(stderr);
578
579         return EXIT_SUCCESS;
580 }
581
582 // ==========================================================================
583 // Main function
584 // ==========================================================================
585
586 int MAIN(const int argc, CHR *const argv[])
587 {
588         init_terminal();
589         setup_signal_handler(SIGINT, sigint_handler);
590         int result = EXIT_FAILURE;
591         char *passphrase_buffer = NULL;
592
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);
595
596         slunkcrypt_startup();
597
598         /* ----------------------------------------------------- */
599         /* Parse arguments                                       */
600         /* ----------------------------------------------------- */
601
602         if (argc < 2)
603         {
604                 FPRINTF(stderr, T("Error: Nothing to do. Please type '%") T(PRISTR) T(" --help' for details!\n\n"), get_file_name(argv[0U]));
605                 goto clean_up;
606         }
607
608         const int mode = parse_mode(argv[1U]);
609         switch (mode)
610         {
611         case SLUNK_MODE_HELP:
612                 print_manpage(get_file_name(argv[0U]));
613         case SLUNK_MODE_VERS:
614                 result = EXIT_SUCCESS;
615                 goto clean_up;
616         case SLUNK_MODE_TEST:
617                 result = run_self_test();
618                 goto clean_up;
619         }
620
621         if (argc < 4)
622         {
623                 FPRINTF(stderr, T("Error: Required argument is missing. Please type '%") T(PRISTR) T(" --help' for details!\n\n"), get_file_name(argv[0U]));
624                 goto clean_up;
625         }
626
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];
629
630         if ((!passphrase) || (!passphrase[0U]) || (((passphrase[0U] == T('@')) || (passphrase[0U] == T(':'))) && (!passphrase[1U])))
631         {
632                 FPUTS(T("Error: The specified passphrase must not be empty!\n\n"), stderr);
633                 goto clean_up;
634         }
635
636         if ((!input_file[0U]) || (!output_file[0U]))
637         {
638                 FPUTS(T("Error: The input file and/or output file must not be empty!\n\n"), stderr);
639                 goto clean_up;
640         }
641
642         /* ----------------------------------------------------- */
643         /* Initialize passphrase                                 */
644         /* ----------------------------------------------------- */
645
646         passphrase_buffer = (passphrase[0U] == T('@')) ? read_passphrase(passphrase + 1U) : CHR_to_utf8((passphrase[0U] == T(':')) ? (passphrase + 1U) : passphrase);
647         if (!passphrase_buffer)
648         {
649                 FPUTS(T("Error: Failed to read the passphrase!\n\n"), stderr);
650                 goto clean_up;
651         }
652
653         slunkcrypt_bzero((CHR*)passphrase, STRLEN(passphrase) * sizeof(CHR));
654
655         const size_t passphrase_len = strlen(passphrase_buffer);
656         if (passphrase_len < SLUNKCRYPT_PWDLEN_MIN)
657         {
658                 FPRINTF(stderr, T("Error: Passphrase must be at least %") T(PRIu64) T(" characters in length!\n\n"), (uint64_t)SLUNKCRYPT_PWDLEN_MIN);
659                 goto clean_up;
660         }
661         else if (passphrase_len > SLUNKCRYPT_PWDLEN_MAX)
662         {
663                 FPRINTF(stderr, T("Error: Passphrase must be at most %") T(PRIu64) T(" characters in length!\n\n"), (uint64_t)SLUNKCRYPT_PWDLEN_MAX);
664                 goto clean_up;
665         }
666
667         if (passphrase_len < 12U)
668         {
669                 FPUTS(T("Warning: Using a *short* passphrase; a length of 12 characters or more is recommended!\n\n"), stderr);
670         }
671         else if (weak_passphrase(passphrase_buffer))
672         {
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);
674         }
675
676         /* ----------------------------------------------------- */
677         /* Encrypt or decrypt                                    */
678         /* ----------------------------------------------------- */
679
680         const clock_t clk_start = clock();
681
682         switch (mode)
683         {
684         case SLUNK_MODE_ENCR:
685                 result = encrypt(passphrase_buffer, input_file, output_file);
686                 break;
687         case SLUNK_MODE_DECR:
688                 result = decrypt(passphrase_buffer, input_file, output_file);
689                 break;
690         default:
691                 FPUTS(T("Unexpected mode encountered!\n\n"), stderr);
692         }
693
694         if (!g_slunkcrypt_abort_flag)
695         {
696                 FPUTS(T("--------\n\n"), stderr);
697                 fflush(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));
700         }
701
702         /* ----------------------------------------------------- */
703         /* Final clean-up                                        */
704         /* ----------------------------------------------------- */
705
706 clean_up:
707         
708         if (passphrase_buffer)
709         {
710                 slunkcrypt_bzero(passphrase_buffer, strlen(passphrase_buffer));
711                 free(passphrase_buffer);
712         }
713
714         slunkcrypt_cleanup();
715         return result;
716 }
717
718 #if defined(_WIN32) && defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
719 void __wgetmainargs(int*, wchar_t***, wchar_t***, int, int*);
720 int main()
721 {
722         wchar_t** enpv, ** argv;
723         int argc, si = 0;
724         __wgetmainargs(&argc, &argv, &enpv, 1, &si);
725         return wmain(argc, argv);
726 }
727 #endif