diff --git a/src/3rdparty/CMakeLists.txt b/src/3rdparty/CMakeLists.txt index f3d648568c..29ace8dc50 100644 --- a/src/3rdparty/CMakeLists.txt +++ b/src/3rdparty/CMakeLists.txt @@ -5,3 +5,4 @@ add_subdirectory(os2) add_subdirectory(cpp-btree) add_subdirectory(mingw-std-threads) add_subdirectory(monocypher) +add_subdirectory(randombytes) diff --git a/src/3rdparty/randombytes/CMakeLists.txt b/src/3rdparty/randombytes/CMakeLists.txt new file mode 100644 index 0000000000..66592e13dc --- /dev/null +++ b/src/3rdparty/randombytes/CMakeLists.txt @@ -0,0 +1,4 @@ +add_files( + randombytes.h + randombytes.c +) diff --git a/src/3rdparty/randombytes/LICENSE b/src/3rdparty/randombytes/LICENSE new file mode 100644 index 0000000000..08e9c1ecae --- /dev/null +++ b/src/3rdparty/randombytes/LICENSE @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) 2017 Daan Sprenkels + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/src/3rdparty/randombytes/README.md b/src/3rdparty/randombytes/README.md new file mode 100644 index 0000000000..b5a62a8847 --- /dev/null +++ b/src/3rdparty/randombytes/README.md @@ -0,0 +1,88 @@ +# Pluggable randombytes function + +[![Travis-CI](https://travis-ci.org/dsprenkels/sss.svg?branch=master)](https://travis-ci.org/dsprenkels/randombytes) +[![Appveyor](https://ci.appveyor.com/api/projects/status/github/dsprenkels/randombytes?branch=master&svg=true)](https://ci.appveyor.com/project/dsprenkels/randombytes) + +`randombytes` is a library that exposes a single function for retrieving +_crypto-secure_ random bytes. It is loosely based on [Libsodium's random bytes +API][libsodium_randombytes]. If you can, you should use that one. Otherwise, you +can use this library. + +## Usage + +`randombytes` takes two arguments: +1. A pointer to the buffer +2. The length of the buffer in bytes + +The function will always return an `int` which will be `0` on success. The +caller _must_ check this. If some kind of error occured, `errno` MAY contain a +hint to what the error was, and a subsequent call to the `randombytes` function +MAY succeed. An example of when the function may fail is when `/dev/urandom` +could not be opened, because there were no file descriptors left to use for the +process. + +On sensible systems (like the ones with `arc4random`) the latency is very low. +However, this is totally not guaranteed. Do not expect this function to be very +fast. Benchmark for your specific setup, and use a fast CSPRNG if you need. + +Example code: + +```c +#include "randombytes.h" +#include +#include + +int main() +{ + // Generate some random bytes and print them in hex + int ret; + uint8_t buf[20]; + size_t i; + + ret = randombytes(&buf[0], sizeof(buf)); + if (ret != 0) { + printf("Error in `randombytes`"); + return 1; + } + for (i = 0; i < sizeof(buf); ++i) { + printf("%02hhx", buf[i]); + } + printf("\n"); + return 0; +} +``` + +## How secure is it really? + +While building this I keep one rule of thumb which is: **Trust the OS**. +Most OS'es implement a secure random generator, which is seeded by a good +entropy source. We will always use this random source. This essentially means +that the implementation is highly platform-dependent. For example we use +`getrandom` on Linux and `arc4random` on BSD systems. + +### What if the OS's random generator is bad? + +If you are dealing with an OS that has a compromised random generator you are +out of luck. The reason why you cannot generate high quality random data from +userspace is that userspace is made so that everything is too deterministic. +A secure random generator needs a good source of entropy, such as 2.4 GHz noise +or the user's mouse movements. Collecting these kinds of events only works well +when working on the lowest level. + +## Questions + +### It does not compile on my platform! + +[Please open an issue.](https://github.com/dsprenkels/randombytes/issues/new) +If possible I will try to make a `randombytes` implementation for your platform. + +### Do you have bindings for language _x_? + +No, your language probably already has a random source. Use that one. + +### Other + +Feel free to send me an email on my Github associated e-mail address. + + +[libsodium_randombytes]: https://github.com/jedisct1/libsodium/blob/master/src/libsodium/randombytes/sysrandom/randombytes_sysrandom.c diff --git a/src/3rdparty/randombytes/randombytes.c b/src/3rdparty/randombytes/randombytes.c new file mode 100644 index 0000000000..4364798f37 --- /dev/null +++ b/src/3rdparty/randombytes/randombytes.c @@ -0,0 +1,339 @@ +// In the case that are compiling on linux, we need to define _GNU_SOURCE +// *before* randombytes.h is included. Otherwise SYS_getrandom will not be +// declared. +#if defined(__linux__) +# define _GNU_SOURCE +#endif /* defined(__linux__) */ + +#include "randombytes.h" + +#if defined(_WIN32) +/* Windows */ +# include +# include /* CryptAcquireContext, CryptGenRandom */ +#endif /* defined(_WIN32) */ + +/* wasi */ +#if defined(__wasi__) +#include +#endif + +#if defined(__linux__) +/* Linux */ +// We would need to include , but not every target has access +// to the linux headers. We only need RNDGETENTCNT, so we instead inline it. +// RNDGETENTCNT is originally defined in `include/uapi/linux/random.h` in the +// linux repo. +# define RNDGETENTCNT 0x80045200 + +# include +# include +# include +# include +# include +# include +# include +# if defined(__linux__) && defined(__GLIBC__) && ((__GLIBC__ > 2) || (__GLIBC_MINOR__ > 24)) +# define USE_GLIBC +# include +# endif /* defined(__linux__) && defined(__GLIBC__) && ((__GLIBC__ > 2) || (__GLIBC_MINOR__ > 24)) */ +# include +# include +# include +# include + +// We need SSIZE_MAX as the maximum read len from /dev/urandom +# if !defined(SSIZE_MAX) +# define SSIZE_MAX (SIZE_MAX / 2 - 1) +# endif /* defined(SSIZE_MAX) */ + +#endif /* defined(__linux__) */ + + +#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) +/* Dragonfly, FreeBSD, NetBSD, OpenBSD (has arc4random) */ +# include +# if defined(BSD) +# include +# endif +#endif + +#if defined(__EMSCRIPTEN__) +# include +# include +# include +# include +#endif /* defined(__EMSCRIPTEN__) */ + + +#if defined(_WIN32) +static int randombytes_win32_randombytes(void* buf, const size_t n) +{ + HCRYPTPROV ctx; + BOOL tmp; + + tmp = CryptAcquireContext(&ctx, NULL, NULL, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT); + if (tmp == FALSE) return -1; + + tmp = CryptGenRandom(ctx, n, (BYTE*) buf); + if (tmp == FALSE) return -1; + + tmp = CryptReleaseContext(ctx, 0); + if (tmp == FALSE) return -1; + + return 0; +} +#endif /* defined(_WIN32) */ + +#if defined(__wasi__) +static int randombytes_wasi_randombytes(void *buf, size_t n) { + arc4random_buf(buf, n); + return 0; +} +#endif /* defined(__wasi__) */ + +#if defined(__linux__) && (defined(USE_GLIBC) || defined(SYS_getrandom)) +# if defined(USE_GLIBC) +// getrandom is declared in glibc. +# elif defined(SYS_getrandom) +static ssize_t getrandom(void *buf, size_t buflen, unsigned int flags) { + return syscall(SYS_getrandom, buf, buflen, flags); +} +# endif + +static int randombytes_linux_randombytes_getrandom(void *buf, size_t n) +{ + /* I have thought about using a separate PRF, seeded by getrandom, but + * it turns out that the performance of getrandom is good enough + * (250 MB/s on my laptop). + */ + size_t offset = 0, chunk; + int ret; + while (n > 0) { + /* getrandom does not allow chunks larger than 33554431 */ + chunk = n <= 33554431 ? n : 33554431; + do { + ret = getrandom((char *)buf + offset, chunk, 0); + } while (ret == -1 && errno == EINTR); + if (ret < 0) return ret; + offset += ret; + n -= ret; + } + assert(n == 0); + return 0; +} +#endif // defined(__linux__) && (defined(USE_GLIBC) || defined(SYS_getrandom)) + + +#if defined(__linux__) && !defined(SYS_getrandom) +static int randombytes_linux_read_entropy_ioctl(int device, int *entropy) +{ + return ioctl(device, RNDGETENTCNT, entropy); +} + +static int randombytes_linux_read_entropy_proc(FILE *stream, int *entropy) +{ + int retcode; + do { + rewind(stream); + retcode = fscanf(stream, "%d", entropy); + } while (retcode != 1 && errno == EINTR); + if (retcode != 1) { + return -1; + } + return 0; +} + +static int randombytes_linux_wait_for_entropy(int device) +{ + /* We will block on /dev/random, because any increase in the OS' entropy + * level will unblock the request. I use poll here (as does libsodium), + * because we don't *actually* want to read from the device. */ + enum { IOCTL, PROC } strategy = IOCTL; + const int bits = 128; + struct pollfd pfd; + int fd; + FILE *proc_file; + int retcode, retcode_error = 0; // Used as return codes throughout this function + int entropy = 0; + + /* If the device has enough entropy already, we will want to return early */ + retcode = randombytes_linux_read_entropy_ioctl(device, &entropy); + // printf("errno: %d (%s)\n", errno, strerror(errno)); + if (retcode != 0 && (errno == ENOTTY || errno == ENOSYS)) { + // The ioctl call on /dev/urandom has failed due to a + // - ENOTTY (unsupported action), or + // - ENOSYS (invalid ioctl; this happens on MIPS, see #22). + // + // We will fall back to reading from + // `/proc/sys/kernel/random/entropy_avail`. This less ideal, + // because it allocates a file descriptor, and it may not work + // in a chroot. But at this point it seems we have no better + // options left. + strategy = PROC; + // Open the entropy count file + proc_file = fopen("/proc/sys/kernel/random/entropy_avail", "r"); + } else if (retcode != 0) { + // Unrecoverable ioctl error + return -1; + } + if (entropy >= bits) { + return 0; + } + + do { + fd = open("/dev/random", O_RDONLY); + } while (fd == -1 && errno == EINTR); /* EAGAIN will not occur */ + if (fd == -1) { + /* Unrecoverable IO error */ + return -1; + } + + pfd.fd = fd; + pfd.events = POLLIN; + for (;;) { + retcode = poll(&pfd, 1, -1); + if (retcode == -1 && (errno == EINTR || errno == EAGAIN)) { + continue; + } else if (retcode == 1) { + if (strategy == IOCTL) { + retcode = randombytes_linux_read_entropy_ioctl(device, &entropy); + } else if (strategy == PROC) { + retcode = randombytes_linux_read_entropy_proc(proc_file, &entropy); + } else { + return -1; // Unreachable + } + + if (retcode != 0) { + // Unrecoverable I/O error + retcode_error = retcode; + break; + } + if (entropy >= bits) { + break; + } + } else { + // Unreachable: poll() should only return -1 or 1 + retcode_error = -1; + break; + } + } + do { + retcode = close(fd); + } while (retcode == -1 && errno == EINTR); + if (strategy == PROC) { + do { + retcode = fclose(proc_file); + } while (retcode == -1 && errno == EINTR); + } + if (retcode_error != 0) { + return retcode_error; + } + return retcode; +} + + +static int randombytes_linux_randombytes_urandom(void *buf, size_t n) +{ + int fd; + size_t offset = 0, count; + ssize_t tmp; + do { + fd = open("/dev/urandom", O_RDONLY); + } while (fd == -1 && errno == EINTR); + if (fd == -1) return -1; + if (randombytes_linux_wait_for_entropy(fd) == -1) return -1; + + while (n > 0) { + count = n <= SSIZE_MAX ? n : SSIZE_MAX; + tmp = read(fd, (char *)buf + offset, count); + if (tmp == -1 && (errno == EAGAIN || errno == EINTR)) { + continue; + } + if (tmp == -1) return -1; /* Unrecoverable IO error */ + offset += tmp; + n -= tmp; + } + close(fd); + assert(n == 0); + return 0; +} +#endif /* defined(__linux__) && !defined(SYS_getrandom) */ + + +#if defined(BSD) +static int randombytes_bsd_randombytes(void *buf, size_t n) +{ + arc4random_buf(buf, n); + return 0; +} +#endif /* defined(BSD) */ + + +#if defined(__EMSCRIPTEN__) +static int randombytes_js_randombytes_nodejs(void *buf, size_t n) { + const int ret = EM_ASM_INT({ + var crypto; + try { + crypto = require('crypto'); + } catch (error) { + return -2; + } + try { + writeArrayToMemory(crypto.randomBytes($1), $0); + return 0; + } catch (error) { + return -1; + } + }, buf, n); + switch (ret) { + case 0: + return 0; + case -1: + errno = EINVAL; + return -1; + case -2: + errno = ENOSYS; + return -1; + } + assert(false); // Unreachable +} +#endif /* defined(__EMSCRIPTEN__) */ + + +int randombytes(void *buf, size_t n) +{ +#if defined(__EMSCRIPTEN__) +// # pragma message("Using crypto api from NodeJS") + return randombytes_js_randombytes_nodejs(buf, n); +#elif defined(__linux__) +# if defined(USE_GLIBC) +// # pragma message("Using getrandom function call") + /* Use getrandom system call */ + return randombytes_linux_randombytes_getrandom(buf, n); +# elif defined(SYS_getrandom) +// # pragma message("Using getrandom system call") + /* Use getrandom system call */ + return randombytes_linux_randombytes_getrandom(buf, n); +# else +// # pragma message("Using /dev/urandom device") + /* When we have enough entropy, we can read from /dev/urandom */ + return randombytes_linux_randombytes_urandom(buf, n); +# endif +#elif defined(BSD) +// # pragma message("Using arc4random system call") + /* Use arc4random system call */ + return randombytes_bsd_randombytes(buf, n); +#elif defined(_WIN32) +// # pragma message("Using Windows cryptographic API") + /* Use windows API */ + return randombytes_win32_randombytes(buf, n); +#elif defined(__wasi__) +// # pragma message("Using WASI arc4random_buf system call") + /* Use WASI */ + return randombytes_wasi_randombytes(buf, n); +#else +# error "randombytes(...) is not supported on this platform" +#endif +} diff --git a/src/3rdparty/randombytes/randombytes.h b/src/3rdparty/randombytes/randombytes.h new file mode 100644 index 0000000000..26c6ab1d5d --- /dev/null +++ b/src/3rdparty/randombytes/randombytes.h @@ -0,0 +1,25 @@ +#ifndef sss_RANDOMBYTES_H +#define sss_RANDOMBYTES_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WIN32 +/* Load size_t on windows */ +#include +#else +#include +#endif /* _WIN32 */ + + +/* + * Write `n` bytes of high quality random bytes to `buf` + */ +int randombytes(void *buf, size_t n); + +#ifdef __cplusplus +} +#endif + +#endif /* sss_RANDOMBYTES_H */