#include #include #include #include #include #include #include #include #if !defined(_MSC_VER) && !defined(__BORLANDC__) #include #endif #include #ifndef _WIN32 #include #include #endif #ifdef __linux__ #ifdef __dietlibc__ #define _LINUX_SOURCE #else #include #endif #include #endif #ifdef HAVE_RDRAND #pragma GCC target("rdrnd") #include #endif #include #include #include #include #include #include #include #include #ifdef _WIN32 #include #include #include #include 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};