1 /******************************************************************************/
2 /* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de> */
3 /* This work has been released under the CC0 1.0 Universal license! */
4 /******************************************************************************/
7 # define _CRT_SECURE_NO_WARNINGS 1
14 #include <slunkcrypt.h>
25 // ==========================================================================
27 // ==========================================================================
29 static const uint64_t MAGIC_NUMBER = 0x243F6A8885A308D3ull;
31 #define BUFFER_SIZE 65536U
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, T("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));
46 if (!(*file_out = FOPEN(output_path, T("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));
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);
58 static void init_slunk_param(slunkparam_t *const param, const crypt_options_t *const options)
60 slunkcrypt_bzero(param, sizeof(slunkparam_t));
61 param->version = SLUNKCRYPT_PARAM_VERSION;
62 param->thread_count = options->thread_count;
65 #define UPDATE_PROGRESS_INDICATOR(CLK_UPDATE, CURRENT, TOTAL) do \
67 const uint64_t clk_now = clock_read(); \
68 if ((clk_now < (CLK_UPDATE)) || (clk_now - (CLK_UPDATE) > update_interval)) \
70 FPRINTF(stderr, T("\b\b\b\b\b\b\b%5.1f%% "), ((CURRENT) / ((double)(TOTAL))) * 100.0); \
72 CLK_UPDATE = clk_now; \
77 // ==========================================================================
79 // ==========================================================================
81 int encrypt(const char *const passphrase, const CHR *const input_path, const CHR *const output_path, const crypt_options_t *const options)
83 slunkcrypt_t ctx = SLUNKCRYPT_NULL;
85 FILE* file_in = NULL, * file_out = NULL;
86 int result = EXIT_FAILURE, status;
88 uint8_t *buffer = malloc(BUFFER_SIZE * sizeof(uint8_t));
91 FPUTS(T("Error: Failed to allocate the I/O buffer!\n\n"), stderr);
95 if (open_files(&file_in, &file_out, input_path, output_path) != EXIT_SUCCESS)
100 const uint64_t file_size = get_size(file_in);
101 if (file_size == UINT64_MAX)
103 FPUTS(T("I/O error: Failed to determine size of input file!\n\n"), stderr);
106 else if (file_size < 1U)
108 FPUTS(T("Error: Input file is empty or an unsupported type!\n\n"), stderr);
112 FPUTS(T("Encrypting file contents, please be patient... "), stderr);
116 if (slunkcrypt_generate_nonce(&nonce) != SLUNKCRYPT_SUCCESS)
118 FPUTS(T("\n\nSlunkCrypt error: Failed to generate nonce!\n\n"), stderr);
122 init_slunk_param(¶m, options);
123 ctx = slunkcrypt_alloc_ext(nonce, (const uint8_t*)passphrase, strlen(passphrase), SLUNKCRYPT_ENCRYPT, ¶m);
126 FPUTS(g_slunkcrypt_abort_flag ? T("\n\nProcess interrupted!\n\n") : T("\n\nSlunkCrypt error: Failed to initialize encryption!\n\n"), stderr);
130 if (fwrite_ui64(nonce ^ MAGIC_NUMBER, file_out) < 1U)
132 FPUTS(T("\n\nI/O error: Failed to write nonce value!\n\n"), stderr);
136 uint64_t bytes_read = 0U, clk_update = clock_read();
137 const uint64_t update_interval = (uint64_t)(clock_freq() * 1.414);
139 blake2s_t blake2s_state;
140 blake2s_init(&blake2s_state);
142 FPRINTF(stderr, T("%5.1f%% "), 0.0);
145 while (bytes_read < file_size)
147 const uint64_t bytes_remaining = file_size - bytes_read;
148 const size_t request_len = (bytes_remaining < BUFFER_SIZE) ? ((size_t)bytes_remaining) : BUFFER_SIZE;
149 const size_t count = fread(buffer, sizeof(uint8_t), request_len, file_in);
152 blake2s_update(&blake2s_state, buffer, count);
154 if ((status = slunkcrypt_inplace(ctx, buffer, count)) != SLUNKCRYPT_SUCCESS)
156 FPUTS((status == SLUNKCRYPT_ABORTED) ? T("\n\nProcess interrupted!\n\n") : T("\n\nSlunkCrypt error: Failed to encrypt data!\n\n"), stderr);
159 if (fwrite(buffer, sizeof(uint8_t), count, file_out) < count)
161 FPUTS(T("\n\nI/O error: Failed to write encrypted data!\n\n"), stderr);
165 if (count < request_len)
169 UPDATE_PROGRESS_INDICATOR(clk_update, bytes_read, file_size);
174 FPUTS(T("\n\nI/O error: Failed to read input data!\n\n"), stderr);
178 if (bytes_read != file_size)
180 FPUTS(T("\n\nI/O error: Input file could not be fully read!\n\n"), stderr);
184 const size_t padding = sizeof(uint64_t) - (file_size % sizeof(uint64_t));
185 assert(padding && (padding <= sizeof(uint64_t)));
186 if (slunkcrypt_random_bytes(buffer, padding) < padding)
188 FPUTS(T("\n\nSlunkCrypt error: Failed to generate random data!\n\n"), stderr);
192 SET_LOWBITS(buffer[padding - 1U], padding - 1U);
193 if ((status = slunkcrypt_inplace(ctx, buffer, padding)) != SLUNKCRYPT_SUCCESS)
195 FPUTS((status == SLUNKCRYPT_ABORTED) ? T("\n\nProcess interrupted!\n\n") : T("\n\nSlunkCrypt error: Failed to encrypt data!\n\n"), stderr);
199 if (fwrite(buffer, sizeof(uint8_t), padding, file_out) < padding)
201 FPUTS(T("\n\nI/O error: Failed to write padding data!\n\n"), stderr);
205 uint8_t checksum_buffer[sizeof(uint64_t)];
206 store_ui64(checksum_buffer, blake2s_final(&blake2s_state));
208 if ((status = slunkcrypt_inplace(ctx, checksum_buffer, sizeof(uint64_t))) != SLUNKCRYPT_SUCCESS)
210 FPUTS((status == SLUNKCRYPT_ABORTED) ? T("\n\nProcess interrupted!\n\n") : T("\n\nSlunkCrypt error: Failed to encrypt checksum!\n\n"), stderr);
214 if (fwrite(checksum_buffer, sizeof(uint8_t), sizeof(uint64_t), file_out) < sizeof(uint64_t))
216 FPUTS(T("\n\nI/O error: Failed to write the checksum!\n\n"), stderr);
220 FPRINTF(stderr, T("\b\b\b\b\b\b\b%5.1f%%\n\n"), 100.0);
222 result = EXIT_SUCCESS;
224 FPUTS(T("All is done.\n\n"), stderr);
229 SLUNKCRYPT_SAFE_FREE(ctx);
234 if ((result != EXIT_SUCCESS) && (!options->keep_incomplete))
236 if (REMOVE(output_path))
238 FPUTS(T("Warning: Failed to remove incomplete output file!\n\n"), stderr);
250 slunkcrypt_bzero(buffer, BUFFER_SIZE * sizeof(uint8_t));
254 slunkcrypt_bzero(checksum_buffer, sizeof(uint64_t));
255 slunkcrypt_bzero(&blake2s_state, sizeof(blake2s_t));
256 slunkcrypt_bzero(&nonce, sizeof(uint64_t));
261 // ==========================================================================
263 // ==========================================================================
265 int decrypt(const char *const passphrase, const CHR *const input_path, const CHR *const output_path, const crypt_options_t *const options)
267 slunkcrypt_t ctx = SLUNKCRYPT_NULL;
269 FILE *file_in = NULL, *file_out = NULL;
270 int result = EXIT_FAILURE, status;
272 uint8_t *buffer = malloc(BUFFER_SIZE * sizeof(uint8_t));
275 FPUTS(T("Error: Failed to allocate the I/O buffer!\n\n"), stderr);
279 if (open_files(&file_in, &file_out, input_path, output_path) != EXIT_SUCCESS)
284 const uint64_t file_size = get_size(file_in);
285 if (file_size == UINT64_MAX)
287 FPUTS(T("I/O error: Failed to determine size of input file!\n\n"), stderr);
290 else if (file_size < (3U * sizeof(uint64_t)))
292 FPUTS(T("Error: Input file is too small! Truncated?\n\n"), stderr);
295 else if ((file_size % sizeof(uint64_t)) != 0)
297 FPRINTF(stderr, T("Warning: File size is *not* an integer multiple of %u, ignoring excess bytes!\n\n"), (unsigned)sizeof(uint64_t));
300 FPUTS(T("Decrypting file contents, please be patient... "), stderr);
304 if (fread_ui64(&nonce, file_in) < 1U)
306 FPUTS(T("\n\nI/O error: Failed to read nonce value!\n\n"), stderr);
310 init_slunk_param(¶m, options);
311 ctx = slunkcrypt_alloc_ext(nonce ^ MAGIC_NUMBER, (const uint8_t*)passphrase, strlen(passphrase), SLUNKCRYPT_DECRYPT, ¶m);
314 FPUTS(g_slunkcrypt_abort_flag ? T("\n\nProcess interrupted!\n\n") : T("\n\nSlunkCrypt error: Failed to initialize decryption!\n\n"), stderr);
318 uint64_t bytes_read = sizeof(uint64_t), clk_update = clock_read();
319 const uint64_t update_interval = (uint64_t)(clock_freq() * 1.414);
320 const uint64_t read_limit = round_down(file_size, sizeof(uint64_t)) - (2U * sizeof(uint64_t));
322 blake2s_t blake2s_state;
323 blake2s_init(&blake2s_state);
325 FPRINTF(stderr, T("%5.1f%% "), 0.0);
328 while (bytes_read < read_limit)
330 const uint64_t bytes_remaining = read_limit - bytes_read;
331 const size_t request_len = (bytes_remaining < BUFFER_SIZE) ? ((size_t)bytes_remaining) : BUFFER_SIZE;
332 const size_t count = fread(buffer, sizeof(uint8_t), request_len, file_in);
336 if ((status = slunkcrypt_inplace(ctx, buffer, count)) != SLUNKCRYPT_SUCCESS)
338 FPUTS((status == SLUNKCRYPT_ABORTED) ? T("\n\nProcess interrupted!\n\n") : T("\n\nSlunkCrypt error: Failed to decrypt data!\n\n"), stderr);
341 blake2s_update(&blake2s_state, buffer, count);
342 if (fwrite(buffer, sizeof(uint8_t), count, file_out) < count)
344 FPUTS(T("failed!\n\nI/O error: Failed to write decrypted data!\n\n"), stderr);
348 if (count < request_len)
352 UPDATE_PROGRESS_INDICATOR(clk_update, bytes_read, read_limit);
357 FPUTS(T("\n\nI/O error: Failed to read input data!\n\n"), stderr);
361 if (bytes_read != read_limit)
363 FPUTS(T("\n\nI/O error: Input file could not be fully read!\n\n"), stderr);
367 if (fread(buffer, sizeof(uint8_t), sizeof(uint64_t), file_in) < sizeof(uint64_t))
369 FPUTS(T("\n\nI/O error: Failed to read final block!\n\n"), stderr);
373 if ((status = slunkcrypt_inplace(ctx, buffer, sizeof(uint64_t))) != SLUNKCRYPT_SUCCESS)
375 FPUTS((status == SLUNKCRYPT_ABORTED) ? T("\n\nProcess interrupted!\n\n") : T("\n\nSlunkCrypt error: Failed to decrypt data!\n\n"), stderr);
379 const size_t padding = GET_LOWBITS(buffer[sizeof(uint64_t) - 1U]) + 1U;
380 assert(padding && (padding <= sizeof(uint64_t)));
381 if (padding != sizeof(uint64_t))
383 const size_t count = sizeof(uint64_t) - padding;
384 if (fwrite(buffer, sizeof(uint8_t), count, file_out) < count)
386 FPUTS(T("failed!\n\nI/O error: Failed to write decrypted data!\n\n"), stderr);
389 blake2s_update(&blake2s_state, buffer, count);
392 const uint64_t checksum_actual = blake2s_final(&blake2s_state);
394 uint8_t checksum_buffer[sizeof(uint64_t)];
395 if (fread(checksum_buffer, sizeof(uint8_t), sizeof(uint64_t), file_in) < sizeof(uint64_t))
397 FPUTS(T("\n\nI/O error: Failed to read the checksum!\n\n"), stderr);
401 if ((status = slunkcrypt_inplace(ctx, checksum_buffer, sizeof(uint64_t))) != SLUNKCRYPT_SUCCESS)
403 FPUTS((status == SLUNKCRYPT_ABORTED) ? T("\n\nProcess interrupted!\n\n") : T("\n\nSlunkCrypt error: Failed to decrypt checksum!\n\n"), stderr);
407 FPRINTF(stderr, T("\b\b\b\b\b\b\b%5.1f%%\n\n"), 100.0);
409 const uint64_t checksum_stored = load_ui64(checksum_buffer);
410 if (checksum_actual != checksum_stored)
412 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);
413 FPUTS(T("Wrong passphrase or corrupted file?\n\n"), stderr);
417 result = EXIT_SUCCESS;
419 FPUTS(T("Checksum is correct.\n\n"), stderr);
424 SLUNKCRYPT_SAFE_FREE(ctx);
429 if ((result != EXIT_SUCCESS) && (!options->keep_incomplete))
431 if (REMOVE(output_path))
433 FPUTS(T("Warning: Failed to remove incomplete output file!\n\n"), stderr);
445 slunkcrypt_bzero(buffer, BUFFER_SIZE * sizeof(uint8_t));
449 slunkcrypt_bzero(checksum_buffer, sizeof(uint64_t));
450 slunkcrypt_bzero(&blake2s_state, sizeof(blake2s_t));
451 slunkcrypt_bzero(&nonce, sizeof(uint64_t));
452 slunkcrypt_bzero((void*)&checksum_stored, sizeof(uint64_t));
453 slunkcrypt_bzero((void*)&checksum_actual, sizeof(uint64_t));