2 ** Pseudo-random number generation.
3 ** Copyright (C) 2005-2023 Mike Pall. See Copyright Notice in luajit.h
9 /* To get the syscall prototype. */
10 #if defined(__linux__) && !defined(_GNU_SOURCE)
18 /* -- PRNG step function -------------------------------------------------- */
20 /* This implements a Tausworthe PRNG with period 2^223. Based on:
21 ** Tables of maximally-equidistributed combined LFSR generators,
22 ** Pierre L'Ecuyer, 1991, table 3, 1st entry.
23 ** Full-period ME-CF generator with L=64, J=4, k=223, N1=49.
25 ** Important note: This PRNG is NOT suitable for cryptographic use!
27 ** But it works fine for math.random(), which has an API that's not
28 ** suitable for cryptography, anyway.
30 ** When used as a securely seeded global PRNG, it substantially raises
31 ** the difficulty for various attacks on the VM.
34 /* Update generator i and compute a running xor of all states. */
35 #define TW223_GEN(rs, z, r, i, k, q, s) \
37 z = (((z<<q)^z) >> (k-s)) ^ ((z&((uint64_t)(int64_t)-1 << (64-k)))<<s); \
40 #define TW223_STEP(rs, z, r) \
41 TW223_GEN(rs, z, r, 0, 63, 31, 18) \
42 TW223_GEN(rs, z, r, 1, 58, 19, 28) \
43 TW223_GEN(rs, z, r, 2, 55, 24, 7) \
44 TW223_GEN(rs, z, r, 3, 47, 21, 8)
46 /* PRNG step function with uint64_t result. */
47 LJ_NOINLINE
uint64_t LJ_FASTCALL
lj_prng_u64(PRNGState
*rs
)
54 /* PRNG step function with double in uint64_t result. */
55 LJ_NOINLINE
uint64_t LJ_FASTCALL
lj_prng_u64d(PRNGState
*rs
)
59 /* Returns a double bit pattern in the range 1.0 <= d < 2.0. */
60 return (r
& U64x(000fffff
,ffffffff
)) | U64x(3ff00000
,00000000);
63 /* Condition seed: ensure k[i] MSB of u[i] are non-zero. */
64 static LJ_AINLINE
void lj_prng_condition(PRNGState
*rs
)
66 if (rs
->u
[0] < (1u << 1)) rs
->u
[0] += (1u << 1);
67 if (rs
->u
[1] < (1u << 6)) rs
->u
[1] += (1u << 6);
68 if (rs
->u
[2] < (1u << 9)) rs
->u
[2] += (1u << 9);
69 if (rs
->u
[3] < (1u << 17)) rs
->u
[3] += (1u << 17);
72 /* -- PRNG seeding from OS ------------------------------------------------ */
74 #if LUAJIT_SECURITY_PRNG == 0
76 /* Nothing to define. */
78 #elif LJ_TARGET_XBOX360
80 extern int XNetRandom(void *buf
, unsigned int len
);
84 extern int sys_get_random_number(void *buf
, uint64_t len
);
86 #elif LJ_TARGET_PS4 || LJ_TARGET_PS5 || LJ_TARGET_PSVITA
88 extern int sceRandomGetRandomNumber(void *buf
, size_t len
);
94 #elif LJ_TARGET_WINDOWS || LJ_TARGET_XBOXONE
96 #define WIN32_LEAN_AND_MEAN
99 #if LJ_TARGET_UWP || LJ_TARGET_XBOXONE
100 /* Must use BCryptGenRandom. */
102 #pragma comment(lib, "bcrypt.lib")
104 /* If you wonder about this mess, then search online for RtlGenRandom. */
105 typedef BOOLEAN (WINAPI
*PRGR
)(void *buf
, ULONG len
);
106 static PRGR libfunc_rgr
;
109 #elif LJ_TARGET_POSIX
112 /* Avoid a dependency on glibc 2.25+ and use the getrandom syscall instead. */
113 #include <sys/syscall.h>
116 #if LJ_TARGET_OSX && !LJ_TARGET_IOS
118 ** In their infinite wisdom Apple decided to disallow getentropy() in the
119 ** iOS App Store. Even though the call is common to all BSD-ish OS, it's
120 ** recommended by Apple in their own security-related docs, and, to top
121 ** off the foolery, /dev/urandom is handled by the same kernel code,
122 ** yet accessing it is actually permitted (but less efficient).
124 #include <Availability.h>
125 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
126 #define LJ_TARGET_HAS_GETENTROPY 1
128 #elif (LJ_TARGET_BSD && !defined(__NetBSD__)) || LJ_TARGET_SOLARIS || LJ_TARGET_CYGWIN || LJ_TARGET_QNX
129 #define LJ_TARGET_HAS_GETENTROPY 1
132 #if LJ_TARGET_HAS_GETENTROPY
133 extern int getentropy(void *buf
, size_t len
)
135 __attribute__((weak
))
142 /* For the /dev/urandom fallback. */
148 #if LUAJIT_SECURITY_PRNG == 0
150 /* If you really don't care about security, then define
151 ** LUAJIT_SECURITY_PRNG=0. This yields a predictable seed
152 ** and provides NO SECURITY against various attacks on the VM.
154 ** BTW: This is NOT the way to get predictable table iteration,
155 ** predictable trace generation, predictable bytecode generation, etc.
157 int LJ_FASTCALL
lj_prng_seed_secure(PRNGState
*rs
)
159 lj_prng_seed_fixed(rs
); /* The fixed seed is already conditioned. */
165 /* Securely seed PRNG from system entropy. Returns 0 on failure. */
166 int LJ_FASTCALL
lj_prng_seed_secure(PRNGState
*rs
)
168 #if LJ_TARGET_XBOX360
170 if (XNetRandom(rs
->u
, (unsigned int)sizeof(rs
->u
)) == 0)
175 if (sys_get_random_number(rs
->u
, sizeof(rs
->u
)) == 0)
178 #elif LJ_TARGET_PS4 || LJ_TARGET_PS5 || LJ_TARGET_PSVITA
180 if (sceRandomGetRandomNumber(rs
->u
, sizeof(rs
->u
)) == 0)
185 if (getentropy(rs
->u
, sizeof(rs
->u
)) == 0)
188 #elif LJ_TARGET_UWP || LJ_TARGET_XBOXONE
190 if (BCryptGenRandom(NULL
, (PUCHAR
)(rs
->u
), (ULONG
)sizeof(rs
->u
),
191 BCRYPT_USE_SYSTEM_PREFERRED_RNG
) >= 0)
194 #elif LJ_TARGET_WINDOWS
196 /* Keep the library loaded in case multiple VMs are started. */
198 HMODULE lib
= LJ_WIN_LOADLIBA("advapi32.dll");
200 libfunc_rgr
= (PRGR
)GetProcAddress(lib
, "SystemFunction036");
201 if (!libfunc_rgr
) return 0;
203 if (libfunc_rgr(rs
->u
, (ULONG
)sizeof(rs
->u
)))
206 #elif LJ_TARGET_POSIX
208 #if LJ_TARGET_LINUX && defined(SYS_getrandom)
210 if (syscall(SYS_getrandom
, rs
->u
, sizeof(rs
->u
), 0) == (long)sizeof(rs
->u
))
213 #elif LJ_TARGET_HAS_GETENTROPY
216 if (&getentropy
&& getentropy(rs
->u
, sizeof(rs
->u
)) == 0)
219 if (getentropy(rs
->u
, sizeof(rs
->u
)) == 0)
225 /* Fallback to /dev/urandom. This may fail if the device is not
226 ** existent or accessible in a chroot or container, or if the process
227 ** or the OS ran out of file descriptors.
230 int fd
= open("/dev/urandom", O_RDONLY
|O_CLOEXEC
);
232 ssize_t n
= read(fd
, rs
->u
, sizeof(rs
->u
));
234 if (n
== (ssize_t
)sizeof(rs
->u
))
241 /* Add an elif above for your OS with a secure PRNG seed.
242 ** Note that fiddling around with rand(), getpid(), time() or coercing
243 ** ASLR to yield a few bits of randomness is not helpful.
244 ** If you don't want any security, then don't pretend you have any
245 ** and simply define LUAJIT_SECURITY_PRNG=0 for the build.
247 #error "Missing secure PRNG seed for this OS"
250 return 0; /* Fail. */
253 lj_prng_condition(rs
);
254 (void)lj_prng_u64(rs
);
255 return 1; /* Success. */