Small improvement to platform detection.
[slunkcrypt.git] / frontend / src / main.c
blob7348016db4f6d719b6188e212a7ce37f0cb7be32
1 /******************************************************************************/
2 /* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de> */
3 /* This work has been released under the CC0 1.0 Universal license! */
4 /******************************************************************************/
6 #ifdef _WIN32
7 # define _CRT_SECURE_NO_WARNINGS 1
8 #else
9 # define _GNU_SOURCE 1
10 #endif
12 /* Internal */
13 #include <slunkcrypt.h>
14 #include "utils.h"
15 #include "crypt.h"
16 #include "pwgen.h"
17 #include "selftest.h"
19 /* CRT */
20 #include <string.h>
21 #include <time.h>
22 #include <inttypes.h>
23 #include <ctype.h>
24 #include <signal.h>
26 // ==========================================================================
27 // Constants
28 // ==========================================================================
30 #define MODE_HELP 0
31 #define MODE_VERS 1
32 #define MODE_ENCR 2
33 #define MODE_DECR 3
34 #define MODE_PASS 4
35 #define MODE_TEST 5
37 static const size_t RCMD_PWDLEN_LENGTH = 12U;
38 static const size_t DFLT_PWDLEN_LENGTH = 24U;
40 static const CHR* const ENV_PASSWORD = T("SLUNK_PASSPHRASE");
41 static const CHR* const ENV_KEEPFILE = T("SLUNK_KEEP_INCOMPLETE");
42 static const CHR* const ENV_NTHREADS = T("SLUNK_THREADS");
44 static const CHR* const PREFIX_PASS = T("pass:");
45 static const CHR* const PREFIX_FILE = T("file:");
47 // ==========================================================================
48 // Auxiliary functions
49 // ==========================================================================
51 #define PW_FROM_ENV (!(argc > 4))
53 static int parse_slunk_mode(const CHR* const command)
55 if ((!STRICMP(command, T("-h"))) || (!STRICMP(command, T("/?"))) || (!STRICMP(command, T("--help"))))
57 return MODE_HELP;
59 else if ((!STRICMP(command, T("-v"))) || (!STRICMP(command, T("--version"))))
61 return MODE_VERS;
63 else if ((!STRICMP(command, T("-e"))) || (!STRICMP(command, T("--encrypt"))))
65 return MODE_ENCR;
67 else if ((!STRICMP(command, T("-d"))) || (!STRICMP(command, T("--decrypt"))))
69 return MODE_DECR;
71 else if ((!STRICMP(command, T("-p"))) || (!STRICMP(command, T("--make-pw"))))
73 return MODE_PASS;
75 else if ((!STRICMP(command, T("-t"))) || (!STRICMP(command, T("--self-test"))))
77 return MODE_TEST;
79 else
81 return -1; /*invalid command*/
85 static void print_manpage(const CHR *const program)
87 FPUTS(T("====================================================================\n"), stderr);
88 FPUTS(T("This software has been released under the CC0 1.0 Universal license:\n"), stderr);
89 FPUTS(T("https://creativecommons.org/publicdomain/zero/1.0/legalcode\n"), stderr);
90 FPUTS(T("====================================================================\n\n"), stderr);
91 FPUTS(T("Usage:\n"), stderr);
92 FPRINTF(stderr, T(" %") T(PRISTR) T(" --encrypt [pass:<pass>|file:<file>] <input.txt> <output.enc>\n"), program);
93 FPRINTF(stderr, T(" %") T(PRISTR) T(" --decrypt [pass:<pass>|file:<file>] <input.enc> <output.txt>\n"), program);
94 FPRINTF(stderr, T(" %") T(PRISTR) T(" --make-pw [<length>]\n\n"), program);
95 FPRINTF(stderr, T("Optionally, reads passphrase from the %") T(PRISTR) T(" environment variable.\n\n"), ENV_PASSWORD);
98 static char *copy_passphrase(const CHR *const passphrase)
100 if ((!passphrase) || (!passphrase[0U]))
102 FPUTS(T("Error: The passphrase input string must not be empty!\n\n"), stderr);
103 return NULL;
106 char *const buffer = CHR_to_utf8(passphrase);
107 if (!buffer)
109 FPUTS(T("Error: Failed to allocate the string buffer!\n\n"), stderr);
112 return buffer;
115 static uint32_t environ_get_uint(const CHR *const name)
117 const CHR *const value = GETENV(name);
118 if (value)
120 return (uint32_t) STRTOUL(value);
122 return 0U;
125 static int environ_get_flag(const CHR *const name)
127 return (environ_get_uint(name) != 0);
130 static void check_excess_arguments(const int argc, int maximum)
132 if (argc > maximum)
134 FPUTS(T("Warning: Excess command-line argument(s) will be ignored!\n\n"), stderr);
135 fflush(stderr);
139 static void sigint_handler(const int sig)
141 if (sig == SIGINT)
143 g_slunkcrypt_abort_flag = 1;
147 // ==========================================================================
148 // Main function
149 // ==========================================================================
151 int MAIN(const int argc, CHR *const argv[])
153 int result = EXIT_FAILURE;
154 const CHR *input_file = NULL, *output_file = NULL;
155 char *passphrase_buffer = NULL;
157 init_terminal();
158 setup_signal_handler(SIGINT, sigint_handler);
160 FPRINTF(stderr, T("SlunkCrypt Utility (%") T(PRIstr) T("-%") T(PRIstr) T("), by LoRd_MuldeR <MuldeR2@GMX.de>\n"), OS_TYPE_NAME, CPU_ARCH);
161 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);
163 fflush(stderr);
165 /* ----------------------------------------------------- */
166 /* Parse arguments */
167 /* ----------------------------------------------------- */
169 if ((argc < 1) || (!argv[0]))
171 FPUTS(T("Error: Argument array is empty. The program was called incorrectly!\n\n"), stderr);
172 goto clean_up;
175 if (argc < 2)
177 FPRINTF(stderr, T("Error: Nothing to do. Please type '%") T(PRISTR) T(" --help' for details!\n\n"), get_file_name(argv[0U]));
178 goto clean_up;
181 const int slunk_mode = parse_slunk_mode(argv[1U]);
182 switch (slunk_mode)
184 case MODE_HELP:
185 print_manpage(get_file_name(argv[0U]));
186 case MODE_VERS:
187 result = EXIT_SUCCESS;
188 goto clean_up;
189 case MODE_ENCR:
190 case MODE_DECR:
191 break; /*fallthrough*/
192 case MODE_PASS:
193 check_excess_arguments(argc, 3);
194 result = generate_passphrase((argc > 2) ? STRTOUL(argv[2U]) : DFLT_PWDLEN_LENGTH);
195 goto clean_up;
196 case MODE_TEST:
197 check_excess_arguments(argc, 2);
198 result = run_selftest_routine(environ_get_uint(ENV_NTHREADS));
199 goto clean_up;
200 default:
201 FPRINTF(stderr, T("Error: The specified command \"%") T(PRISTR) T("\" is unknown!\n\n"), argv[1U]);
202 goto clean_up;
205 if (argc < 4)
207 FPRINTF(stderr, T("Error: Required argument is missing. Please type '%") T(PRISTR) T(" --help' for details!\n\n"), get_file_name(argv[0U]));
208 goto clean_up;
211 check_excess_arguments(argc, 5);
213 const CHR *const passphrase = PW_FROM_ENV ? GETENV(ENV_PASSWORD) : argv[2U];
214 if ((!passphrase) || (!passphrase[0U]))
216 FPUTS(T("Error: The passphrase must be specified, directly or indirectly!\n\n"), stderr);
217 goto clean_up;
220 if ((!PW_FROM_ENV) && STRICMP(passphrase, T("-")))
222 if ((!STARTS_WITH(passphrase, PREFIX_PASS)) && (!STARTS_WITH(passphrase, PREFIX_FILE)))
224 FPRINTF(stderr, T("Error: The passphrase must start with a '%") T(PRISTR) T("' or '%") T(PRISTR) T("' prefix!\n\n"), PREFIX_PASS, PREFIX_FILE);
225 goto clean_up;
229 input_file = argv[PW_FROM_ENV ? 2U : 3U];
230 if (!input_file[0U])
232 FPUTS(T("Error: The specified input file name must not be empty!\n\n"), stderr);
233 goto clean_up;
236 output_file = argv[PW_FROM_ENV ? 3U : 4U];
237 if (!output_file[0U])
239 FPUTS(T("Error: The specified output file name must not be empty!\n\n"), stderr);
240 goto clean_up;
243 if (same_file(input_file, output_file) > 0)
245 FPUTS(T("Error: The input and output files must not be the same!\n\n"), stderr);
246 goto clean_up;
249 /* ----------------------------------------------------- */
250 /* Initialize passphrase */
251 /* ----------------------------------------------------- */
253 if (!(passphrase_buffer = PW_FROM_ENV ? copy_passphrase(passphrase) :
254 (STARTS_WITH(passphrase, PREFIX_PASS) ? copy_passphrase(passphrase + STRLEN(PREFIX_PASS)) :
255 (STARTS_WITH(passphrase, PREFIX_FILE) ? read_passphrase(passphrase + STRLEN(PREFIX_FILE)) : read_passphrase(T("-"))))))
257 goto clean_up;
260 slunkcrypt_bzero((CHR*)passphrase, STRLEN(passphrase) * sizeof(CHR));
262 const size_t passphrase_len = strlen(passphrase_buffer);
263 if (passphrase_len < SLUNKCRYPT_PWDLEN_MIN)
265 FPRINTF(stderr, T("Error: Passphrase must be at least %u characters in length!\n\n"), (unsigned)SLUNKCRYPT_PWDLEN_MIN);
266 goto clean_up;
268 else if (passphrase_len > SLUNKCRYPT_PWDLEN_MAX)
270 FPRINTF(stderr, T("Error: Passphrase must be at most %u characters in length!\n\n"), (unsigned)SLUNKCRYPT_PWDLEN_MAX);
271 goto clean_up;
274 if (slunk_mode == MODE_ENCR)
276 if (passphrase_len < RCMD_PWDLEN_LENGTH)
278 FPRINTF(stderr, T("Warning: Using a *short* passphrase; a length of %u characters or more is recommended!\n\n"), (unsigned)RCMD_PWDLEN_LENGTH);
280 else if (weak_passphrase(passphrase_buffer))
282 FPUTS(T("Warning: Using a *weak* passphrase; a mix of upper-case letters, lower-case letters, digits and other characters is recommended!\n\n"), stderr);
286 fflush(stderr);
288 /* ----------------------------------------------------- */
289 /* Encrypt or decrypt */
290 /* ----------------------------------------------------- */
292 const uint64_t clk_start = clock_read();
293 const crypt_options_t options = { environ_get_flag(ENV_KEEPFILE), environ_get_uint(ENV_NTHREADS) };
295 switch (slunk_mode)
297 case MODE_ENCR:
298 result = encrypt(passphrase_buffer, input_file, output_file, &options);
299 break;
300 case MODE_DECR:
301 result = decrypt(passphrase_buffer, input_file, output_file, &options);
302 break;
303 default:
304 FPUTS(T("Unexpected mode encountered!\n\n"), stderr);
307 if (!g_slunkcrypt_abort_flag)
309 FPRINTF(stderr, T("--------\n\nOperation completed after %.1f seconds.\n\n"), (clock_read() - clk_start) / ((double)clock_freq()));
312 /* ----------------------------------------------------- */
313 /* Final clean-up */
314 /* ----------------------------------------------------- */
316 clean_up:
318 fflush(stderr);
320 if (passphrase_buffer)
322 slunkcrypt_bzero(passphrase_buffer, strlen(passphrase_buffer));
323 free(passphrase_buffer);
326 return result;
329 #if defined(_WIN32) && defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
330 void __wgetmainargs(int*, wchar_t***, wchar_t***, int, int*);
331 int main()
333 wchar_t** enpv, ** argv;
334 int argc, si = 0;
335 __wgetmainargs(&argc, &argv, &enpv, 1, &si);
336 return wmain(argc, argv);
338 #endif