summaryrefslogtreecommitdiff
path: root/src/encryption.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/encryption.c')
-rw-r--r--src/encryption.c544
1 files changed, 295 insertions, 249 deletions
diff --git a/src/encryption.c b/src/encryption.c
index 65b8843..382f6d5 100644
--- a/src/encryption.c
+++ b/src/encryption.c
@@ -1,12 +1,12 @@
1/** 1/**
2 * @file encryption.c 2 * @file encryption.c
3 * @author syxhe (https://t.me/syxhe) 3 * @author syxhe (https://t.me/syxhe)
4 * @brief *Implementing `encryption.h`* 4 * @brief A collection of all encryption related functions
5 * @version 0.1 5 * @version 0.1
6 * @date 2025-06-09 6 * @date 2025-06-09
7 * 7 *
8 * @copyright Copyright (c) 2025 8 * @copyright Copyright (c) 2025
9 * 9 *
10 */ 10 */
11 11
12// TODO: Go back and make sure every function has proper error handling 12// TODO: Go back and make sure every function has proper error handling
@@ -14,11 +14,16 @@
14// I need to make sure every single function in this file returns with an indicated error instead of nuking the whole program with 14// I need to make sure every single function in this file returns with an indicated error instead of nuking the whole program with
15// error() 15// error()
16 16
17#ifndef __VXGG_REWRITE___ENCRYPTION_C___1481879318188___
18#define __VXGG_REWRITE___ENCRYPTION_C___1481879318188___
19
20
17#define _GNU_SOURCE 21#define _GNU_SOURCE
18 22
19#include "shared.h" 23#define TPSIZE (1<<13)
20#include "encryption.h" 24
21#include "threadpool.h" 25#include "shared.c"
26#include "threadpool.c"
22 27
23#include <sodium.h> 28#include <sodium.h>
24 29
@@ -33,16 +38,91 @@
33#include <fcntl.h> 38#include <fcntl.h>
34#include <stdio.h> 39#include <stdio.h>
35 40
41/// Determines whether any function that calls libsodium functions also checks to make sure libsodium is actually initialized. May
42/// cause unexpected issues with early exiting due to libsodium failing to initialize properly. It's recommended that you just
43/// manually run `sodium_init()` in some main or init function of your own so that you can deal with a potential error yourself
44#define ___VXGG___ALWAYS_CHECK_LIBSODIUM___ 1
45
46/// Grants access to the `vxgg_setsodiumfailcb` function, which can be used to set a custom callback for what to do when libsodium
47/// fails upon initialization
48#define ___VXGG___USE_CLS_CALLBACK___ 1
49
50/// Chunk size for en/decryption. I originally wanted to use st_blksize from stat(), but given that those chunks may be of different
51/// sizes between computers / filesystems / architectures / files, it's easier to just have this be a consistent macro
52#define CHUNKSIZE (1 << 9)
53
54// Fuck reading from a file. Even if someone ran strings on the binary and got this they wouldn't be able to regenerate the key
55//! A list of possible words for password creation
56#define PASSWORD_WORDS (\
57 (const char * const []){\
58 "the", "of", "to", "and", "for", "our", "their", "has", "in", "he", "a", "them", "that", "these", "by", "have", "we", \
59 "us", "people", "which", "all", "is", "with", "laws", "be", "are", "his", "states", "on", "they", "right", "it", "from", \
60 "government", "such", "among", "powers", "most", "an", "time", "should", "new", "as", "been", "colonies", "assent", \
61 "large", "at", "independent", "free", "united", "when", "mankind", "hold", "rights", "governments", "consent", "its", \
62 "long", "themselves", "abolishing", "usurpations", "absolute", "repeated", "this", "world", "refused", "pass", "other", \
63 "others", "without", "justice", "peace", "power", "seas", "war", "do", "declaration", "america", "becomes", "necessary", \
64 "political", "equal", "declare", "causes", "separation", "men", "happiness", "any", "form", "alter", "or", "will", \
65 "forms", "same", "object", "off", "necessity", "history", "great", "britain", "tyranny", "over", "public", "good", \
66 "unless", "suspended", "so", "would", "legislature", "only", "legislative", "bodies", "purpose", "into", "dissolved", \
67 "state", "endeavoured", "refusing", "hither", "conditions", "establishing", "offices", "out", "armies", "legislatures", \
68 "render", "jurisdiction", "foreign", "acts", "pretended", "trial", "inhabitants", "cases", "transporting", "rule", \
69 "declaring", "here", "protection", "against", "lives", "circumstances", "ages", "totally", "friends", "brethren", "whose", \
70 "every", "may", "therefore", "ought", "unanimous", "thirteen", "course", "human", "events", "one", "dissolve", "bands", \
71 "connected", "another", "assume", "earth", "separate", "station", "nature", "natures", "god", "entitle", "decent", \
72 "respect", "opinions", "requires", "impel", "truths", "self", "evident", "created", "endowed", "creator", "certain", \
73 "unalienable", "life", "liberty", "pursuit", "secure", "instituted", "deriving", "just", "governed", "whenever", \
74 "destructive", "ends", "abolish", "institute", "laying", "foundation", "principles", "organizing", "shall", "seem", \
75 "likely", "effect", "safety", "prudence", "indeed", "dictate", "established", "not", "changed", "light", "transient", \
76 "accordingly", "experience", "hath", "shewn", "more", "disposed", "suffer", "while", "evils", "sufferable", "than", \
77 "accustomed", "but", "train", "abuses", "pursuing", "invariably", "evinces", "design", "reduce", "under", "despotism", \
78 "duty", "throw", "provide", "guards", "future", "security", "patient", "sufferance", "now", "constrains", "former", \
79 "systems", "present", "king", "injuries", "having", "direct", "establishment", "prove", "let", "facts", "submitted", \
80 "candid", "wholesome", "forbidden", "governors", "immediate", "pressing", "importance", "operation", "till", "obtained", \
81 "utterly", "neglected", "attend", "accommodation", "districts", "those", "relinquish", "representation", "inestimable", \
82 "formidable", "tyrants", "called", "together", "places", "unusual", "uncomfortable", "distant", "depository", "records", \
83 "sole", "fatiguing", "compliance", "measures", "representative", "houses", "repeatedly", "opposing", "manly", "firmness", \
84 "invasions", "after", "dissolutions", "cause", "elected", "whereby", "incapable", "annihilation", "returned", "exercise", \
85 "remaining", "mean", "exposed", "dangers", "invasion", "convulsions", "within", "prevent", "population", "obstructing", \
86 "naturalization", "foreigners", "encourage", "migrations", "raising", "appropriations", "lands", "obstructed", \
87 "administration", "judiciary", "made", "judges", "dependent", "alone", "tenure", "amount", "payment", "salaries", \
88 "erected", "multitude", "sent", "swarms", "officers", "harrass", "eat", "substance", "kept", "times", "standing", \
89 "affected", "military", "superior", "civil", "combined", "subject", "constitution", "unacknowledged", "giving", \
90 "legislation", "quartering", "armed", "troops", "protecting", "mock", "punishment", "murders", "commit", "cutting", \
91 "trade", "parts", "imposing", "taxes", "depriving", "many", "benefits", "jury", "beyond", "tried", "offences", "system", \
92 "english", "neighbouring", "province", "therein", "arbitrary", "enlarging", "boundaries", "once", "example", "fit", \
93 "instrument", "introducing", "taking", "away", "charters", "valuable", "altering", "fundamentally", "suspending", "own", \
94 "invested", "legislate", "whatsoever", "abdicated", "waging", "plundered", "ravaged", "coasts", "burnt", "towns", \
95 "destroyed", "mercenaries", "compleat", "works", "death", "desolation", "already", "begun", "cruelty", "perfidy", \
96 "scarcely", "paralleled", "barbarous", "unworthy", "head", "civilized", "nation", "constrained", "fellow", "citizens", \
97 "taken", "captive", "high", "bear", "arms", "country", "become", "executioners", "fall", "hands", "excited", "domestic", \
98 "insurrections", "amongst", "bring", "frontiers", "merciless", "indian", "savages", "known", "warfare", "undistinguished", \
99 "destruction", "sexes", "stage", "oppressions", "petitioned", "redress", "humble", "terms", "petitions", "answered", \
100 "injury", "prince", "character", "thus", "marked", "act", "define", "tyrant", "unfit", "ruler", "nor", "wanting", \
101 "attentions", "brittish", "warned", "attempts", "extend", "unwarrantable", "reminded", "emigration", "settlement", \
102 "appealed", "native", "magnanimity", "conjured", "ties", "common", "kindred", "disavow", "inevitably", "interrupt", \
103 "connections", "correspondence", "too", "deaf", "voice", "consanguinity", "must", "acquiesce", "denounces", "rest", \
104 "enemies", "representatives", "general", "congress", "assembled", "appealing", "supreme", "judge", "rectitude", \
105 "intentions", "name", "authority", "solemnly", "publish", "absolved", "allegiance", "british", "crown", "connection", \
106 "between", "full", "levy", "conclude", "contract", "alliances", "establish", "commerce", "things", "support", "firm", \
107 "reliance", "divine", "providence", "mutually", "pledge", "each", "fortunes", "sacred", "honor"\
108 }\
109)
110//! Short macro for getting the `PASSWORD_WORDS` array size
111#define PASSWORD_WORDS_LEN (STATIC_ARRAY_LEN(PASSWORD_WORDS))
112
36#if ___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0 113#if ___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0
37#if ___VXGG___USE_CLS_CALLBACK___ > 0 114#if ___VXGG___USE_CLS_CALLBACK___ > 0
38 115
116//! Definition for the callback function that fires when a call to checksodium fails
117typedef void (*vxgg_naclfailcb)(void*);
118
39/** 119/**
40 * @brief Default sodium init fail callback for use in `checksodiumcb()` 120 * @brief Default sodium init fail callback for use in `checksodiumcb()`
41 * 121 *
42 * @param none Unused param to fit callback spec 122 * @param none Unused param to fit callback spec
43 */ 123 */
44static void naclfaildefault(void *none) { 124static void naclfaildefault(void *none) {
45 none = none; // Makes gcc happy 125 none = none; // Makes gcc happy
46 if(___VXGG___VERBOSE_ERRORS___) 126 if(___VXGG___VERBOSE_ERRORS___)
47 error(1, ENOTSUP, "<naclfaildefault> Couldn't initialize sodium for some reason. Quitting..."); 127 error(1, ENOTSUP, "<naclfaildefault> Couldn't initialize sodium for some reason. Quitting...");
48 exit(EXIT_FAILURE); 128 exit(EXIT_FAILURE);
@@ -50,7 +130,7 @@ static void naclfaildefault(void *none) {
50 130
51/** 131/**
52 * @brief Internal function to deal with the `___VXGG___USE_CLS_CALLBACK___` macro 132 * @brief Internal function to deal with the `___VXGG___USE_CLS_CALLBACK___` macro
53 * 133 *
54 * `checksodiumcb()` runs the sodium function `sodium_init()` and on error calls the provided callback with the provided data. The 134 * `checksodiumcb()` runs the sodium function `sodium_init()` and on error calls the provided callback with the provided data. The
55 * callback and data default to `naclfaildefault` and `NULL`, but can be changed when the `set` parameter is non-zero. When `set` 135 * callback and data default to `naclfaildefault` and `NULL`, but can be changed when the `set` parameter is non-zero. When `set`
56 * is zero, the sodium init check is preformed 136 * is zero, the sodium init check is preformed
@@ -59,11 +139,11 @@ static void naclfaildefault(void *none) {
59 * macros are both greater than 0 when compiling, and 2: a function in `encryption.c` calls a function originating from sodium. This 139 * macros are both greater than 0 when compiling, and 2: a function in `encryption.c` calls a function originating from sodium. This
60 * function exists as a way to deal with sodium failing yourself, instead of instantly calling `exit()`. If you don't care to handle 140 * function exists as a way to deal with sodium failing yourself, instead of instantly calling `exit()`. If you don't care to handle
61 * it, or are initializing sodium yourself, this is unnecessary 141 * it, or are initializing sodium yourself, this is unnecessary
62 * 142 *
63 * @param callback A callback to be ran when sodium fails to initialize itself. Ignored if `set` is zero. Must be non-null when `set` is non-zero 143 * @param callback A callback to be ran when sodium fails to initialize itself. Ignored if `set` is zero. Must be non-null when `set` is non-zero
64 * @param data Data to be passed to the callback when it is fired. Ignored if `set` is zero. May be null 144 * @param data Data to be passed to the callback when it is fired. Ignored if `set` is zero. May be null
65 * @param set Flag on whether to check sodium or to set a new callback and data pair. Checks sodium when zero, sets callback & data when non-zero 145 * @param set Flag on whether to check sodium or to set a new callback and data pair. Checks sodium when zero, sets callback & data when non-zero
66 * @retval int 146 * @retval int
67 */ 147 */
68static int checksodiumcb(vxgg_naclfailcb const callback, void *data, unsigned char set) { 148static int checksodiumcb(vxgg_naclfailcb const callback, void *data, unsigned char set) {
69 static vxgg_naclfailcb cb = naclfaildefault; 149 static vxgg_naclfailcb cb = naclfaildefault;
@@ -97,9 +177,9 @@ void vxgg_setsodiumfailcb(vxgg_naclfailcb cb, void *data) {
97 * @brief Simple function to check if sodium has been properly initialized 177 * @brief Simple function to check if sodium has been properly initialized
98 * 178 *
99 * `checksodium()` will run in functions located in `encryption.h` only when the macro `___VXGG___ALWAYS_CHECK_LIBSODIUM___` is greater 179 * `checksodium()` will run in functions located in `encryption.h` only when the macro `___VXGG___ALWAYS_CHECK_LIBSODIUM___` is greater
100 * than zero when compiling. It will call the `checksodiumcb()` function if compiled with the `___VXGG___USE_CLS_CALLBACK___` macro. 180 * than zero when compiling. It will call the `checksodiumcb()` function if compiled with the `___VXGG___USE_CLS_CALLBACK___` macro.
101 * When called, checksodium will run `sodium_init()`, and will either run the user-defined callback or `XALLOC_EXIT`. 181 * When called, checksodium will run `sodium_init()`, and will either run the user-defined callback or `XALLOC_EXIT`.
102 * 182 *
103 */ 183 */
104static void checksodium(void) { 184static void checksodium(void) {
105 #if ___VXGG___USE_CLS_CALLBACK___ > 0 185 #if ___VXGG___USE_CLS_CALLBACK___ > 0
@@ -112,17 +192,30 @@ static void checksodium(void) {
112 } 192 }
113 193
114 #endif 194 #endif
115 195
116 return; 196 return;
117} 197}
118 198
119#endif 199#endif
120 200
201/**
202 * @brief open() with the flags O_TMPFILE, O_WRONLY, O_CLOEXEC, and O_SYNC. Opened with mode S_IRUSR, S_IWUSR
203 *
204 * @param dest The filename the new descriptor should have. Must be non-null
205 * @retval (int)[-1,int] A new file descriptor. -1 on error
206 */
121int maketmp(const char * const dest) { 207int maketmp(const char * const dest) {
122 if(!dest) ERRRET(EINVAL, -1); 208 if(!dest) ERRRET(EINVAL, -1);
123 return open(dest, (O_TMPFILE | O_WRONLY | O_CLOEXEC | O_SYNC), (S_IRUSR | S_IWUSR)); 209 return open(dest, (O_TMPFILE | O_WRONLY | O_CLOEXEC | O_SYNC), (S_IRUSR | S_IWUSR));
124} 210}
125 211
212/**
213 * @brief Link a file descriptor into the filesystem
214 *
215 * @param target New filename the descriptor should have
216 * @param tgfd The file descriptor to link
217 * @retval (int)[-1, 0] 0 on success, -1 on error
218 */
126int linkto(const char * const target, int tgfd) { 219int linkto(const char * const target, int tgfd) {
127 if(!target) ERRRET(EINVAL, -1); 220 if(!target) ERRRET(EINVAL, -1);
128 221
@@ -131,6 +224,9 @@ int linkto(const char * const target, int tgfd) {
131 if(!path) 224 if(!path)
132 ERROR(1, errno, "<linkto> Couldn't get path to move file into system",); 225 ERROR(1, errno, "<linkto> Couldn't get path to move file into system",);
133 remove(target); // Make sure an old version isn't sticking around (it's not catastrophic if this fails, but it should be noted or logged somewhere) 226 remove(target); // Make sure an old version isn't sticking around (it's not catastrophic if this fails, but it should be noted or logged somewhere)
227 // TODO: This is bad. If a file gets deleted and the program crashes before the new one can get linked into the fs, the data is lost.
228 // I really should write a function entirely dedicated to dealing with linking
229
134 int res = linkat(AT_FDCWD, path, AT_FDCWD, target, AT_SYMLINK_FOLLOW); 230 int res = linkat(AT_FDCWD, path, AT_FDCWD, target, AT_SYMLINK_FOLLOW);
135 free(path); 231 free(path);
136 return res; 232 return res;
@@ -150,6 +246,132 @@ static void __ucl_fclose(void *file) {
150 return; 246 return;
151} 247}
152 248
249/**
250 * @brief Encrypt src to dst using libsodium's xchacha encryption suite
251 *
252 * @param src File to encrypt
253 * @param dst Destination to write encrypted file
254 * @param key Key for encryption
255 * @retval (int)[-1, 0] Returns 0 on success, sets errno and returns -1 on error
256 */
257int encrypttofile(FILE *src, FILE *dst, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]) {
258 if(!src || !dst || !key) ERRRET(EINVAL, -1);
259 #if ___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0
260 checksodium();
261 #endif
262
263 unsigned char buf[CHUNKSIZE], cbuf[CHUNKSIZE + crypto_secretstream_xchacha20poly1305_ABYTES];
264 unsigned char header[crypto_secretstream_xchacha20poly1305_HEADERBYTES];
265 crypto_secretstream_xchacha20poly1305_state state;
266 unsigned long long cbuflen;
267 unsigned char tag;
268 size_t bytesread;
269 int eof;
270
271 // Write the header
272 crypto_secretstream_xchacha20poly1305_init_push(&state, header, key);
273 if(fwrite(header, 1, sizeof(header), dst) < sizeof(header)) {
274 if(ferror(dst)) {
275 WARN(errno, "<encrypttofile> Could not write header",);
276 return -1;
277 }
278 }
279
280 // Encrypt each chunk
281 do {
282 if((bytesread = fread(buf, 1, sizeof(buf), src)) < sizeof(buf))
283 if(ferror(src)) {
284 WARN(errno, "<encrypttofile> Could not read from source",);
285 return -1;
286 }
287 eof = feof(src);
288 tag = eof ? crypto_secretstream_xchacha20poly1305_TAG_FINAL : 0;
289
290 crypto_secretstream_xchacha20poly1305_push(&state, cbuf, &cbuflen, buf, bytesread, NULL, 0, tag);
291 if(fwrite(cbuf, 1, (size_t)cbuflen, dst) < (size_t)cbuflen)
292 if(ferror(dst)) {
293 WARN(errno, "<encrypttofile> Could not write to target",);
294 return -1;
295 }
296 } while (!eof);
297
298 return 0;
299}
300
301/**
302 * @brief Decrypt src to dst using libsodium's xchacha encryption suite
303 *
304 * @param src File to decrypt
305 * @param dst Destination to write decrypted file
306 * @param key Key used to encrypt
307 * @retval (int)[-1, 0] Returns 0 on success, sets errno and returns -1 on error
308 */
309int decrypttofile(FILE *src, FILE *dst, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]) {
310 if(!src || !dst || !key) ERRRET(EINVAL, -1);
311 #if ___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0
312 checksodium();
313 #endif
314
315 unsigned char cbuf[CHUNKSIZE + crypto_secretstream_xchacha20poly1305_ABYTES], buf[CHUNKSIZE];
316 unsigned char header[crypto_secretstream_xchacha20poly1305_HEADERBYTES];
317 crypto_secretstream_xchacha20poly1305_state state;
318 unsigned long long buflen;
319 unsigned char tag;
320 size_t bytesread;
321 int eof;
322
323 // Read the header
324 if(fread(header, 1, sizeof(header), src) < sizeof(header)) {
325 if(ferror(src)) {
326 WARN(errno, "<decrypttofile> Couldn't read header", );
327 return -1;
328 }
329 }
330
331 // Make sure the header isn't fuckey
332 if(crypto_secretstream_xchacha20poly1305_init_pull(&state, header, key) != 0) {
333 WARN(errno, "<decrypttofile> Incomplete header", );
334 return -1;
335 }
336
337 // Decrypt each chunk
338 do {
339 if((bytesread = fread(cbuf, 1, sizeof(cbuf), src)) < sizeof(cbuf)) {
340 if(ferror(src)) {
341 WARN(errno, "<decrypttofile> Ran into problem reading for decryption", );
342 return -1;
343 }
344 }
345 eof = feof(src);
346
347 if (crypto_secretstream_xchacha20poly1305_pull(&state, buf, &buflen, &tag, cbuf, bytesread, NULL, 0) != 0) {
348 WARN(errno, "<decrypttofile> Corrupted chunk", );
349 return -1;
350 }
351
352 if(tag == crypto_secretstream_xchacha20poly1305_TAG_FINAL && !eof) {
353 WARN(errno, "<decrypttofile> End of stream before end of file", );
354 return -1;
355 }
356 if(eof && tag != crypto_secretstream_xchacha20poly1305_TAG_FINAL) {
357 WARN(errno, "<decrypttofile> End of file before end of stream", );
358 return -1;
359 }
360
361 fwrite(buf, 1, (size_t)buflen, dst);
362 } while(! eof);
363
364 return 0;
365}
366
367/**
368 * @brief Encrypt file at `target` to `output` using Linux's named temp file system to do it in the background
369 *
370 * @param target
371 * @param output
372 * @param key
373 * @retval (int)[,]
374 */
153int encryptviatmp(const char * const target, const char * const output, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]) { 375int encryptviatmp(const char * const target, const char * const output, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]) {
154 #if ___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0 376 #if ___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0
155 checksodium(); 377 checksodium();
@@ -203,7 +425,7 @@ int encryptviatmp(const char * const target, const char * const output, const un
203 // Not going to bother changing this, it probably is catastrophic if an error happens when it shouldn't 425 // Not going to bother changing this, it probably is catastrophic if an error happens when it shouldn't
204 if(encrypttofile(src, dst, key) < 0) 426 if(encrypttofile(src, dst, key) < 0)
205 ERROR(1, ENOTRECOVERABLE, "<encryptviatmp> I don't even have a way to cause an error here. How did you do it?",); 427 ERROR(1, ENOTRECOVERABLE, "<encryptviatmp> I don't even have a way to cause an error here. How did you do it?",);
206 428
207 // Link the temp file into the system 429 // Link the temp file into the system
208 if(linkto(output, tfd) < 0) 430 if(linkto(output, tfd) < 0)
209 WARN(errno, "<encryptviatmp> Could not link \"%s\" into system after encryption", , output); 431 WARN(errno, "<encryptviatmp> Could not link \"%s\" into system after encryption", , output);
@@ -213,21 +435,29 @@ int encryptviatmp(const char * const target, const char * const output, const un
213 fclose(src); 435 fclose(src);
214 // fclose alco closes fd and tfd, as fdopen does not dup the file descriptors 436 // fclose alco closes fd and tfd, as fdopen does not dup the file descriptors
215 ); 437 );
216 438
217 439
218 cleanup_CNDFIRE(); 440 cleanup_CNDFIRE();
219 if(cleanup_ERRORFLAGGED) 441 if(cleanup_ERRORFLAGGED)
220 return -1; 442 return -1;
221 443
222 return 0; 444 return 0;
223} 445}
224 446
447/**
448 * @brief Decrypt the file at `encrypted` to `target`
449 *
450 * @param encrypted
451 * @param target
452 * @param key
453 * @retval (int)[,]
454 */
225int decryptto(const char * const target, const char * const output, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]) { 455int decryptto(const char * const target, const char * const output, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]) {
226 if(!target || !output || !key) ERRRET(EINVAL, -1); 456 if(!target || !output || !key) ERRRET(EINVAL, -1);
227 #if ___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0 457 #if ___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0
228 checksodium(); 458 checksodium();
229 #endif 459 #endif
230 460
231 cleanup_CREATE(10); 461 cleanup_CREATE(10);
232 FILE *src, *dst; 462 FILE *src, *dst;
233 int fdst; 463 int fdst;
@@ -276,7 +506,7 @@ int decryptto(const char * const target, const char * const output, const unsign
276 if(cleanup_ERRORFLAGGED) 506 if(cleanup_ERRORFLAGGED)
277 return -1; 507 return -1;
278 508
279 // Note: If an error were to theoretically occur, which shouldn't be possible but I'm covering my bases here, after the 509 // Note: If an error were to theoretically occur, which shouldn't be possible but I'm covering my bases here, after the
280 // `dst = fdopen` line, a double close on the temp file descriptor would occur. I've been told that this is not catastrophic, 510 // `dst = fdopen` line, a double close on the temp file descriptor would occur. I've been told that this is not catastrophic,
281 // and considering how my multithreading works it *should* be fine, but it very well could cause problems. The easy solution is 511 // and considering how my multithreading works it *should* be fine, but it very well could cause problems. The easy solution is
282 // to man up and just write another cleanup function to pop the last thing off the stack, but again this is an error I can't 512 // to man up and just write another cleanup function to pop the last thing off the stack, but again this is an error I can't
@@ -285,108 +515,13 @@ int decryptto(const char * const target, const char * const output, const unsign
285 return 0; 515 return 0;
286} 516}
287 517
288int encrypttofile(FILE *src, FILE *dst, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]) { 518/**
289 if(!src || !dst || !key) ERRRET(EINVAL, -1); 519 * @brief Generate a password viable for use in the derivation of a key
290 #if ___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0 520 *
291 checksodium(); 521 * @param str Pointer to a string. This will be filled by a malloc'ed string of words (the password). Must be non-null
292 #endif 522 * @param words The number of words to include in the password. A password of at least 20 words and probably not more than 40 is recommended
293 523 * @retval (int)[-1, words] On success, returns the number of words requested. On error, returns -1 and sets errno
294 unsigned char buf[CHUNKSIZE], cbuf[CHUNKSIZE + crypto_secretstream_xchacha20poly1305_ABYTES]; 524 */
295 unsigned char header[crypto_secretstream_xchacha20poly1305_HEADERBYTES];
296 crypto_secretstream_xchacha20poly1305_state state;
297 unsigned long long cbuflen;
298 unsigned char tag;
299 size_t bytesread;
300 int eof;
301
302 // Write the header
303 crypto_secretstream_xchacha20poly1305_init_push(&state, header, key);
304 if(fwrite(header, 1, sizeof(header), dst) < sizeof(header)) {
305 if(ferror(dst)) {
306 WARN(errno, "<encrypttofile> Could not write header",);
307 return -1;
308 }
309 }
310
311 // Encrypt each chunk
312 do {
313 if((bytesread = fread(buf, 1, sizeof(buf), src)) < sizeof(buf))
314 if(ferror(src)) {
315 WARN(errno, "<encrypttofile> Could not read from source",);
316 return -1;
317 }
318 eof = feof(src);
319 tag = eof ? crypto_secretstream_xchacha20poly1305_TAG_FINAL : 0;
320
321 crypto_secretstream_xchacha20poly1305_push(&state, cbuf, &cbuflen, buf, bytesread, NULL, 0, tag);
322 if(fwrite(cbuf, 1, (size_t)cbuflen, dst) < (size_t)cbuflen)
323 if(ferror(dst)) {
324 WARN(errno, "<encrypttofile> Could not write to target",);
325 return -1;
326 }
327 } while (!eof);
328
329 return 0;
330}
331
332int decrypttofile(FILE *src, FILE *dst, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]) {
333 if(!src || !dst || !key) ERRRET(EINVAL, -1);
334 #if ___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0
335 checksodium();
336 #endif
337
338 unsigned char cbuf[CHUNKSIZE + crypto_secretstream_xchacha20poly1305_ABYTES], buf[CHUNKSIZE];
339 unsigned char header[crypto_secretstream_xchacha20poly1305_HEADERBYTES];
340 crypto_secretstream_xchacha20poly1305_state state;
341 unsigned long long buflen;
342 unsigned char tag;
343 size_t bytesread;
344 int eof;
345
346 // Read the header
347 if(fread(header, 1, sizeof(header), src) < sizeof(header)) {
348 if(ferror(src)) {
349 WARN(errno, "<decrypttofile> Couldn't read header", );
350 return -1;
351 }
352 }
353
354 // Make sure the header isn't fuckey
355 if(crypto_secretstream_xchacha20poly1305_init_pull(&state, header, key) != 0) {
356 WARN(errno, "<decrypttofile> Incomplete header", );
357 return -1;
358 }
359
360 // Decrypt each chunk
361 do {
362 if((bytesread = fread(cbuf, 1, sizeof(cbuf), src)) < sizeof(cbuf)) {
363 if(ferror(src)) {
364 WARN(errno, "<decrypttofile> Ran into problem reading for decryption", );
365 return -1;
366 }
367 }
368 eof = feof(src);
369
370 if (crypto_secretstream_xchacha20poly1305_pull(&state, buf, &buflen, &tag, cbuf, bytesread, NULL, 0) != 0) {
371 WARN(errno, "<decrypttofile> Corrupted chunk", );
372 return -1;
373 }
374
375 if(tag == crypto_secretstream_xchacha20poly1305_TAG_FINAL && !eof) {
376 WARN(errno, "<decrypttofile> End of stream before end of file", );
377 return -1;
378 }
379 if(eof && tag != crypto_secretstream_xchacha20poly1305_TAG_FINAL) {
380 WARN(errno, "<decrypttofile> End of file before end of stream", );
381 return -1;
382 }
383
384 fwrite(buf, 1, (size_t)buflen, dst);
385 } while(! eof);
386
387 return 0;
388}
389
390int genpassword(char **str, unsigned int words) { 525int genpassword(char **str, unsigned int words) {
391 // Early returns 526 // Early returns
392 if(words < 1) return 0; 527 if(words < 1) return 0;
@@ -402,7 +537,7 @@ int genpassword(char **str, unsigned int words) {
402 537
403 // Concat the rest of the words into the password (without leaking memory) 538 // Concat the rest of the words into the password (without leaking memory)
404 int ret; 539 int ret;
405 for(unsigned int i = 1; i < words; i++) { 540 for(unsigned int i = 1; i < words; i++) {
406 ret = asprintf(&tmp, "%s %s", lstr, PASSWORD_WORDS[randombytes_uniform(PASSWORD_WORDS_LEN)]); 541 ret = asprintf(&tmp, "%s %s", lstr, PASSWORD_WORDS[randombytes_uniform(PASSWORD_WORDS_LEN)]);
407 sodium_memzero(lstr, strlen(lstr) + 1); 542 sodium_memzero(lstr, strlen(lstr) + 1);
408 free(lstr); 543 free(lstr);
@@ -416,6 +551,14 @@ int genpassword(char **str, unsigned int words) {
416 return words; 551 return words;
417} 552}
418 553
554/**
555 * @brief sodium_malloc wrapper.
556 *
557 * Calls `error()` or `abort()` depnding on the value of `___VXGG___XALLOC_EXIT_ON_ERROR___`. Will make sure libsodium is initialized if `___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0`
558 *
559 * @param size
560 * @retval (void*) A pointer to some data allocated via `sodium_malloc()`
561 */
419void* xsodium_malloc(size_t size) { 562void* xsodium_malloc(size_t size) {
420 #if ___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0 563 #if ___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0
421 checksodium(); 564 checksodium();
@@ -436,7 +579,7 @@ void* xsodium_malloc(size_t size) {
436 579
437// dlinkedlist * scandirlist(const char * const dir, int (*selector)(const struct dirent *), int (*cmp)(const struct dirent **, const struct dirent **)) { 580// dlinkedlist * scandirlist(const char * const dir, int (*selector)(const struct dirent *), int (*cmp)(const struct dirent **, const struct dirent **)) {
438// if(!dir || selector == NULL || cmp == NULL) ERRRET(EINVAL, NULL); 581// if(!dir || selector == NULL || cmp == NULL) ERRRET(EINVAL, NULL);
439 582
440// struct dirent **namelist = NULL; 583// struct dirent **namelist = NULL;
441// dlinkedlist *list = NULL; 584// dlinkedlist *list = NULL;
442// int numentries = -1; 585// int numentries = -1;
@@ -459,137 +602,38 @@ void* xsodium_malloc(size_t size) {
459// return list; 602// return list;
460// } 603// }
461 604
605// Above implementation is flawed and would not actually scan the entire system. The process must be recursive:
606 // Step 1 - Create directory list
607 // Step 2 - Create --cryption ctq
608 // Step 3 - Scan initial starting dir. This will be /home/
609 // Step 4 - Iterate over scan results
610 // Step 4.1 - For all directory dirent objects, add them to the directory list
611 // Step 4.2 - For all file dirent objects, add them to the --cryption ctq
612 // Step 5 - Scan next entry in the dirlist, removing it once done. Repeat Step 4
613 // Step 6 - Free dirlist once empty, return newly populated --cryption ctq
462 614
615// Idea: Create 2 ctqs. Use one for the actual scanning, and the other as the return result. That way, not only will scanning be
616// fast, but I can also just reuse code I've already written and not make some absolute spaghetti mess trying to do everything
617// linearly
463 618
464struct __FLC_FKP { 619int __cscan_worker(void *data) {
465 char *target; 620 if(!data) return -1;
466 char *output;
467 const unsigned char *key;
468};
469
470struct __FLC_FKP * fkp_init(char * const target, char * const output, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]) {
471 if(!target || !output || !key) ERRRET(EINVAL, NULL);
472 struct __FLC_FKP *fkp = calloc(1, sizeof(*fkp));
473 if(!fkp)
474 return NULL;
475
476 fkp->key = key;
477 fkp->output = output;
478 fkp->target = target;
479
480 return fkp;
481}
482
483void fkp_free(void *fkp) {
484 if(!fkp) return;
485 struct __FLC_FKP *real = (struct __FLC_FKP *)fkp;
486
487 free(real->output);
488 free(real->target);
489 free(real);
490
491 return;
492}
493
494static int __FLC_TASK_ENCRYPT(void *fkp) {
495 if(!fkp)
496 return -1;
497 struct __FLC_FKP *real = (struct __FLC_FKP *)fkp;
498 return encryptviatmp(real->target, real->output, real->key);
499}
500static int __FLC_TASK_DECRYPT(void *fkp) {
501 if(!fkp)
502 return -1;
503 struct __FLC_FKP *real = (struct __FLC_FKP *)fkp;
504 return decryptto(real->target, real->output, real->key);
505}
506 621
507enum VXGG_FLC { 622 return 0;
508 VXGG_FLC__INVALID,
509 VXGG_FLC__ENCRYPT,
510 VXGG_FLC__DECRYPT,
511 VXGG_FLC__TOOBIG
512};
513
514struct __ucl_namelist_vals {
515 struct dirent **namelist;
516 int entries;
517};
518
519static void __ucl_namelist(void *namelist) {
520 if(!namelist) return;
521 struct __ucl_namelist_vals *real = namelist;
522 for(int i = 0; i > real->entries; i++)
523 free(real->namelist[i]);
524 free(real->namelist);
525 return;
526} 623}
527 624
528// TODO: Write these 625ctqueue * cryptscan() {
529static int encryption_filter(const struct dirent *de) { 626 ctqueue *res = ctqueue_init(TPSIZE), *working = ctqueue_init(TPSIZE);
627 if(!res || !working) ERRRET(errno, NULL);
530 628
531} 629 task *start = task_init(__cscan_worker, free, void *data);
532static int decryption_filter(const struct dirent *de) { 630 if(!start) ERRRET(errno, NULL);
631 ctqueue_waitpush(working, start);
533 632
534}
535 633
536ctqueue * getfilelist(enum VXGG_FLC mode, const char * const dir, int threads, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]) { 634 return res;
537 if(mode <= VXGG_FLC__INVALID || mode >= VXGG_FLC__TOOBIG || !dir || threads <= 0 || !key) ERRRET(EINVAL, NULL);
538
539 cleanup_CREATE(10);
540 ctqueue *ctq = NULL;
541 int files = -1;
542
543 // Create the ctqueue for later
544 ctq = ctqueue_init(threads);
545 if(!ctq)
546 return NULL;
547 cleanup_REGISTER(ctqueue_free, ctq);
548
549 // Get the scandir list
550 struct dirent **namelist;
551 if((files = scandir(dir, &namelist, ((mode == VXGG_FLC__ENCRYPT) ? encryption_filter : decryption_filter), alphasort)) < 0)
552 cleanup_MARK();
553 cleanup_CNDREGISTER(__ucl_namelist, (void*)(&(struct __ucl_namelist_vals){.namelist = namelist, .entries = files}));
554
555 // Push everything onto the ctqueue
556 // TODO: Write task* compatible callbacks for encryption and decryption, then populate the ctqueue based on the specified mode
557 cleanup_CNDEXEC(
558 for(int i = 0; i < files; i++) {
559
560 // Target is either "filename" or "filename.vxggr"
561 // Output is either "filename.vxggr" or "filename"
562
563 struct __FLC_FKP *fkp = fkp_init(target, output, key);
564 if(!fkp) {
565 WARN(errno, "<getfilelist> Could not create file-key pair for \"%s\"'s %scryption task, skipping...",, namelist[i]->d_name, ((mode == VXGG_FLC__ENCRYPT) ? "en" : "de"));
566 free(target);
567 free(output);
568 }
569 task *ctask = task_init((mode == VXGG_FLC__ENCRYPT) ? __FLC_TASK_ENCRYPT : __FLC_TASK_DECRYPT, fkp_free, fkp);
570 if(!ctask) {
571 WARN(errno, "<getfilelist> Could not push \"%s\"'s %scryption task, skipping...",, namelist[i]->d_name, ((mode == VXGG_FLC__ENCRYPT) ? "en" : "de"));
572 free(namelist[i]);
573 continue;
574 }
575 ctqueue_waitpush(ctq, ctask);
576 }
577
578 free(namelist);
579 // namelist is the array that holds each pointer to each dirent, so it needs to be free'd separately from the other elements
580 );
581
582 cleanup_CNDFIRE();
583 if(cleanup_ERRORFLAGGED)
584 return NULL;
585
586 return ctq;
587} 635}
588 636
589
590
591
592
593/* 637/*
594int main(void) { 638int main(void) {
595 // Example code for creating a temp file, writing to it, then linking it back into the fs 639 // Example code for creating a temp file, writing to it, then linking it back into the fs
@@ -602,7 +646,7 @@ int main(void) {
602 646
603 if(write(fd, testmsg, strlen(testmsg)) < 0) 647 if(write(fd, testmsg, strlen(testmsg)) < 0)
604 error(1, errno, "write broke"); 648 error(1, errno, "write broke");
605 649
606 asprintf(&path, "/proc/self/fd/%d", fd); 650 asprintf(&path, "/proc/self/fd/%d", fd);
607 linkat(AT_FDCWD, path, AT_FDCWD, "./test", AT_SYMLINK_FOLLOW); 651 linkat(AT_FDCWD, path, AT_FDCWD, "./test", AT_SYMLINK_FOLLOW);
608 free(path); 652 free(path);
@@ -623,7 +667,7 @@ int main(void) {
623 //*/// 667 //*///
624 668
625 /*// Example code for generating a password, derriving a secret key from it, and storing things properly 669 /*// Example code for generating a password, derriving a secret key from it, and storing things properly
626 670
627 // Initialization 671 // Initialization
628 checksodium(); 672 checksodium();
629 char *pass = NULL, hpass[crypto_pwhash_STRBYTES]; 673 char *pass = NULL, hpass[crypto_pwhash_STRBYTES];
@@ -638,7 +682,7 @@ int main(void) {
638 // Store the password 682 // Store the password
639 if(crypto_pwhash_str(hpass, pass, strlen(pass) + 1, crypto_pwhash_OPSLIMIT_MODERATE, crypto_pwhash_MEMLIMIT_MODERATE) != 0) 683 if(crypto_pwhash_str(hpass, pass, strlen(pass) + 1, crypto_pwhash_OPSLIMIT_MODERATE, crypto_pwhash_MEMLIMIT_MODERATE) != 0)
640 error(1, errno, "Couldn't generate password, quitting..."); 684 error(1, errno, "Couldn't generate password, quitting...");
641 // Don't know if I want to use MODERATE or SENSITIVE for this. SENSITIVE takes a little bit on my laptop, which honestly 685 // Don't know if I want to use MODERATE or SENSITIVE for this. SENSITIVE takes a little bit on my laptop, which honestly
642 // shouldn't be a problem, but it annoys me. MODERATE is quick and snappy, or at least quick enough that the slowdown is 686 // shouldn't be a problem, but it annoys me. MODERATE is quick and snappy, or at least quick enough that the slowdown is
643 // barely noticable. I might do MODERATE for testing and SENSITIVE for release 687 // barely noticable. I might do MODERATE for testing and SENSITIVE for release
644 688
@@ -661,4 +705,6 @@ int main(void) {
661 705
662 return 0; 706 return 0;
663} 707}
664*/ \ No newline at end of file 708*/
709
710#endif \ No newline at end of file