432 lines
12 KiB
C
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);
|
|
}
|
|
}
|