#ifndef _MSC_VER # include #endif #include "mirage_crypto.h" #include #include #include #include #if defined(__ANDROID_API__) && __ANDROID_API__ < 28 // on Android 27 and earlier, we use Google's recommended arc4random_buf # include void raw_getrandom (uint8_t *data, size_t len) { arc4random_buf(data, len); } #elif defined(__linux) || defined(__GNU__) # include // on Linux and GNU/Hurd, we use getrandom and loop # if __GLIBC__ && __GLIBC__ <= 2 && __GLIBC_MINOR__ < 25 # include # define getrandom(buf, len, flags) syscall(SYS_getrandom, (buf), (len), (flags)) # else # include # define getrandom(buf, len, flags) getrandom((buf), (len), (flags)) # endif void raw_getrandom (uint8_t *data, size_t len) { size_t off = 0; ssize_t r = 0; while (off < len) { r = getrandom(data + off, len - off, 0); if (r == -1) { if (errno == EINTR) continue; else uerror("getrandom", Nothing); } off += (size_t)r; } } #elif (defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) || defined(__OpenBSD__) || defined(__APPLE__)) || defined(__NetBSD__) // on BSD and macOS, loop (in pieces of 256) getentropy #if defined(__APPLE__) // on macOS, getentropy is defined in sys/random.h (on BSD in unistd.h) #include #endif #include void raw_getrandom (uint8_t *data, size_t len) { size_t rlen = 0; for (size_t i = 0; i <= len; i += 256) { rlen = MIN(256, len - i); if (getentropy(data + i, rlen) == -1) uerror("getentropy", Nothing); } } #elif (defined(_WIN32)) /* There is a choice between using RtlGenRandom and BCryptGenRandom * here, and Microsoft does not make the choice obvious. It appears * that RtlGenRandom is best used when older Windows compatibility * is of concern, but requires some gymnastics around binding it * with the right calling convention. * * Therefore (https://github.com/mirage/mirage-crypto/pull/39) we * have decided to go with the more modern Windows API with bcrypt, * and make Windows 10 our minimum supported version of mirage-crypto. */ #include #include #include void raw_getrandom(uint8_t *data, size_t len) { NTSTATUS Status; Status = BCryptGenRandom(NULL, data, len, BCRYPT_USE_SYSTEM_PREFERRED_RNG); if (Status != STATUS_SUCCESS) uerror("BCryptGenRandom", Nothing); } #else #error "Retrieving random data not supported on this platform" #endif CAMLprim value mc_getrandom (value buf, value off, value len) { raw_getrandom(_bp_uint8_off(buf, off), Long_val(len)); return Val_unit; }