lokinet/crypto/libsodium/init.c
2018-11-13 10:16:14 -05:00

230 lines
3.7 KiB
C

#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#ifdef _WIN32
#include <windows.h>
#elif defined(HAVE_PTHREAD)
#include <pthread.h>
#endif
#include <sodium/core.h>
#include <sodium/crypto_generichash.h>
#include <sodium/crypto_scalarmult.h>
#include <sodium/crypto_stream_chacha20.h>
#include <sodium/crypto_stream_salsa20.h>
#include <sodium/randombytes.h>
#include <sodium/runtime.h>
#include <sodium/utils.h>
#include <sodium/private/implementations.h>
#include <sodium/private/mutex.h>
static volatile int initialized;
int
sodium_init(void)
{
if(sodium_crit_enter() != 0)
{
return -1; /* LCOV_EXCL_LINE */
}
if(initialized != 0)
{
if(sodium_crit_leave() != 0)
{
return -1; /* LCOV_EXCL_LINE */
}
return 1;
}
_sodium_runtime_get_cpu_features();
_crypto_generichash_blake2b_pick_best_implementation();
_crypto_scalarmult_curve25519_pick_best_implementation();
_crypto_stream_chacha20_pick_best_implementation();
_crypto_stream_salsa20_pick_best_implementation();
randombytes_stir();
initialized = 1;
if(sodium_crit_leave() != 0)
{
return -1; /* LCOV_EXCL_LINE */
}
return initialized ? 0 : -1;
}
#ifdef _WIN32
static CRITICAL_SECTION _sodium_lock;
static volatile LONG _sodium_lock_initialized;
static volatile int locked;
int
_sodium_crit_init(void)
{
LONG status = 0L;
while((status = InterlockedCompareExchange(&_sodium_lock_initialized, 1L, 0L))
== 1L)
{
Sleep(0);
}
switch(status)
{
case 0L:
InitializeCriticalSection(&_sodium_lock);
return InterlockedExchange(&_sodium_lock_initialized, 2L) == 1L ? 0 : -1;
case 2L:
return 0;
default: /* should never be reached */
return -1;
}
}
int
sodium_crit_enter(void)
{
if(_sodium_crit_init() != 0)
{
return -1; /* LCOV_EXCL_LINE */
}
EnterCriticalSection(&_sodium_lock);
assert(locked == 0);
locked = 1;
return 0;
}
int
sodium_crit_leave(void)
{
if(locked == 0)
{
#ifdef EPERM
errno = EPERM;
#endif
return -1;
}
locked = 0;
LeaveCriticalSection(&_sodium_lock);
return 0;
}
#elif defined(HAVE_PTHREAD) && !defined(__EMSCRIPTEN__)
static pthread_mutex_t _sodium_lock = PTHREAD_MUTEX_INITIALIZER;
static volatile int locked;
int
sodium_crit_enter(void)
{
int ret;
if((ret = pthread_mutex_lock(&_sodium_lock)) == 0)
{
assert(locked == 0);
locked = 1;
}
return ret;
}
int
sodium_crit_leave(void)
{
int ret;
if(locked == 0)
{
#ifdef EPERM
errno = EPERM;
#endif
return -1;
}
locked = 0;
return pthread_mutex_unlock(&_sodium_lock);
}
#elif defined(HAVE_ATOMIC_OPS) && !defined(__EMSCRIPTEN__) \
&& !defined(__native_client__)
static volatile int _sodium_lock;
int
sodium_crit_enter(void)
{
#ifdef HAVE_NANOSLEEP
struct timespec q;
memset(&q, 0, sizeof q);
#endif
while(__sync_lock_test_and_set(&_sodium_lock, 1) != 0)
{
#ifdef HAVE_NANOSLEEP
(void)nanosleep(&q, NULL);
#elif defined(__x86_64__) || defined(__i386__)
__asm__ __volatile__("pause");
#endif
}
return 0;
}
int
sodium_crit_leave(void)
{
__sync_lock_release(&_sodium_lock);
return 0;
}
#else
int
sodium_crit_enter(void)
{
return 0;
}
int
sodium_crit_leave(void)
{
return 0;
}
#endif
static void (*_misuse_handler)(void);
void
sodium_misuse(void)
{
void (*handler)(void);
(void)sodium_crit_leave();
if(sodium_crit_enter() == 0)
{
handler = _misuse_handler;
if(handler != NULL)
{
handler();
}
}
/* LCOV_EXCL_START */
abort();
}
/* LCOV_EXCL_STOP */
int
sodium_set_misuse_handler(void (*handler)(void))
{
if(sodium_crit_enter() != 0)
{
return -1; /* LCOV_EXCL_LINE */
}
_misuse_handler = handler;
if(sodium_crit_leave() != 0)
{
return -1; /* LCOV_EXCL_LINE */
}
return 0;
}