Added flag to indicate multi-thread support to the public API.
[slunkcrypt.git] / libslunkcrypt / src / slunkcrypt.c
blob2a5c91fd85b5d2763fc9cb768c81c978f0a6dc5a
1 /******************************************************************************/
2 /* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de> */
3 /* This work has been released under the CC0 1.0 Universal license! */
4 /******************************************************************************/
6 /* Internal */
7 #include "slunkcrypt.h"
8 #include "compiler.h"
9 #include "keygen.h"
10 #include "thread.h"
11 #include "version.h"
13 /* CRT */
14 #include <string.h>
15 #include <limits.h>
17 /* Version */
18 const uint16_t SLUNKCRYPT_VERSION_MAJOR = LIB_VERSION_MAJOR;
19 const uint16_t SLUNKCRYPT_VERSION_MINOR = LIB_VERSION_MINOR;
20 const uint16_t SLUNKCRYPT_VERSION_PATCH = LIB_VERSION_PATCH;
21 const char *const SLUNKCRYPT_BUILD = __DATE__ ", " __TIME__;
23 /* Utilities */
24 #define BOOLIFY(X) (!!(X))
25 #define THREAD_COUNT(X) (((X)->thread_pool) ? slunkcrypt_thrdpl_count((X)->thread_pool) : 1U)
27 /* Configuration */
28 const int SLUNKCRYPT_HAVE_THREADS = BOOLIFY(MAX_THREADS > 1U);
30 // ==========================================================================
31 // Data structures
32 // ==========================================================================
34 typedef struct
36 uint32_t x, y, z, w, v, d;
38 rand_state_t;
40 typedef struct
42 int reverse_mode;
43 const uint8_t (*wheel)[256U];
44 uint32_t counter;
45 size_t index_off;
46 rand_state_t random;
48 thread_state_t;
50 typedef struct
52 uint8_t wheel[256U][256U];
53 thread_state_t thread_data[MAX_THREADS];
55 crypt_data_t;
57 typedef struct
59 thrdpl_t *thread_pool;
60 crypt_data_t data;
62 crypt_state_t;
64 // ==========================================================================
65 // Abort flag
66 // ==========================================================================
68 volatile int g_slunkcrypt_abort_flag = 0;
70 #define CHECK_ABORTED() do \
71 { \
72 if (g_slunkcrypt_abort_flag) \
73 { \
74 goto aborted; \
75 } \
76 } \
77 while (0)
79 // ==========================================================================
80 // Byte access (endianness agnostic)
81 // ==========================================================================
83 static INLINE uint32_t lower_u64(const uint64_t value)
85 return (uint32_t)(value & 0xFFFFFFFF);
88 static INLINE uint32_t upper_u64(const uint64_t value)
90 return (uint32_t)(value >> 32U);
93 // ==========================================================================
94 // Deterministic random bit generator
95 // ==========================================================================
97 static INLINE void random_init(rand_state_t *const state, const keydata_t *const key)
99 slunkcrypt_bzero(state, sizeof(rand_state_t));
100 state->x = lower_u64(key->a);
101 state->y = upper_u64(key->a);
102 state->z = lower_u64(key->b);
103 state->w = upper_u64(key->b);
104 state->v = lower_u64(key->c);
105 state->d = upper_u64(key->c);
108 static INLINE uint32_t random_next(rand_state_t *const state)
110 const uint32_t t = state->x ^ (state->x >> 2);
111 state->x = state->y;
112 state->y = state->z;
113 state->z = state->w;
114 state->w = state->v;
115 state->v ^= (state->v << 4) ^ t ^ (t << 1);
116 return (state->d += 0x000587C5) + state->v;
119 static INLINE void random_skip(rand_state_t *const state, const size_t skip_count)
121 size_t i;
122 for (i = 0U; i < skip_count; ++i)
124 UNUSED /*volatile*/ uint32_t q = random_next(state);
128 static INLINE void random_seed(rand_state_t *const state, uint64_t salt, const uint16_t pepper, const uint8_t *const passwd, const size_t passwd_len)
130 keydata_t key;
133 slunkcrypt_keygen(&key, salt++, pepper, passwd, passwd_len);
134 random_init(state, &key);
135 slunkcrypt_bzero(&key, sizeof(keydata_t));
137 while (!(state->x || state->y || state->z || state->w || state->v));
138 random_skip(state, 97U);
141 // ==========================================================================
142 // Initialization
143 // ==========================================================================
145 static int initialize_state(crypt_data_t *const data, const size_t thread_count, const uint64_t nonce, const uint8_t *const passwd, const size_t passwd_len, const int mode)
147 uint8_t temp[256U][256U];
148 size_t r, i;
149 const int reverse_mode = BOOLIFY(mode);
151 /* initialize state */
152 slunkcrypt_bzero(data, sizeof(crypt_data_t));
154 /* initialize counter */
155 random_seed(&data->thread_data[0].random, nonce, (uint16_t)(-1), passwd, passwd_len);
156 data->thread_data[0].counter = random_next(&data->thread_data[0].random);
158 /* set up the wheel permutations */
159 for (r = 0U; r < 256U; ++r)
161 random_seed(&data->thread_data[0].random, nonce, (uint16_t)r, passwd, passwd_len);
162 for (i = 0U; i < 256U; ++i)
164 const size_t j = random_next(&data->thread_data[0].random) % (i + 1U);
165 if (j != i)
167 data->wheel[r][i] = data->wheel[r][j];
169 data->wheel[r][j] = (uint8_t)i;
171 CHECK_ABORTED();
174 /* reverse the wheels, if requested */
175 if (reverse_mode)
177 for (r = 0U; r < 256U; ++r)
179 for (i = 0U; i < 256U; ++i)
181 temp[r][data->wheel[r][i]] = (uint8_t)i;
184 for (r = 0U; r < 256U; ++r)
186 memcpy(data->wheel[255U - r], temp[r], 256U);
188 slunkcrypt_bzero(temp, sizeof(temp));
189 CHECK_ABORTED();
192 /* initialize thread state */
193 data->thread_data[0].reverse_mode = reverse_mode;
194 data->thread_data[0].wheel = (const uint8_t(*)[256]) data->wheel;
195 data->thread_data[0].index_off = 0U;
196 random_seed(&data->thread_data[0].random, nonce, 256U, passwd, passwd_len);
197 for (i = 1U; i < thread_count; ++i)
199 data->thread_data[i].reverse_mode = data->thread_data[0].reverse_mode;
200 data->thread_data[i].wheel = data->thread_data[0].wheel;
201 data->thread_data[i].counter = data->thread_data[0].counter + ((uint32_t)i);
202 data->thread_data[i].index_off = data->thread_data[i - 1U].index_off + 1U;
203 memcpy(&data->thread_data[i].random, &data->thread_data[0].random, sizeof(rand_state_t));
204 random_skip(&data->thread_data[i].random, i * 63U);
205 CHECK_ABORTED();
208 return SLUNKCRYPT_SUCCESS;
210 /* aborted */
211 aborted:
212 slunkcrypt_bzero(data, sizeof(crypt_data_t));
213 return SLUNKCRYPT_ABORTED;
216 // ==========================================================================
217 // Encrypt / Decrypt
218 // ==========================================================================
220 static INLINE void update_offset(uint8_t *const offset, uint32_t seed, rand_state_t *const state, const int reverse)
222 size_t i;
223 for (i = 0U; i < 256U; ++i, seed >>= CHAR_BIT)
225 if (i && (!(i & 3U)))
227 seed = random_next(state);
229 offset[reverse ? (255U - i) : i] = (uint8_t)seed;
233 static INLINE uint8_t process_next_symbol(thread_state_t *const state, uint8_t value)
235 uint8_t offset[256U];
236 size_t i;
237 update_offset(offset, state->counter, &state->random, state->reverse_mode);
238 for (i = 0U; i < 256U; ++i)
240 value = (state->wheel[i][(value + offset[i]) & 0xFF] - offset[i]) & 0xFF;
242 return value;
245 // ==========================================================================
246 // Thread entry point
247 // ==========================================================================
249 static INLINE void update_index(thread_state_t *const state, const size_t thread_count, const size_t length)
251 const size_t remaining = thread_count - (length % thread_count);
252 if (remaining != thread_count)
254 state->index_off = (state->index_off + remaining) % thread_count;
258 static void thread_worker(const size_t thread_count, void *const context, uint8_t *const buffer, const size_t length)
260 thread_state_t *const state = (thread_state_t*) context;
261 size_t i;
263 for (i = state->index_off; i < length; i += thread_count)
265 buffer[i] = process_next_symbol(state, buffer[i]);
266 state->counter += (uint32_t)thread_count;
267 random_skip(&state->random, 63U * (thread_count - 1U));
270 update_index(state, thread_count, length);
273 // ==========================================================================
274 // Public API
275 // ==========================================================================
277 int slunkcrypt_generate_nonce(uint64_t *const nonce)
279 if (!nonce)
281 return SLUNKCRYPT_FAILURE;
285 if (slunkcrypt_random_bytes((uint8_t*)nonce, sizeof(uint64_t)) != sizeof(uint64_t))
287 return SLUNKCRYPT_FAILURE;
290 while (!(*nonce));
291 return SLUNKCRYPT_SUCCESS;
294 slunkcrypt_t slunkcrypt_alloc(const uint64_t nonce, const uint8_t *const passwd, const size_t passwd_len, const int mode)
296 slunkparam_t param = { SLUNKCRYPT_PARAM_VERSION, 0U };
297 return slunkcrypt_alloc_ext(nonce, passwd, passwd_len, mode, &param);
300 slunkcrypt_t slunkcrypt_alloc_ext(const uint64_t nonce, const uint8_t *const passwd, const size_t passwd_len, const int mode, const slunkparam_t *const param)
302 crypt_state_t* state = NULL;
304 if ((!passwd) || (passwd_len < SLUNKCRYPT_PWDLEN_MIN) || (passwd_len > SLUNKCRYPT_PWDLEN_MAX) ||
305 (mode < SLUNKCRYPT_ENCRYPT) || (mode > SLUNKCRYPT_DECRYPT) || (!param) || (param->version == 0U) || (param->version > SLUNKCRYPT_PARAM_VERSION))
307 return SLUNKCRYPT_NULL;
310 if (!(state = (crypt_state_t*)malloc(sizeof(crypt_state_t))))
312 return SLUNKCRYPT_NULL;
315 if ((state->thread_pool = slunkcrypt_thrdpl_create(param->thread_count, thread_worker)))
317 const size_t thread_count = slunkcrypt_thrdpl_count(state->thread_pool);
318 size_t i;
319 for (i = 0U; i < thread_count; ++i)
321 slunkcrypt_thrdpl_init(state->thread_pool, i, &state->data.thread_data[i]);
325 if (initialize_state(&state->data, THREAD_COUNT(state), nonce, passwd, passwd_len, mode) == SLUNKCRYPT_SUCCESS)
327 return (slunkcrypt_t)state;
330 slunkcrypt_free((slunkcrypt_t)state);
331 return SLUNKCRYPT_NULL;
334 int slunkcrypt_reset(const slunkcrypt_t context, const uint64_t nonce, const uint8_t *const passwd, const size_t passwd_len, const int mode)
336 crypt_state_t *const state = (crypt_state_t*) context;
337 int result = SLUNKCRYPT_FAILURE;
339 if ((!state) || (!passwd) || (passwd_len < SLUNKCRYPT_PWDLEN_MIN) || (passwd_len > SLUNKCRYPT_PWDLEN_MAX) || (mode < SLUNKCRYPT_ENCRYPT) || (mode > SLUNKCRYPT_DECRYPT))
341 return SLUNKCRYPT_FAILURE;
343 if ((result = initialize_state(&state->data, THREAD_COUNT(state), nonce, passwd, passwd_len, mode)) != SLUNKCRYPT_SUCCESS)
345 slunkcrypt_bzero(&state->data, sizeof(crypt_data_t));
347 return result;
350 int slunkcrypt_process(const slunkcrypt_t context, const uint8_t *const input, uint8_t *const output, size_t length)
352 crypt_state_t *const state = (crypt_state_t*)context;
353 if (!state)
355 return SLUNKCRYPT_FAILURE;
358 if (length > 0U)
360 memcpy(output, input, length);
361 return slunkcrypt_inplace(context, output, length);
364 return SLUNKCRYPT_SUCCESS;
367 int slunkcrypt_inplace(const slunkcrypt_t context, uint8_t *const buffer, size_t length)
369 crypt_state_t *const state = (crypt_state_t*)context;
370 if (!state)
372 return SLUNKCRYPT_FAILURE;
375 if (length > 0U)
377 if (THREAD_COUNT(state) > 1U)
379 slunkcrypt_thrdpl_exec(state->thread_pool, buffer, length);
381 else
383 thread_worker(1U, &state->data.thread_data[0U], buffer, length);
387 CHECK_ABORTED();
388 return SLUNKCRYPT_SUCCESS;
390 aborted:
391 slunkcrypt_bzero(buffer, length);
392 slunkcrypt_bzero(&state->data, sizeof(crypt_data_t));
393 return SLUNKCRYPT_ABORTED;
396 void slunkcrypt_free(const slunkcrypt_t context)
398 crypt_state_t *const state = (crypt_state_t*) context;
399 if (state)
401 if (state->thread_pool)
403 slunkcrypt_thrdpl_destroy(state->thread_pool);
405 slunkcrypt_bzero(state, sizeof(crypt_state_t));
406 free(state);