#include #include #include #include #ifdef _WIN32 # include #elif defined(HAVE_PTHREAD) # include #endif #include "core.h" #include "crypto_generichash.h" #include "crypto_onetimeauth.h" #include "crypto_scalarmult.h" #include "crypto_stream_chacha20.h" #include "crypto_stream_salsa20.h" #include "randombytes.h" #include "runtime.h" #include "utils.h" #include "private/implementations.h" #include "private/mutex.h" static volatile int initialized; static volatile int locked; 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(); randombytes_stir(); _sodium_alloc_init(); _crypto_pwhash_argon2_pick_best_implementation(); _crypto_generichash_blake2b_pick_best_implementation(); _crypto_onetimeauth_poly1305_pick_best_implementation(); _crypto_scalarmult_curve25519_pick_best_implementation(); _crypto_stream_chacha20_pick_best_implementation(); _crypto_stream_salsa20_pick_best_implementation(); initialized = 1; if (sodium_crit_leave() != 0) { return -1; /* LCOV_EXCL_LINE */ } return 0; } #ifdef _WIN32 static CRITICAL_SECTION _sodium_lock; static volatile LONG _sodium_lock_initialized; 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; 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; }