| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259 |
- /*
- ** Pseudo-random number generation.
- ** Copyright (C) 2005-2023 Mike Pall. See Copyright Notice in luajit.h
- */
- #define lj_prng_c
- #define LUA_CORE
- /* To get the syscall prototype. */
- #if defined(__linux__) && !defined(_GNU_SOURCE)
- #define _GNU_SOURCE
- #endif
- #include "lj_def.h"
- #include "lj_arch.h"
- #include "lj_prng.h"
- /* -- PRNG step function -------------------------------------------------- */
- /* This implements a Tausworthe PRNG with period 2^223. Based on:
- ** Tables of maximally-equidistributed combined LFSR generators,
- ** Pierre L'Ecuyer, 1991, table 3, 1st entry.
- ** Full-period ME-CF generator with L=64, J=4, k=223, N1=49.
- **
- ** Important note: This PRNG is NOT suitable for cryptographic use!
- **
- ** But it works fine for math.random(), which has an API that's not
- ** suitable for cryptography, anyway.
- **
- ** When used as a securely seeded global PRNG, it substantially raises
- ** the difficulty for various attacks on the VM.
- */
- /* Update generator i and compute a running xor of all states. */
- #define TW223_GEN(rs, z, r, i, k, q, s) \
- z = rs->u[i]; \
- z = (((z<<q)^z) >> (k-s)) ^ ((z&((uint64_t)(int64_t)-1 << (64-k)))<<s); \
- r ^= z; rs->u[i] = z;
- #define TW223_STEP(rs, z, r) \
- TW223_GEN(rs, z, r, 0, 63, 31, 18) \
- TW223_GEN(rs, z, r, 1, 58, 19, 28) \
- TW223_GEN(rs, z, r, 2, 55, 24, 7) \
- TW223_GEN(rs, z, r, 3, 47, 21, 8)
- /* PRNG step function with uint64_t result. */
- LJ_NOINLINE uint64_t LJ_FASTCALL lj_prng_u64(PRNGState *rs)
- {
- uint64_t z, r = 0;
- TW223_STEP(rs, z, r)
- return r;
- }
- /* PRNG step function with double in uint64_t result. */
- LJ_NOINLINE uint64_t LJ_FASTCALL lj_prng_u64d(PRNGState *rs)
- {
- uint64_t z, r = 0;
- TW223_STEP(rs, z, r)
- /* Returns a double bit pattern in the range 1.0 <= d < 2.0. */
- return (r & U64x(000fffff,ffffffff)) | U64x(3ff00000,00000000);
- }
- /* Condition seed: ensure k[i] MSB of u[i] are non-zero. */
- static LJ_AINLINE void lj_prng_condition(PRNGState *rs)
- {
- if (rs->u[0] < (1u << 1)) rs->u[0] += (1u << 1);
- if (rs->u[1] < (1u << 6)) rs->u[1] += (1u << 6);
- if (rs->u[2] < (1u << 9)) rs->u[2] += (1u << 9);
- if (rs->u[3] < (1u << 17)) rs->u[3] += (1u << 17);
- }
- /* -- PRNG seeding from OS ------------------------------------------------ */
- #if LUAJIT_SECURITY_PRNG == 0
- /* Nothing to define. */
- #elif LJ_TARGET_XBOX360
- extern int XNetRandom(void *buf, unsigned int len);
- #elif LJ_TARGET_PS3
- extern int sys_get_random_number(void *buf, uint64_t len);
- #elif LJ_TARGET_PS4 || LJ_TARGET_PS5 || LJ_TARGET_PSVITA
- extern int sceRandomGetRandomNumber(void *buf, size_t len);
- #elif LJ_TARGET_NX
- #include <unistd.h>
- #elif LJ_TARGET_WINDOWS || LJ_TARGET_XBOXONE
- #define WIN32_LEAN_AND_MEAN
- #include <windows.h>
- #if LJ_TARGET_UWP || LJ_TARGET_XBOXONE
- /* Must use BCryptGenRandom. */
- #include <bcrypt.h>
- #pragma comment(lib, "bcrypt.lib")
- #else
- /* If you wonder about this mess, then search online for RtlGenRandom. */
- typedef BOOLEAN (WINAPI *PRGR)(void *buf, ULONG len);
- static PRGR libfunc_rgr;
- #endif
- #elif LJ_TARGET_POSIX
- #if LJ_TARGET_LINUX
- /* Avoid a dependency on glibc 2.25+ and use the getrandom syscall instead. */
- #include <sys/syscall.h>
- #else
- #if LJ_TARGET_OSX && !LJ_TARGET_IOS
- /*
- ** In their infinite wisdom Apple decided to disallow getentropy() in the
- ** iOS App Store. Even though the call is common to all BSD-ish OS, it's
- ** recommended by Apple in their own security-related docs, and, to top
- ** off the foolery, /dev/urandom is handled by the same kernel code,
- ** yet accessing it is actually permitted (but less efficient).
- */
- #include <Availability.h>
- #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
- #define LJ_TARGET_HAS_GETENTROPY 1
- #endif
- #elif (LJ_TARGET_BSD && !defined(__NetBSD__)) || LJ_TARGET_SOLARIS || LJ_TARGET_CYGWIN || LJ_TARGET_QNX
- #define LJ_TARGET_HAS_GETENTROPY 1
- #endif
- #if LJ_TARGET_HAS_GETENTROPY
- extern int getentropy(void *buf, size_t len)
- #ifdef __ELF__
- __attribute__((weak))
- #endif
- ;
- #endif
- #endif
- /* For the /dev/urandom fallback. */
- #include <fcntl.h>
- #include <unistd.h>
- #endif
- #if LUAJIT_SECURITY_PRNG == 0
- /* If you really don't care about security, then define
- ** LUAJIT_SECURITY_PRNG=0. This yields a predictable seed
- ** and provides NO SECURITY against various attacks on the VM.
- **
- ** BTW: This is NOT the way to get predictable table iteration,
- ** predictable trace generation, predictable bytecode generation, etc.
- */
- int LJ_FASTCALL lj_prng_seed_secure(PRNGState *rs)
- {
- lj_prng_seed_fixed(rs); /* The fixed seed is already conditioned. */
- return 1;
- }
- #else
- /* Securely seed PRNG from system entropy. Returns 0 on failure. */
- int LJ_FASTCALL lj_prng_seed_secure(PRNGState *rs)
- {
- #if LJ_TARGET_XBOX360
- if (XNetRandom(rs->u, (unsigned int)sizeof(rs->u)) == 0)
- goto ok;
- #elif LJ_TARGET_PS3
- if (sys_get_random_number(rs->u, sizeof(rs->u)) == 0)
- goto ok;
- #elif LJ_TARGET_PS4 || LJ_TARGET_PS5 || LJ_TARGET_PSVITA
- if (sceRandomGetRandomNumber(rs->u, sizeof(rs->u)) == 0)
- goto ok;
- #elif LJ_TARGET_NX
- if (getentropy(rs->u, sizeof(rs->u)) == 0)
- goto ok;
- #elif LJ_TARGET_UWP || LJ_TARGET_XBOXONE
- if (BCryptGenRandom(NULL, (PUCHAR)(rs->u), (ULONG)sizeof(rs->u),
- BCRYPT_USE_SYSTEM_PREFERRED_RNG) >= 0)
- goto ok;
- #elif LJ_TARGET_WINDOWS
- /* Keep the library loaded in case multiple VMs are started. */
- if (!libfunc_rgr) {
- HMODULE lib = LJ_WIN_LOADLIBA("advapi32.dll");
- if (!lib) return 0;
- libfunc_rgr = (PRGR)GetProcAddress(lib, "SystemFunction036");
- if (!libfunc_rgr) return 0;
- }
- if (libfunc_rgr(rs->u, (ULONG)sizeof(rs->u)))
- goto ok;
- #elif LJ_TARGET_POSIX
- #if LJ_TARGET_LINUX && defined(SYS_getrandom)
- if (syscall(SYS_getrandom, rs->u, sizeof(rs->u), 0) == (long)sizeof(rs->u))
- goto ok;
- #elif LJ_TARGET_HAS_GETENTROPY
- #ifdef __ELF__
- if (&getentropy && getentropy(rs->u, sizeof(rs->u)) == 0)
- goto ok;
- #else
- if (getentropy(rs->u, sizeof(rs->u)) == 0)
- goto ok;
- #endif
- #endif
- /* Fallback to /dev/urandom. This may fail if the device is not
- ** existent or accessible in a chroot or container, or if the process
- ** or the OS ran out of file descriptors.
- */
- {
- int fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC);
- if (fd != -1) {
- ssize_t n = read(fd, rs->u, sizeof(rs->u));
- (void)close(fd);
- if (n == (ssize_t)sizeof(rs->u))
- goto ok;
- }
- }
- #else
- /* Add an elif above for your OS with a secure PRNG seed.
- ** Note that fiddling around with rand(), getpid(), time() or coercing
- ** ASLR to yield a few bits of randomness is not helpful.
- ** If you don't want any security, then don't pretend you have any
- ** and simply define LUAJIT_SECURITY_PRNG=0 for the build.
- */
- #error "Missing secure PRNG seed for this OS"
- #endif
- return 0; /* Fail. */
- ok:
- lj_prng_condition(rs);
- (void)lj_prng_u64(rs);
- return 1; /* Success. */
- }
- #endif
|