Small code clean-up + improved the Makefile + detect GNU/Hurd operating system.
[slunkcrypt.git] / frontend / src / crypt.c
blob13ca107b0d60c90ffb52afff6535b156ade7525e
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 "crypt.h"
14 #include <slunkcrypt.h>
15 #include "utils.h"
16 #include "blake2.h"
18 /* CRT */
19 #include <time.h>
20 #include <inttypes.h>
21 #include <ctype.h>
22 #include <assert.h>
23 #include <errno.h>
25 // ==========================================================================
26 // Constants
27 // ==========================================================================
29 static const uint64_t MAGIC_NUMBER = 0x243F6A8885A308D3ull;
31 #define MAX_BUFFER_SIZE 1048576U // 1 MiB
33 // ==========================================================================
34 // Auxiliary functions
35 // ==========================================================================
37 static int open_files(FILE **const file_in, FILE **const file_out, const CHR *const input_path, const CHR *const output_path)
39 if (!(*file_in = FOPEN(input_path, "rb")))
41 FPRINTF(stderr, T("Error: Failed to open input file \"%") T(PRISTR) T("\" for reading!\n\n%") T(PRISTR) T("\n\n"), input_path, STRERROR(errno));
42 *file_out = NULL;
43 return EXIT_FAILURE;
46 if (!(*file_out = FOPEN(output_path, "wb")))
48 FPRINTF(stderr, T("Error: Failed to open output file \"%") T(PRISTR) T("\" for writing!\n\n%") T(PRISTR) T("\n\n"), output_path, STRERROR(errno));
49 return EXIT_FAILURE;
52 //setvbuf(*file_in, NULL, _IOFBF, (((8U * BUFFER_SIZE) + (BUFSIZ - 1U)) / BUFSIZ) * BUFSIZ);
53 //setvbuf(*file_out, NULL, _IOFBF, (((8U * BUFFER_SIZE) + (BUFSIZ - 1U)) / BUFSIZ) * BUFSIZ);
55 return EXIT_SUCCESS;
58 static size_t get_buffer_size(const uint64_t file_size)
60 const uint64_t upper_limit = file_size / 101U;
61 size_t pwr2, result = 0U;
62 for (pwr2 = 1U; (pwr2 <= MAX_BUFFER_SIZE) && (pwr2 <= upper_limit); pwr2 <<= 1)
64 result = pwr2;
66 return BOUND(BUFSIZ, result, MAX_BUFFER_SIZE);
69 static void init_slunk_param(slunkparam_t *const param, const crypt_options_t *const options)
71 slunkcrypt_bzero(param, sizeof(slunkparam_t));
72 param->version = SLUNKCRYPT_PARAM_VERSION;
73 param->thread_count = options->thread_count;
76 #define UPDATE_PROGRESS_INDICATOR(CLK_UPDATE, CURRENT, TOTAL) do \
77 { \
78 const uint64_t clk_now = clock_read(); \
79 if ((clk_now < (CLK_UPDATE)) || (clk_now - (CLK_UPDATE) > update_interval)) \
80 { \
81 FPRINTF(stderr, T("\b\b\b\b\b\b\b%5.1f%% "), ((CURRENT) / ((double)(TOTAL))) * 100.0); \
82 fflush(stderr); \
83 CLK_UPDATE = clk_now; \
84 } \
85 } \
86 while(0)
88 // ==========================================================================
89 // Encrypt
90 // ==========================================================================
92 int encrypt(const char *const passphrase, const CHR *const input_path, const CHR *const output_path, const crypt_options_t *const options)
94 slunkcrypt_t ctx = SLUNKCRYPT_NULL;
95 slunkparam_t param;
96 FILE *file_in = NULL, *file_out = NULL;
97 uint8_t *buffer = NULL;
98 size_t buffer_size = BUFSIZ;
99 int result = EXIT_FAILURE, status = -1;
101 if (open_files(&file_in, &file_out, input_path, output_path) != EXIT_SUCCESS)
103 goto clean_up;
106 const uint64_t file_size = get_size(file_in);
107 if (file_size == UINT64_MAX)
109 FPUTS(T("I/O error: Failed to determine size of input file!\n\n"), stderr);
110 goto clean_up;
112 else if (file_size < 1U)
114 FPUTS(T("Error: Input file is empty or an unsupported type!\n\n"), stderr);
115 goto clean_up;
118 if (!(buffer = malloc((buffer_size = get_buffer_size(file_size)) * sizeof(uint8_t))))
120 FPUTS(T("Error: Failed to allocate the I/O buffer!\n\n"), stderr);
121 goto clean_up;
124 FPUTS(T("Encrypting file contents, please be patient... "), stderr);
125 fflush(stderr);
127 uint64_t nonce;
128 if (slunkcrypt_generate_nonce(&nonce) != SLUNKCRYPT_SUCCESS)
130 FPUTS(T("\n\nSlunkCrypt error: Failed to generate nonce!\n\n"), stderr);
131 goto clean_up;
134 init_slunk_param(&param, options);
135 ctx = slunkcrypt_alloc_ext(nonce, (const uint8_t*)passphrase, strlen(passphrase), SLUNKCRYPT_ENCRYPT, &param);
136 if (!ctx)
138 FPUTS(g_slunkcrypt_abort_flag ? T("\n\nProcess interrupted!\n\n") : T("\n\nSlunkCrypt error: Failed to initialize encryption!\n\n"), stderr);
139 goto clean_up;
142 if (fwrite_ui64(nonce ^ MAGIC_NUMBER, file_out) < 1U)
144 FPUTS(T("\n\nI/O error: Failed to write nonce value!\n\n"), stderr);
145 goto clean_up;
148 uint64_t bytes_read = 0U, clk_update = clock_read();
149 const uint64_t update_interval = (uint64_t)(clock_freq() * 1.414);
151 blake2s_t blake2s_state;
152 blake2s_init(&blake2s_state);
154 FPRINTF(stderr, T("%5.1f%% "), 0.0);
155 fflush(stderr);
157 while (bytes_read < file_size)
159 const uint64_t bytes_remaining = file_size - bytes_read;
160 const size_t request_len = (bytes_remaining < buffer_size) ? ((size_t)bytes_remaining) : buffer_size;
161 const size_t count = fread(buffer, sizeof(uint8_t), request_len, file_in);
162 if (count > 0U)
164 blake2s_update(&blake2s_state, buffer, count);
165 bytes_read += count;
166 if ((status = slunkcrypt_inplace(ctx, buffer, count)) != SLUNKCRYPT_SUCCESS)
168 FPUTS((status == SLUNKCRYPT_ABORTED) ? T("\n\nProcess interrupted!\n\n") : T("\n\nSlunkCrypt error: Failed to encrypt data!\n\n"), stderr);
169 goto clean_up;
171 if (fwrite(buffer, sizeof(uint8_t), count, file_out) < count)
173 FPUTS(T("\n\nI/O error: Failed to write encrypted data!\n\n"), stderr);
174 goto clean_up;
177 if (count < request_len)
179 break; /*EOF*/
181 UPDATE_PROGRESS_INDICATOR(clk_update, bytes_read, file_size);
184 if (ferror(file_in))
186 FPUTS(T("\n\nI/O error: Failed to read input data!\n\n"), stderr);
187 goto clean_up;
190 if (bytes_read != file_size)
192 FPUTS(T("\n\nI/O error: Input file could not be fully read!\n\n"), stderr);
193 goto clean_up;
196 const size_t padding = sizeof(uint64_t) - (file_size % sizeof(uint64_t));
197 assert(padding && (padding <= sizeof(uint64_t)));
198 if (slunkcrypt_random_bytes(buffer, padding) < padding)
200 FPUTS(T("\n\nSlunkCrypt error: Failed to generate random data!\n\n"), stderr);
201 goto clean_up;
204 SET_LOWBITS(buffer[padding - 1U], padding - 1U);
205 if ((status = slunkcrypt_inplace(ctx, buffer, padding)) != SLUNKCRYPT_SUCCESS)
207 FPUTS((status == SLUNKCRYPT_ABORTED) ? T("\n\nProcess interrupted!\n\n") : T("\n\nSlunkCrypt error: Failed to encrypt data!\n\n"), stderr);
208 goto clean_up;
211 if (fwrite(buffer, sizeof(uint8_t), padding, file_out) < padding)
213 FPUTS(T("\n\nI/O error: Failed to write padding data!\n\n"), stderr);
214 goto clean_up;
217 uint8_t checksum_buffer[sizeof(uint64_t)];
218 store_ui64(checksum_buffer, blake2s_final(&blake2s_state));
220 if ((status = slunkcrypt_inplace(ctx, checksum_buffer, sizeof(uint64_t))) != SLUNKCRYPT_SUCCESS)
222 FPUTS((status == SLUNKCRYPT_ABORTED) ? T("\n\nProcess interrupted!\n\n") : T("\n\nSlunkCrypt error: Failed to encrypt checksum!\n\n"), stderr);
223 goto clean_up;
226 if (fwrite(checksum_buffer, sizeof(uint8_t), sizeof(uint64_t), file_out) < sizeof(uint64_t))
228 FPUTS(T("\n\nI/O error: Failed to write the checksum!\n\n"), stderr);
229 goto clean_up;
232 FPRINTF(stderr, T("\b\b\b\b\b\b\b%5.1f%%\n\n"), 100.0);
234 result = EXIT_SUCCESS;
236 FPUTS(T("All is done.\n\n"), stderr);
237 fflush(stderr);
239 clean_up:
241 SLUNKCRYPT_SAFE_FREE(ctx);
243 if (file_out)
245 fclose(file_out);
246 if ((result != EXIT_SUCCESS) && (!options->keep_incomplete))
248 if (REMOVE(output_path))
250 FPUTS(T("Warning: Failed to remove incomplete output file!\n\n"), stderr);
255 if (file_in)
257 fclose(file_in);
260 if (buffer)
262 slunkcrypt_bzero(buffer, buffer_size * sizeof(uint8_t));
263 free(buffer);
266 slunkcrypt_bzero(checksum_buffer, sizeof(uint64_t));
267 slunkcrypt_bzero(&blake2s_state, sizeof(blake2s_t));
268 slunkcrypt_bzero(&nonce, sizeof(uint64_t));
270 return result;
273 // ==========================================================================
274 // Decrypt
275 // ==========================================================================
277 int decrypt(const char *const passphrase, const CHR *const input_path, const CHR *const output_path, const crypt_options_t *const options)
279 slunkcrypt_t ctx = SLUNKCRYPT_NULL;
280 slunkparam_t param;
281 FILE *file_in = NULL, *file_out = NULL;
282 uint8_t *buffer = NULL;
283 size_t buffer_size = BUFSIZ;
284 int result = EXIT_FAILURE, status = -1;
286 if (open_files(&file_in, &file_out, input_path, output_path) != EXIT_SUCCESS)
288 goto clean_up;
291 const uint64_t file_size = get_size(file_in);
292 if (file_size == UINT64_MAX)
294 FPUTS(T("I/O error: Failed to determine size of input file!\n\n"), stderr);
295 goto clean_up;
297 else if (file_size < (3U * sizeof(uint64_t)))
299 FPUTS(T("Error: Input file is too small! Truncated?\n\n"), stderr);
300 goto clean_up;
302 else if ((file_size % sizeof(uint64_t)) != 0)
304 FPRINTF(stderr, T("Warning: File size is *not* an integer multiple of %u, ignoring excess bytes!\n\n"), (unsigned)sizeof(uint64_t));
307 if (!(buffer = malloc((buffer_size = get_buffer_size(file_size)) * sizeof(uint8_t))))
309 FPUTS(T("Error: Failed to allocate the I/O buffer!\n\n"), stderr);
310 goto clean_up;
313 FPUTS(T("Decrypting file contents, please be patient... "), stderr);
314 fflush(stderr);
316 uint64_t nonce;
317 if (fread_ui64(&nonce, file_in) < 1U)
319 FPUTS(T("\n\nI/O error: Failed to read nonce value!\n\n"), stderr);
320 goto clean_up;
323 init_slunk_param(&param, options);
324 ctx = slunkcrypt_alloc_ext(nonce ^ MAGIC_NUMBER, (const uint8_t*)passphrase, strlen(passphrase), SLUNKCRYPT_DECRYPT, &param);
325 if (!ctx)
327 FPUTS(g_slunkcrypt_abort_flag ? T("\n\nProcess interrupted!\n\n") : T("\n\nSlunkCrypt error: Failed to initialize decryption!\n\n"), stderr);
328 goto clean_up;
331 uint64_t bytes_read = sizeof(uint64_t), clk_update = clock_read();
332 const uint64_t update_interval = (uint64_t)(clock_freq() * 1.414);
333 const uint64_t read_limit = round_down(file_size, sizeof(uint64_t)) - (2U * sizeof(uint64_t));
335 blake2s_t blake2s_state;
336 blake2s_init(&blake2s_state);
338 FPRINTF(stderr, T("%5.1f%% "), 0.0);
339 fflush(stderr);
341 while (bytes_read < read_limit)
343 const uint64_t bytes_remaining = read_limit - bytes_read;
344 const size_t request_len = (bytes_remaining < buffer_size) ? ((size_t)bytes_remaining) : buffer_size;
345 const size_t count = fread(buffer, sizeof(uint8_t), request_len, file_in);
346 if (count > 0U)
348 bytes_read += count;
349 if ((status = slunkcrypt_inplace(ctx, buffer, count)) != SLUNKCRYPT_SUCCESS)
351 FPUTS((status == SLUNKCRYPT_ABORTED) ? T("\n\nProcess interrupted!\n\n") : T("\n\nSlunkCrypt error: Failed to decrypt data!\n\n"), stderr);
352 goto clean_up;
354 blake2s_update(&blake2s_state, buffer, count);
355 if (fwrite(buffer, sizeof(uint8_t), count, file_out) < count)
357 FPUTS(T("failed!\n\nI/O error: Failed to write decrypted data!\n\n"), stderr);
358 goto clean_up;
361 if (count < request_len)
363 break; /*EOF*/
365 UPDATE_PROGRESS_INDICATOR(clk_update, bytes_read, read_limit);
368 if (ferror(file_in))
370 FPUTS(T("\n\nI/O error: Failed to read input data!\n\n"), stderr);
371 goto clean_up;
374 if (bytes_read != read_limit)
376 FPUTS(T("\n\nI/O error: Input file could not be fully read!\n\n"), stderr);
377 goto clean_up;
380 if (fread(buffer, sizeof(uint8_t), sizeof(uint64_t), file_in) < sizeof(uint64_t))
382 FPUTS(T("\n\nI/O error: Failed to read final block!\n\n"), stderr);
383 goto clean_up;
386 if ((status = slunkcrypt_inplace(ctx, buffer, sizeof(uint64_t))) != SLUNKCRYPT_SUCCESS)
388 FPUTS((status == SLUNKCRYPT_ABORTED) ? T("\n\nProcess interrupted!\n\n") : T("\n\nSlunkCrypt error: Failed to decrypt data!\n\n"), stderr);
389 goto clean_up;
392 const size_t padding = GET_LOWBITS(buffer[sizeof(uint64_t) - 1U]) + 1U;
393 assert(padding && (padding <= sizeof(uint64_t)));
394 if (padding != sizeof(uint64_t))
396 const size_t count = sizeof(uint64_t) - padding;
397 if (fwrite(buffer, sizeof(uint8_t), count, file_out) < count)
399 FPUTS(T("failed!\n\nI/O error: Failed to write decrypted data!\n\n"), stderr);
400 goto clean_up;
402 blake2s_update(&blake2s_state, buffer, count);
405 const uint64_t checksum_actual = blake2s_final(&blake2s_state);
407 uint8_t checksum_buffer[sizeof(uint64_t)];
408 if (fread(checksum_buffer, sizeof(uint8_t), sizeof(uint64_t), file_in) < sizeof(uint64_t))
410 FPUTS(T("\n\nI/O error: Failed to read the checksum!\n\n"), stderr);
411 goto clean_up;
414 if ((status = slunkcrypt_inplace(ctx, checksum_buffer, sizeof(uint64_t))) != SLUNKCRYPT_SUCCESS)
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;
420 FPRINTF(stderr, T("\b\b\b\b\b\b\b%5.1f%%\n\n"), 100.0);
422 const uint64_t checksum_stored = load_ui64(checksum_buffer);
423 if (checksum_actual != checksum_stored)
425 FPRINTF(stderr, T("Error: Checksum mismatch detected! [expected: 0x%016") T(PRIX64) T(", actual: 0x%016") T(PRIX64) T("]\n\n"), checksum_stored, checksum_actual);
426 FPUTS(T("Wrong passphrase or corrupted file?\n\n"), stderr);
427 goto clean_up;
430 result = EXIT_SUCCESS;
432 FPUTS(T("Checksum is correct.\n\n"), stderr);
433 fflush(stderr);
435 clean_up:
437 SLUNKCRYPT_SAFE_FREE(ctx);
439 if (file_out)
441 fclose(file_out);
442 if ((result != EXIT_SUCCESS) && (!options->keep_incomplete))
444 if (REMOVE(output_path))
446 FPUTS(T("Warning: Failed to remove incomplete output file!\n\n"), stderr);
451 if (file_in)
453 fclose(file_in);
456 if (buffer)
458 slunkcrypt_bzero(buffer, buffer_size * sizeof(uint8_t));
459 free(buffer);
462 slunkcrypt_bzero(checksum_buffer, sizeof(uint64_t));
463 slunkcrypt_bzero(&blake2s_state, sizeof(blake2s_t));
464 slunkcrypt_bzero(&nonce, sizeof(uint64_t));
465 slunkcrypt_bzero((void*)&checksum_stored, sizeof(uint64_t));
466 slunkcrypt_bzero((void*)&checksum_actual, sizeof(uint64_t));
468 return result;