1
0

432 lines
12 KiB
C

/******************************************************************************/
/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de> */
/* This work has been released under the CC0 1.0 Universal license! */
/******************************************************************************/
/* Internal */
#include "../include/slunkcrypt.h"
#include "version.h"
/* CRT */
#include <string.h>
#include <limits.h>
#include <assert.h>
/* Intel(R) oneAPI DPC++/C++ Compiler */
#if defined(__INTEL_LLVM_COMPILER) && (!defined(__GNUC__))
# define __GNUC__ 9
#endif
/* Compiler compatibility */
#if defined(_MSC_VER) && (!defined(__GNUC__))
# define FORCE_INLINE __forceinline
# define UNUSED __pragma(warning(suppress: 4189))
#elif defined(__GNUC__)
# define FORCE_INLINE __attribute__((always_inline)) inline
# define UNUSED __attribute__((unused))
#else
# define FORCE_INLINE inline
# define UNUSED
#endif
/* Version info */
const uint16_t SLUNKCRYPT_VERSION_MAJOR = LIB_VERSION_MAJOR;
const uint16_t SLUNKCRYPT_VERSION_MINOR = LIB_VERSION_MINOR;
const uint16_t SLUNKCRYPT_VERSION_PATCH = LIB_VERSION_PATCH;
const char *const SLUNKCRYPT_BUILD = __DATE__ ", " __TIME__;
/* Utilities */
#define BOOLIFY(X) (!!(X))
// ==========================================================================
// Data structures
// ==========================================================================
typedef struct
{
uint64_t hi, lo;
}
uint128_t;
typedef struct
{
uint32_t x, y, z, w, v, d;
}
rand_state_t;
typedef struct
{
int reverse_mode;
uint8_t wheel[256U][256U];
uint32_t counter;
rand_state_t random;
}
crypt_state_t;
// ==========================================================================
// Abort flag
// ==========================================================================
volatile int g_slunkcrypt_abort_flag = 0;
#define CHECK_ABORTED() do \
{ \
if (g_slunkcrypt_abort_flag) \
{ \
goto aborted; \
} \
} \
while (0)
// ==========================================================================
// Byte access (endianness agnostic)
// ==========================================================================
static FORCE_INLINE uint32_t lower_u64(const uint64_t value)
{
return (uint32_t)(value & 0xFFFFFFFF);
}
static FORCE_INLINE uint32_t upper_u64(const uint64_t value)
{
return (uint32_t)(value >> 32U);
}
static FORCE_INLINE uint8_t byte_u64(const uint64_t value, const size_t off)
{
assert(off < sizeof(uint64_t));
return (uint8_t)((value >> (CHAR_BIT * off)) & 0xFF);
}
// ==========================================================================
// 128-Bit math support
// ==========================================================================
#define READ_U128(X) ((((__uint128_t)(X).hi) << 64U) | ((__uint128_t)(X).lo))
static FORCE_INLINE void multiply_u128(uint128_t *const out, const uint128_t lhs, const uint128_t rhs)
{
#if defined(__GNUC__) && defined(__SIZEOF_INT128__)
const __uint128_t tmp = READ_U128(lhs) * READ_U128(rhs);
out->hi = (uint64_t)(tmp >> 64U);
out->lo = (uint64_t)(tmp & 0xFFFFFFFFFFFFFFFF);
#else
const uint64_t lolo = (lhs.lo & 0xFFFFFFFF) * (rhs.lo & 0xFFFFFFFF);
const uint64_t hilo = (lhs.lo >> 32U) * (rhs.lo & 0xFFFFFFFF);
const uint64_t lohi = (lhs.lo & 0xFFFFFFFF) * (rhs.lo >> 32U);
const uint64_t hihi = (lhs.lo >> 32U) * (rhs.lo >> 32U);
const uint64_t crss = (lolo >> 32U) + (hilo & 0xFFFFFFFF) + lohi;
out->hi = (hilo >> 32U) + (crss >> 32) + hihi;
out->lo = (crss << 32U) | (lolo & 0xFFFFFFFF);
out->hi += (lhs.hi * rhs.lo) + (lhs.lo * rhs.hi); /* 128x128=128 */
#endif
}
// ==========================================================================
// Hash function
// ==========================================================================
static const uint128_t HASH_OFFSET_BASE = { 0x6C62272E07BB0142, 0x62B821756295C58D };
static const uint128_t HASH_MAGIC_PRIME = { 0x0000000001000000, 0x000000000000013B };
static FORCE_INLINE void hash_update_str(uint128_t *const hash, const uint8_t *const data, const size_t data_len)
{
size_t i;
for (i = 0U; i < data_len; ++i)
{
hash->lo ^= data[i];
multiply_u128(hash, *hash, HASH_MAGIC_PRIME);
}
}
static FORCE_INLINE void hash_update_u64(uint128_t *const hash, const uint64_t value)
{
size_t i;
for (i = 0U; i < sizeof(uint64_t); ++i)
{
hash->lo ^= byte_u64(value, i);
multiply_u128(hash, *hash, HASH_MAGIC_PRIME);
}
}
static uint64_t hash_code(const uint64_t salt, const uint8_t *const data, const size_t data_len)
{
uint128_t hash = HASH_OFFSET_BASE;
hash_update_u64(&hash, salt);
hash_update_str(&hash, data, data_len);
return hash.hi ^ hash.lo;
}
// ==========================================================================
// Key derivation
// ==========================================================================
static FORCE_INLINE uint64_t keygen_loop(uint64_t salt, const uint8_t *const passwd, const size_t passwd_len)
{
uint64_t result = 0U;
size_t u;
for (u = 0U; u < 99971U; ++u)
{
result ^= salt = hash_code(salt, passwd, passwd_len);
}
return result;
}
static void generate_key(uint64_t *const key, const uint64_t salt, const uint16_t pepper, const uint8_t *const passwd, const size_t passwd_len)
{
key[0U] = keygen_loop(0x162603FA1CDA99D3 + salt + pepper, passwd, passwd_len);
key[1U] = keygen_loop(0xBFDEC4A6C1A46E09 + salt + pepper, passwd, passwd_len);
key[2U] = keygen_loop(0x6BA17D11624973EE + salt + pepper, passwd, passwd_len);
}
// ==========================================================================
// Deterministic random bit generator
// ==========================================================================
static void random_init(rand_state_t *const state, const uint64_t *const key)
{
slunkcrypt_bzero(state, sizeof(rand_state_t));
state->x = lower_u64(key[0U]);
state->y = upper_u64(key[0U]);
state->z = lower_u64(key[1U]);
state->w = upper_u64(key[1U]);
state->v = lower_u64(key[2U]);
state->d = upper_u64(key[2U]);
}
static uint32_t random_next(rand_state_t *const state)
{
const uint32_t t = state->x ^ (state->x >> 2);
state->x = state->y;
state->y = state->z;
state->z = state->w;
state->w = state->v;
state->v ^= (state->v << 4) ^ t ^ (t << 1);
return (state->d += 0x000587C5) + state->v;
}
static 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)
{
size_t i;
uint64_t key[3U];
do
{
generate_key(key, salt++, pepper, passwd, passwd_len);
random_init(state, key);
slunkcrypt_bzero(&key, 3U * sizeof(uint64_t));
}
while (!(state->x || state->y || state->z || state->w || state->v));
for (i = 0U; i < 97U; ++i)
{
UNUSED volatile uint32_t q = random_next(state);
}
}
// ==========================================================================
// Initialization
// ==========================================================================
static int initialize_state(crypt_state_t *const state, const uint64_t nonce, const uint8_t *const passwd, const size_t passwd_len, const int mode)
{
uint8_t temp[256U][256U];
size_t r, i;
const int reverse = BOOLIFY(mode);
/* initialize state */
slunkcrypt_bzero(state, sizeof(crypt_state_t));
state->reverse_mode = reverse;
/* initialize counter */
random_seed(&state->random, nonce, (uint16_t)(-1), passwd, passwd_len);
state->counter = random_next(&state->random);
/* set up the wheel permutations */
for (r = 0U; r < 256U; ++r)
{
random_seed(&state->random, nonce, (uint16_t)r, passwd, passwd_len);
for (i = 0U; i < 256U; ++i)
{
const size_t j = random_next(&state->random) % (i + 1U);
if (j != i)
{
state->wheel[r][i] = state->wheel[r][j];
}
state->wheel[r][j] = (uint8_t)i;
}
CHECK_ABORTED();
}
/* reverse the wheels, if requested */
if (reverse)
{
for (r = 0U; r < 256U; ++r)
{
for (i = 0U; i < 256U; ++i)
{
temp[r][state->wheel[r][i]] = (uint8_t)i;
}
}
for (r = 0U; r < 256U; ++r)
{
memcpy(state->wheel[255U - r], temp[r], 256U);
}
slunkcrypt_bzero(temp, sizeof(temp));
CHECK_ABORTED();
}
random_seed(&state->random, nonce, 256U, passwd, passwd_len);
return SLUNKCRYPT_SUCCESS;
/* aborted */
aborted:
slunkcrypt_bzero(state, sizeof(crypt_state_t));
return SLUNKCRYPT_ABORTED;
}
// ==========================================================================
// Encrypt / Decrypt
// ==========================================================================
static FORCE_INLINE void update_offset(uint8_t *const offset, uint32_t seed, rand_state_t *const state, const int reverse)
{
size_t i;
for (i = 0U; i < 256U; ++i, seed >>= CHAR_BIT)
{
if (i && (!(i & 3U)))
{
seed = random_next(state);
}
offset[reverse ? (255U - i) : i] = (uint8_t)seed;
}
}
static FORCE_INLINE uint8_t process_next_symbol(crypt_state_t *const state, uint8_t value)
{
uint8_t offset[256U];
size_t i;
update_offset(offset, state->counter++, &state->random, state->reverse_mode);
for (i = 0U; i < 256U; ++i)
{
value = (state->wheel[i][(value + offset[i]) & 0xFF] - offset[i]) & 0xFF;
}
return value;
}
// ==========================================================================
// Public API
// ==========================================================================
int slunkcrypt_generate_nonce(uint64_t *const nonce)
{
if (!nonce)
{
return SLUNKCRYPT_FAILURE;
}
do
{
if (slunkcrypt_random_bytes((uint8_t*)nonce, sizeof(uint64_t)) != sizeof(uint64_t))
{
return SLUNKCRYPT_FAILURE;
}
}
while (!(*nonce));
return SLUNKCRYPT_SUCCESS;
}
slunkcrypt_t slunkcrypt_alloc(const uint64_t nonce, const uint8_t *const passwd, const size_t passwd_len, const int mode)
{
crypt_state_t* state = NULL;
if ((!passwd) || (passwd_len < SLUNKCRYPT_PWDLEN_MIN) || (passwd_len > SLUNKCRYPT_PWDLEN_MAX) || (mode < SLUNKCRYPT_ENCRYPT) || (mode > SLUNKCRYPT_DECRYPT))
{
return SLUNKCRYPT_NULL;
}
if (!(state = (crypt_state_t*)malloc(sizeof(crypt_state_t))))
{
return SLUNKCRYPT_NULL;
}
if (initialize_state(state, nonce, passwd, passwd_len, mode) == SLUNKCRYPT_SUCCESS)
{
return ((slunkcrypt_t)state);
}
else
{
slunkcrypt_bzero(state, sizeof(crypt_state_t));
return SLUNKCRYPT_NULL;
}
}
int slunkcrypt_reset(const slunkcrypt_t context, const uint64_t nonce, const uint8_t *const passwd, const size_t passwd_len, const int mode)
{
crypt_state_t *const state = (crypt_state_t*)context;
int result = SLUNKCRYPT_FAILURE;
if ((!state) || (!passwd) || (passwd_len < SLUNKCRYPT_PWDLEN_MIN) || (passwd_len > SLUNKCRYPT_PWDLEN_MAX) || (mode < SLUNKCRYPT_ENCRYPT) || (mode > SLUNKCRYPT_DECRYPT))
{
return SLUNKCRYPT_FAILURE;
}
if ((result = initialize_state(state, nonce, passwd, passwd_len, mode)) != SLUNKCRYPT_SUCCESS)
{
slunkcrypt_bzero(state, sizeof(crypt_state_t));
}
return result;
}
int slunkcrypt_process(const slunkcrypt_t context, const uint8_t *const input, uint8_t *const output, size_t length)
{
crypt_state_t *const state = (crypt_state_t*)context;
if (!state)
{
return SLUNKCRYPT_FAILURE;
}
if (length > 0U)
{
size_t i;
for (i = 0; i < length; ++i)
{
output[i] = process_next_symbol(state, input[i]);
CHECK_ABORTED();
}
}
return SLUNKCRYPT_SUCCESS;
aborted:
slunkcrypt_bzero(state, sizeof(crypt_state_t));
return SLUNKCRYPT_ABORTED;
}
int slunkcrypt_inplace(const slunkcrypt_t context, uint8_t *const buffer, size_t length)
{
crypt_state_t *const state = (crypt_state_t*)context;
if (!state)
{
return SLUNKCRYPT_FAILURE;
}
if (length > 0U)
{
size_t i;
for (i = 0; i < length; ++i)
{
buffer[i] = process_next_symbol(state, buffer[i]);
CHECK_ABORTED();
}
}
return SLUNKCRYPT_SUCCESS;
aborted:
slunkcrypt_bzero(state, sizeof(crypt_state_t));
return SLUNKCRYPT_ABORTED;
}
void slunkcrypt_free(const slunkcrypt_t context)
{
crypt_state_t *const state = (crypt_state_t*)context;
if (state)
{
slunkcrypt_bzero(state, sizeof(crypt_state_t));
free(state);
}
}