mirror of
https://github.com/oxen-io/lokinet.git
synced 2024-11-17 15:25:35 +00:00
637 lines
14 KiB
C
637 lines
14 KiB
C
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <limits.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#if !defined(_MSC_VER) && !defined(__BORLANDC__)
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include <sys/types.h>
|
|
#ifndef _WIN32
|
|
#include <sys/stat.h>
|
|
#include <sys/time.h>
|
|
#endif
|
|
#ifdef __linux__
|
|
#ifdef __dietlibc__
|
|
#define _LINUX_SOURCE
|
|
#else
|
|
#include <sys/syscall.h>
|
|
#endif
|
|
#include <poll.h>
|
|
#endif
|
|
#ifdef HAVE_RDRAND
|
|
#pragma GCC target("rdrnd")
|
|
#include <immintrin.h>
|
|
#endif
|
|
|
|
#include <sodium/core.h>
|
|
#include <sodium/crypto_core_salsa20.h>
|
|
#include <sodium/crypto_stream_salsa20.h>
|
|
#include <sodium/private/common.h>
|
|
#include <sodium/randombytes.h>
|
|
#include <sodium/randombytes_salsa20_random.h>
|
|
#include <sodium/runtime.h>
|
|
#include <sodium/utils.h>
|
|
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#include <sys/timeb.h>
|
|
#include <wincrypt.h>
|
|
#include <bcrypt.h>
|
|
typedef NTSTATUS(FAR PASCAL *CNGAPI_DRBG)(BCRYPT_ALG_HANDLE, UCHAR *, ULONG,
|
|
ULONG);
|
|
#ifdef __BORLANDC__
|
|
#define _ftime ftime
|
|
#define _timeb timeb
|
|
#endif
|
|
#endif
|
|
|
|
#define SALSA20_RANDOM_BLOCK_SIZE crypto_core_salsa20_OUTPUTBYTES
|
|
|
|
#if defined(__OpenBSD__) || defined(__CloudABI__)
|
|
#define HAVE_SAFE_ARC4RANDOM 1
|
|
#endif
|
|
|
|
#ifndef SSIZE_MAX
|
|
#define SSIZE_MAX (SIZE_MAX / 2 - 1)
|
|
#endif
|
|
#ifndef S_ISNAM
|
|
#ifdef __COMPCERT__
|
|
#define S_ISNAM(X) 1
|
|
#else
|
|
#define S_ISNAM(X) 0
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef TLS
|
|
#ifdef _WIN32
|
|
#define TLS __thread
|
|
#else
|
|
#define TLS
|
|
#endif
|
|
#endif
|
|
|
|
typedef struct Salsa20RandomGlobal_
|
|
{
|
|
int initialized;
|
|
int random_data_source_fd;
|
|
int getrandom_available;
|
|
int rdrand_available;
|
|
#ifdef HAVE_GETPID
|
|
pid_t pid;
|
|
#endif
|
|
} Salsa20RandomGlobal;
|
|
|
|
typedef struct Salsa20Random_
|
|
{
|
|
int initialized;
|
|
size_t rnd32_outleft;
|
|
unsigned char key[crypto_stream_salsa20_KEYBYTES];
|
|
unsigned char rnd32[16U * SALSA20_RANDOM_BLOCK_SIZE];
|
|
uint64_t nonce;
|
|
} Salsa20Random;
|
|
|
|
static Salsa20RandomGlobal global = {
|
|
SODIUM_C99(.initialized =) 0,
|
|
SODIUM_C99(.random_data_source_fd =) - 1,
|
|
SODIUM_C99(.getrandom_available =) 0,
|
|
SODIUM_C99(.rdrand_available =) 0,
|
|
|
|
};
|
|
|
|
static TLS Salsa20Random stream = {
|
|
SODIUM_C99(.initialized =) 0, SODIUM_C99(.rnd32_outleft =)(size_t) 0U,
|
|
SODIUM_C99(.key =){0}, SODIUM_C99(.rnd32 =){0},
|
|
SODIUM_C99(.nonce =) 1,
|
|
};
|
|
|
|
/*
|
|
* Get a high-resolution timestamp, as a uint64_t value
|
|
*/
|
|
|
|
#ifdef _WIN32
|
|
static uint64_t
|
|
sodium_hrtime(void)
|
|
{
|
|
struct _timeb tb;
|
|
_ftime(&tb);
|
|
return ((uint64_t)tb.time) * 1000000U + ((uint64_t)tb.millitm) * 1000U;
|
|
}
|
|
|
|
#else /* _WIN32 */
|
|
|
|
static uint64_t
|
|
sodium_hrtime(void)
|
|
{
|
|
struct timeval tv;
|
|
|
|
if(gettimeofday(&tv, NULL) != 0)
|
|
{
|
|
sodium_misuse(); /* LCOV_EXCL_LINE */
|
|
}
|
|
return ((uint64_t)tv.tv_sec) * 1000000U + (uint64_t)tv.tv_usec;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Initialize the entropy source
|
|
*/
|
|
|
|
#ifdef _WIN32
|
|
|
|
static void
|
|
randombytes_salsa20_random_init(void)
|
|
{
|
|
stream.nonce = sodium_hrtime();
|
|
assert(stream.nonce != (uint64_t)0U);
|
|
global.rdrand_available = sodium_runtime_has_rdrand();
|
|
}
|
|
|
|
#else /* _WIN32 */
|
|
|
|
static ssize_t
|
|
safe_read(const int fd, void *const buf_, size_t size)
|
|
{
|
|
unsigned char *buf = (unsigned char *)buf_;
|
|
ssize_t readnb;
|
|
|
|
assert(size > (size_t)0U);
|
|
assert(size <= SSIZE_MAX);
|
|
do
|
|
{
|
|
while((readnb = read(fd, buf, size)) < (ssize_t)0
|
|
&& (errno == EINTR || errno == EAGAIN))
|
|
; /* LCOV_EXCL_LINE */
|
|
if(readnb < (ssize_t)0)
|
|
{
|
|
return readnb; /* LCOV_EXCL_LINE */
|
|
}
|
|
if(readnb == (ssize_t)0)
|
|
{
|
|
break; /* LCOV_EXCL_LINE */
|
|
}
|
|
size -= (size_t)readnb;
|
|
buf += readnb;
|
|
} while(size > (ssize_t)0);
|
|
|
|
return (ssize_t)(buf - (unsigned char *)buf_);
|
|
}
|
|
|
|
#if defined(__linux__) && !defined(USE_BLOCKING_RANDOM) \
|
|
&& !defined(NO_BLOCKING_RANDOM_POLL)
|
|
static int
|
|
randombytes_block_on_dev_random(void)
|
|
{
|
|
struct pollfd pfd;
|
|
int fd;
|
|
int pret;
|
|
|
|
fd = open("/dev/random", O_RDONLY);
|
|
if(fd == -1)
|
|
{
|
|
return 0;
|
|
}
|
|
pfd.fd = fd;
|
|
pfd.events = POLLIN;
|
|
pfd.revents = 0;
|
|
do
|
|
{
|
|
pret = poll(&pfd, 1, -1);
|
|
} while(pret < 0 && (errno == EINTR || errno == EAGAIN));
|
|
if(pret != 1)
|
|
{
|
|
(void)close(fd);
|
|
errno = EIO;
|
|
return -1;
|
|
}
|
|
return close(fd);
|
|
}
|
|
#endif
|
|
|
|
#ifndef HAVE_SAFE_ARC4RANDOM
|
|
static int
|
|
randombytes_salsa20_random_random_dev_open(void)
|
|
{
|
|
/* LCOV_EXCL_START */
|
|
struct stat st;
|
|
static const char *devices[] = {
|
|
#ifndef USE_BLOCKING_RANDOM
|
|
"/dev/urandom",
|
|
#endif
|
|
"/dev/random", NULL};
|
|
const char **device = devices;
|
|
int fd;
|
|
|
|
#if defined(__linux__) && !defined(USE_BLOCKING_RANDOM) \
|
|
&& !defined(NO_BLOCKING_RANDOM_POLL)
|
|
if(randombytes_block_on_dev_random() != 0)
|
|
{
|
|
return -1;
|
|
}
|
|
#endif
|
|
do
|
|
{
|
|
fd = open(*device, O_RDONLY);
|
|
if(fd != -1)
|
|
{
|
|
if(fstat(fd, &st) == 0 && (S_ISNAM(st.st_mode) || S_ISCHR(st.st_mode)))
|
|
{
|
|
#if defined(F_SETFD) && defined(FD_CLOEXEC)
|
|
(void)fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
|
|
#endif
|
|
return fd;
|
|
}
|
|
(void)close(fd);
|
|
}
|
|
else if(errno == EINTR)
|
|
{
|
|
continue;
|
|
}
|
|
device++;
|
|
} while(*device != NULL);
|
|
|
|
errno = EIO;
|
|
return -1;
|
|
/* LCOV_EXCL_STOP */
|
|
}
|
|
#endif
|
|
|
|
#if defined(__dietlibc__) || (defined(SYS_getrandom) && defined(__NR_getrandom))
|
|
static int
|
|
_randombytes_linux_getrandom(void *const buf, const size_t size)
|
|
{
|
|
int readnb;
|
|
|
|
assert(size <= 256U);
|
|
do
|
|
{
|
|
#ifdef __dietlibc__
|
|
readnb = getrandom(buf, size, 0);
|
|
#else
|
|
readnb = syscall(SYS_getrandom, buf, (int)size, 0);
|
|
#endif
|
|
} while(readnb < 0 && (errno == EINTR || errno == EAGAIN));
|
|
|
|
return (readnb == (int)size) - 1;
|
|
}
|
|
|
|
static int
|
|
randombytes_linux_getrandom(void *const buf_, size_t size)
|
|
{
|
|
unsigned char *buf = (unsigned char *)buf_;
|
|
size_t chunk_size = 256U;
|
|
|
|
do
|
|
{
|
|
if(size < chunk_size)
|
|
{
|
|
chunk_size = size;
|
|
assert(chunk_size > (size_t)0U);
|
|
}
|
|
if(_randombytes_linux_getrandom(buf, chunk_size) != 0)
|
|
{
|
|
return -1;
|
|
}
|
|
size -= chunk_size;
|
|
buf += chunk_size;
|
|
} while(size > (size_t)0U);
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
randombytes_salsa20_random_init(void)
|
|
{
|
|
const int errno_save = errno;
|
|
|
|
stream.nonce = sodium_hrtime();
|
|
global.rdrand_available = sodium_runtime_has_rdrand();
|
|
assert(stream.nonce != (uint64_t)0U);
|
|
|
|
#ifdef HAVE_SAFE_ARC4RANDOM
|
|
errno = errno_save;
|
|
#else
|
|
|
|
#if defined(SYS_getrandom) && defined(__NR_getrandom)
|
|
{
|
|
unsigned char fodder[16];
|
|
|
|
if(randombytes_linux_getrandom(fodder, sizeof fodder) == 0)
|
|
{
|
|
global.getrandom_available = 1;
|
|
errno = errno_save;
|
|
return;
|
|
}
|
|
global.getrandom_available = 0;
|
|
}
|
|
#endif /* SYS_getrandom */
|
|
|
|
if((global.random_data_source_fd =
|
|
randombytes_salsa20_random_random_dev_open())
|
|
== -1)
|
|
{
|
|
sodium_misuse(); /* LCOV_EXCL_LINE */
|
|
}
|
|
errno = errno_save;
|
|
#endif /* HAVE_SAFE_ARC4RANDOM */
|
|
}
|
|
|
|
#endif /* _WIN32 */
|
|
|
|
/*
|
|
* (Re)seed the generator using the entropy source
|
|
*/
|
|
|
|
static void
|
|
randombytes_salsa20_random_stir(void)
|
|
{
|
|
unsigned char
|
|
m0[crypto_stream_salsa20_KEYBYTES + crypto_stream_salsa20_NONCEBYTES];
|
|
|
|
memset(stream.rnd32, 0, sizeof stream.rnd32);
|
|
stream.rnd32_outleft = (size_t)0U;
|
|
if(global.initialized == 0)
|
|
{
|
|
randombytes_salsa20_random_init();
|
|
global.initialized = 1;
|
|
}
|
|
#ifdef HAVE_GETPID
|
|
global.pid = getpid();
|
|
#endif
|
|
|
|
#ifndef _WIN32
|
|
|
|
#ifdef HAVE_SAFE_ARC4RANDOM
|
|
arc4random_buf(m0, sizeof m0);
|
|
#elif defined(SYS_getrandom) && defined(__NR_getrandom)
|
|
if(global.getrandom_available != 0)
|
|
{
|
|
if(randombytes_linux_getrandom(m0, sizeof m0) != 0)
|
|
{
|
|
sodium_misuse(); /* LCOV_EXCL_LINE */
|
|
}
|
|
}
|
|
else if(global.random_data_source_fd == -1
|
|
|| safe_read(global.random_data_source_fd, m0, sizeof m0)
|
|
!= (ssize_t)sizeof m0)
|
|
{
|
|
sodium_misuse(); /* LCOV_EXCL_LINE */
|
|
}
|
|
#else
|
|
if(global.random_data_source_fd == -1
|
|
|| safe_read(global.random_data_source_fd, m0, sizeof m0)
|
|
!= (ssize_t)sizeof m0)
|
|
{
|
|
sodium_misuse(); /* LCOV_EXCL_LINE */
|
|
}
|
|
#endif
|
|
|
|
#else /* _WIN32 */
|
|
HANDLE hCAPINg;
|
|
BOOL rtld;
|
|
CNGAPI_DRBG getrandom;
|
|
HCRYPTPROV hProv;
|
|
/* load bcrypt dynamically, see if we're already loaded */
|
|
rtld = FALSE;
|
|
hCAPINg = GetModuleHandle("bcrypt.dll");
|
|
/* otherwise, load CNG manually */
|
|
if(!hCAPINg)
|
|
{
|
|
hCAPINg = LoadLibraryEx("bcrypt.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
|
|
rtld = TRUE;
|
|
}
|
|
if(hCAPINg)
|
|
{
|
|
/* call BCryptGenRandom(2) */
|
|
getrandom = (CNGAPI_DRBG)GetProcAddress(hCAPINg, "BCryptGenRandom");
|
|
if(!BCRYPT_SUCCESS(
|
|
getrandom(NULL, m0, sizeof m0, BCRYPT_USE_SYSTEM_PREFERRED_RNG)))
|
|
{
|
|
sodium_misuse();
|
|
}
|
|
/* don't leak lib refs */
|
|
if(rtld)
|
|
FreeLibrary(hCAPINg);
|
|
}
|
|
/* if that fails use the regular ARC4-SHA1 RNG (!!!) *cringes* */
|
|
else
|
|
{
|
|
CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL,
|
|
CRYPT_VERIFYCONTEXT | CRYPT_SILENT);
|
|
if(!CryptGenRandom(hProv, sizeof m0, m0))
|
|
{
|
|
sodium_misuse(); /* LCOV_EXCL_LINE */
|
|
}
|
|
CryptReleaseContext(hProv, 0);
|
|
}
|
|
#endif
|
|
|
|
crypto_stream_salsa20(stream.key, sizeof stream.key,
|
|
m0 + crypto_stream_salsa20_KEYBYTES, m0);
|
|
sodium_memzero(m0, sizeof m0);
|
|
stream.initialized = 1;
|
|
}
|
|
|
|
/*
|
|
* Reseed the generator if it hasn't been initialized yet
|
|
*/
|
|
|
|
static void
|
|
randombytes_salsa20_random_stir_if_needed(void)
|
|
{
|
|
#ifdef HAVE_GETPID
|
|
if(stream.initialized == 0)
|
|
{
|
|
randombytes_salsa20_random_stir();
|
|
}
|
|
else if(global.pid != getpid())
|
|
{
|
|
sodium_misuse(); /* LCOV_EXCL_LINE */
|
|
}
|
|
#else
|
|
if(stream.initialized == 0)
|
|
{
|
|
randombytes_salsa20_random_stir();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Close the stream, free global resources
|
|
*/
|
|
|
|
#ifdef _WIN32
|
|
static int
|
|
randombytes_salsa20_random_close(void)
|
|
{
|
|
int ret = -1;
|
|
|
|
if(global.initialized != 0)
|
|
{
|
|
global.initialized = 0;
|
|
ret = 0;
|
|
}
|
|
sodium_memzero(&stream, sizeof stream);
|
|
|
|
return ret;
|
|
}
|
|
#else
|
|
static int
|
|
randombytes_salsa20_random_close(void)
|
|
{
|
|
int ret = -1;
|
|
|
|
if(global.random_data_source_fd != -1
|
|
&& close(global.random_data_source_fd) == 0)
|
|
{
|
|
global.random_data_source_fd = -1;
|
|
global.initialized = 0;
|
|
#ifdef HAVE_GETPID
|
|
global.pid = (pid_t)0;
|
|
#endif
|
|
ret = 0;
|
|
}
|
|
|
|
#ifdef HAVE_SAFE_ARC4RANDOM
|
|
ret = 0;
|
|
#endif
|
|
|
|
#if defined(SYS_getrandom) && defined(__NR_getrandom)
|
|
if(global.getrandom_available != 0)
|
|
{
|
|
ret = 0;
|
|
}
|
|
#endif
|
|
|
|
sodium_memzero(&stream, sizeof stream);
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* RDRAND is only used to mitigate prediction if a key is compromised
|
|
*/
|
|
|
|
static void
|
|
randombytes_salsa20_random_xorhwrand(void)
|
|
{
|
|
/* LCOV_EXCL_START */
|
|
#ifdef HAVE_RDRAND
|
|
unsigned int r;
|
|
|
|
if(global.rdrand_available == 0)
|
|
{
|
|
return;
|
|
}
|
|
(void)_rdrand32_step(&r);
|
|
*(uint32_t *)(void *)&stream.key[crypto_stream_salsa20_KEYBYTES - 4] ^=
|
|
(uint32_t)r;
|
|
#endif
|
|
/* LCOV_EXCL_STOP */
|
|
}
|
|
|
|
/*
|
|
* XOR the key with another same-length secret
|
|
*/
|
|
|
|
static inline void
|
|
randombytes_salsa20_random_xorkey(const unsigned char *const mix)
|
|
{
|
|
unsigned char *key = stream.key;
|
|
size_t i;
|
|
|
|
for(i = (size_t)0U; i < sizeof stream.key; i++)
|
|
{
|
|
key[i] ^= mix[i];
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Put `size` random bytes into `buf` and overwrite the key
|
|
*/
|
|
|
|
static void
|
|
randombytes_salsa20_random_buf(void *const buf, const size_t size)
|
|
{
|
|
size_t i;
|
|
int ret;
|
|
|
|
randombytes_salsa20_random_stir_if_needed();
|
|
COMPILER_ASSERT(sizeof stream.nonce == crypto_stream_salsa20_NONCEBYTES);
|
|
#if defined(ULONG_LONG_MAX) && defined(SIZE_MAX)
|
|
#if SIZE_MAX > ULONG_LONG_MAX
|
|
/* coverity[result_independent_of_operands] */
|
|
assert(size <= ULONG_LONG_MAX);
|
|
#endif
|
|
#endif
|
|
ret = crypto_stream_salsa20((unsigned char *)buf, (unsigned long long)size,
|
|
(unsigned char *)&stream.nonce, stream.key);
|
|
assert(ret == 0);
|
|
for(i = 0U; i < sizeof size; i++)
|
|
{
|
|
stream.key[i] ^= ((const unsigned char *)(const void *)&size)[i];
|
|
}
|
|
randombytes_salsa20_random_xorhwrand();
|
|
stream.nonce++;
|
|
crypto_stream_salsa20_xor(stream.key, stream.key, sizeof stream.key,
|
|
(unsigned char *)&stream.nonce, stream.key);
|
|
(void)ret;
|
|
}
|
|
|
|
/*
|
|
* Pop a 32-bit value from the random pool
|
|
*
|
|
* Overwrite the key after the pool gets refilled.
|
|
*/
|
|
|
|
static uint32_t
|
|
randombytes_salsa20_random(void)
|
|
{
|
|
uint32_t val;
|
|
int ret;
|
|
|
|
COMPILER_ASSERT(sizeof stream.rnd32 >= (sizeof stream.key) + (sizeof val));
|
|
COMPILER_ASSERT(((sizeof stream.rnd32) - (sizeof stream.key)) % sizeof val
|
|
== (size_t)0U);
|
|
if(stream.rnd32_outleft <= (size_t)0U)
|
|
{
|
|
randombytes_salsa20_random_stir_if_needed();
|
|
COMPILER_ASSERT(sizeof stream.nonce == crypto_stream_salsa20_NONCEBYTES);
|
|
ret = crypto_stream_salsa20((unsigned char *)stream.rnd32,
|
|
(unsigned long long)sizeof stream.rnd32,
|
|
(unsigned char *)&stream.nonce, stream.key);
|
|
assert(ret == 0);
|
|
stream.rnd32_outleft = (sizeof stream.rnd32) - (sizeof stream.key);
|
|
randombytes_salsa20_random_xorhwrand();
|
|
randombytes_salsa20_random_xorkey(&stream.rnd32[stream.rnd32_outleft]);
|
|
memset(&stream.rnd32[stream.rnd32_outleft], 0, sizeof stream.key);
|
|
stream.nonce++;
|
|
}
|
|
stream.rnd32_outleft -= sizeof val;
|
|
memcpy(&val, &stream.rnd32[stream.rnd32_outleft], sizeof val);
|
|
memset(&stream.rnd32[stream.rnd32_outleft], 0, sizeof val);
|
|
(void)ret;
|
|
return val;
|
|
}
|
|
|
|
static const char *
|
|
randombytes_salsa20_implementation_name(void)
|
|
{
|
|
return "salsa20";
|
|
}
|
|
|
|
struct randombytes_implementation randombytes_salsa20_implementation = {
|
|
SODIUM_C99(.implementation_name =) randombytes_salsa20_implementation_name,
|
|
SODIUM_C99(.random =) randombytes_salsa20_random,
|
|
SODIUM_C99(.stir =) randombytes_salsa20_random_stir,
|
|
SODIUM_C99(.uniform =) NULL,
|
|
SODIUM_C99(.buf =) randombytes_salsa20_random_buf,
|
|
SODIUM_C99(.close =) randombytes_salsa20_random_close};
|