OSDN Git Service

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