cat /proc/cpuinfo | fgrep rdrand
#include <stdio.h>
#include <cpuid.h>
/* %ecx */
#define bit_RDRND (1 << 30)
int main() {
unsigned int eax = 0, ebx = 0, ecx = 0, edx = 0;
__get_cpuid(1, &eax, &ebx, &ecx, &edx);
if (bit_RDRND & ecx) {
puts("RDRAND supported\n");
} else {
puts("RDRAND NOT supported\n");
}
}
remark: __builtin_cpu_supports
GCC intrinsics does not support the rdrand bit
compile
gcc cpuinfo.c -o cpuinfo
example output
$ ./cpuinfo
RDRAND supported
In intel, it's supported from Ivy Bridge (i.e. second generation i*-2***
cpus).
On AMD it is supported from Zen (Gen 1.) architecture, but it had serious issues in the past
RDRAND is enabled by default and cannot be disabled.
- RDRAND only can be disabled via a microcode update on the CPU, which is undocumented by Intel.
- Linux kernel can be started with nordrand or random.trust_cpu=0 option, which disables its usage in kernel functions (random, urandom, ASLR, KASLR, etc.)
- There is no resource efficient way to filter out CPU opcodes from hardware. Only binary translation based virtualization or full software emulation can disable or modify the behaviour of RDRAND (example in fault testing part).
Fortunately GCC supports RDRAND as intrinsics (with -mrdrnd
), so no inline ASM is required.
#include <stdio.h>
#include <immintrin.h>
int main() {
unsigned long long int result = 0ULL;
for (int i = 0; i < 10; i++) {
int rc = _rdrand64_step (&result);
if (rc == 1) {
printf("%016llx\n", result);
} else {
printf("failed to generate random number\n");
}
}
return 0;
}
Build and test it out.
$ gcc --version
gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0
$ gcc -mrdrnd rdrand.c -Wall -o rdrand
$ ./rdrand
97709d079ea66d4a
902e75564ae84836
8addf245b4861af5
73cb2ef2b5cca40e
6c47ae93df7feffc
118c774743b28a8a
f08718c072a6a878
042949e48f1d0bc0
0226bfc7e63ff258
2f58b21171457403
# objdump -M intel -S ./rdrand | fgrep -A 4 -B 4 rdrand
./rdrand: file format elf64-x86-64
Disassembly of section .init:
--
720: eb 4e jmp 770 <main+0x76>
722: 48 8d 45 e8 lea rax,[rbp-0x18]
726: 48 89 45 f0 mov QWORD PTR [rbp-0x10],rax
72a: 48 8b 55 f0 mov rdx,QWORD PTR [rbp-0x10]
72e: 48 0f c7 f0 rdrand rax
732: 48 89 02 mov QWORD PTR [rdx],rax
735: ba 01 00 00 00 mov edx,0x1
73a: 0f 42 c2 cmovb eax,edx
73d: 89 45 e4 mov DWORD PTR [rbp-0x1c],eax
RDRAND
native instruction is called in line 0x72e
.
Unlike the RDRAND instruction, the seed values come directly from the entropy conditioner, and it is possible for callers to invoke RDSEED faster than those values are generated. This means that applications must be designed robustly and be prepared for calls to RDSEED to fail because seeds are not available (CF=0).
If only one thread is calling RDSEED infrequently, it is very unlikely that a random seed will not be available. Only during periods of heavy demand, such as when one thread is calling RDSEED in rapid succession or multiple threads are calling RDSEED simultaneously, are underflows likely to occur. Because the RDSEED instruction does not have a fairness mechanism built into it, however, there are no guarantees as to how often a thread should retry the instruction, or how many retries might be needed, in order to obtain a random seed. In practice, this depends on the number of hardware threads on the CPU and how aggressively they are calling RDSEED.
Since there is no simple procedure for retrying the instruction to obtain a random seed, follow these basic guidelines.
Docker will map / mount the host's cpuinfo for /proc/cpuinfo
. No further configuration is needed. Also all of the checked solutions are not using /proc/cpuinfo
for CPU feature detection, but CPUID
native instruction (see the support check part).
Docker also can't filter the x64 instructions, so it can't disable the RDRAND
instruction (see enable/disable part).
Get a 6+ year old intel CPU, before IvyBridge.
Or use QEMU to emulate a SandyBridge (latest CPU before RDRAND support) and install an Ubuntu 20 to it.
qemu-img create -f qcow2 hd_img.img 20g
qemu-system-x86_64 -cdrom ./ubuntu-20.04.1-live-server-amd64.iso -hda ./hd_img.img -m 2g -cpu SandyBridge -boot menu=on
SandyBridge emulation is resource intensive, it can't use VT-X, only runtime binary translation, which is slow on x64.
An install will took about an hour on a modern PC.
(Alternatively you can use -cpu host
option if the hosts system supports KVM, install the system with hardware assisted virtualization and reboot it using SandyBridge.)
Results
- CPUID does not have the RDRAND bit (
ecx reg, 30th bit
) rdrand
missing from/proc/cpuinfo
- RDRAND will throw a
SIGILL
error and the program is terminated
"Faulty system" is a system, where random is weak, or has low entropy. It's quite hard to taint the CPU intentionally to corrupt the quality of the random, but there were occurences in the past, when a CPU bug caused an issue (especially shortly after booting the system).
- https://linuxreviews.org/AMD_Ryzen_3000_series_CPUs_can%27t_do_Random_on_boot_causing_Boot_Failure_on_newer_Linux_distributions
- https://www.phoronix.com/scan.php?page=news_item&px=AMD-RdRand-Disable-15h-16h
- https://www.phoronix.com/scan.php?page=news_item&px=Linux-5.5-RdRand-Sanity-Check
- https://www.phoronix.com/scan.php?page=news_item&px=AMD-CPUs-RdRand-Suspend
It can be tested by modifying the target_ulong HELPER(rdrand)(CPUX86State *env)
function in ./target/i386/int_helper.c
in the source code of qemu, recompile it and give it a try.
Newer linux kernels (from 4.19) are using a combination of RDRAND and an internal algorithm to produce random numbers for /dev/random
and /dev/urandom
.
https://research.jvroig.com/linuxrand/DevsJustUSeUrandPlease_Templated_2018-05-19.pdf
The NativePRNG
is using /dev/random
to seed the SecureRandom
instance and /dev/urandom
to salt the output after that.
This may can be enough...
The best way is to use a native library with JNI
https://github.com/cambecc/drnglib
- This contains libraries for multiple operating system, so it will work and can be built on MacOS / Linux / Windows
- This is using the CPUID based RDRND feature detection and not relies on
/proc/cpuinfo
and throws a Java exception if it's not supported - Needs simple GCC on linux (but not further deps) to build it, on windows it uses a preshipped DLL.
There are another hacky and more complicated alternative solutions, like
- Write a native library for the JDK/JVM itself: http://openjdk.java.net/projects/jigsaw/doc/topics/nativecode.html
- Create a small utility to write bytes from RDRAND to a named pipe and pass it as a random source to
java.security.egd
.
Usually this metric is measured by "diehard tests", like the rng-tools package.
Unfortunately these won't provide a simple GO / NOGO metric.
More info
- https://warmcat.com/2009/05/21/whirlygig-verification-and-rngtest-analysis.html
- https://www.scribd.com/document/11305936/NIST-Statistical-Test-Suite-for-Random-and-Pseudo-Random
The easiest way is to request a few hundres kbytes on the start from the Java app and compress (e.g. with the builtin GZIPOutputStream
) it and it should not have more than 0.1% of size change.