From 9026598999d996d403d0b5951006ef4bb0f1c1c0 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Fri, 3 Jan 2020 12:36:30 +0100 Subject: [PATCH] Kernel: Add a more expressive API for getting random bytes We now have these API's in : - get_fast_random_bytes(u8* buffer, size_t buffer_size) - get_good_random_bytes(u8* buffer, size_t buffer_size) - get_fast_random() - get_good_random() Internally they both use x86 RDRAND if available, otherwise they fall back to the same LCG we had in RandomDevice all along. The main purpose of this patch is to give kernel code a way to better express its needs for random data. Randomness is something that will require a lot more work, but this is hopefully a step in the right direction. --- Kernel/Arch/i386/CPU.cpp | 2 ++ Kernel/Arch/i386/CPU.h | 1 + Kernel/Devices/RandomDevice.cpp | 29 ++++------------------- Kernel/Devices/RandomDevice.h | 2 -- Kernel/Makefile | 1 + Kernel/Net/TCPSocket.cpp | 3 ++- Kernel/Net/UDPSocket.cpp | 3 ++- Kernel/Process.cpp | 21 ++--------------- Kernel/Random.cpp | 41 +++++++++++++++++++++++++++++++++ Kernel/Random.h | 26 +++++++++++++++++++++ Kernel/init.cpp | 6 +++++ 11 files changed, 87 insertions(+), 48 deletions(-) create mode 100644 Kernel/Random.cpp create mode 100644 Kernel/Random.h diff --git a/Kernel/Arch/i386/CPU.cpp b/Kernel/Arch/i386/CPU.cpp index acb08e19ed..efe2a67ddf 100644 --- a/Kernel/Arch/i386/CPU.cpp +++ b/Kernel/Arch/i386/CPU.cpp @@ -524,6 +524,7 @@ void sse_init() bool g_cpu_supports_nx; bool g_cpu_supports_pae; bool g_cpu_supports_pge; +bool g_cpu_supports_rdrand; bool g_cpu_supports_smep; bool g_cpu_supports_sse; bool g_cpu_supports_tsc; @@ -536,6 +537,7 @@ void detect_cpu_features() g_cpu_supports_pge = (processor_info.edx() & (1 << 13)); g_cpu_supports_sse = (processor_info.edx() & (1 << 25)); g_cpu_supports_tsc = (processor_info.edx() & (1 << 4)); + g_cpu_supports_rdrand = (processor_info.ecx() & (1 << 30)); CPUID extended_processor_info(0x80000001); g_cpu_supports_nx = (extended_processor_info.edx() & (1 << 20)); diff --git a/Kernel/Arch/i386/CPU.h b/Kernel/Arch/i386/CPU.h index 482247a2d2..96d579cfeb 100644 --- a/Kernel/Arch/i386/CPU.h +++ b/Kernel/Arch/i386/CPU.h @@ -519,6 +519,7 @@ void detect_cpu_features(); extern bool g_cpu_supports_nx; extern bool g_cpu_supports_pae; extern bool g_cpu_supports_pge; +extern bool g_cpu_supports_rdrand; extern bool g_cpu_supports_smep; extern bool g_cpu_supports_sse; extern bool g_cpu_supports_tsc; diff --git a/Kernel/Devices/RandomDevice.cpp b/Kernel/Devices/RandomDevice.cpp index 9df6a2c23a..19d81225a7 100644 --- a/Kernel/Devices/RandomDevice.cpp +++ b/Kernel/Devices/RandomDevice.cpp @@ -1,5 +1,5 @@ -#include "RandomDevice.h" -#include +#include +#include RandomDevice::RandomDevice() : CharacterDevice(1, 8) @@ -10,22 +10,6 @@ RandomDevice::~RandomDevice() { } -static u32 next = 1; - -#define MY_RAND_MAX 4294967295U -u32 RandomDevice::random_value() -{ - next = next * 1103515245 + 12345; - return next; -} - -#if 0 -static void mysrand(unsigned seed) -{ - next = seed; -} -#endif - bool RandomDevice::can_read(const FileDescription&) const { return true; @@ -33,13 +17,8 @@ bool RandomDevice::can_read(const FileDescription&) const ssize_t RandomDevice::read(FileDescription&, u8* buffer, ssize_t size) { - const int range = 'z' - 'a'; - ssize_t nread = min(size, PAGE_SIZE); - for (ssize_t i = 0; i < nread; ++i) { - u32 r = random_value() % range; - buffer[i] = (u8)('a' + r); - } - return nread; + get_good_random_bytes(buffer, size); + return size; } ssize_t RandomDevice::write(FileDescription&, const u8*, ssize_t size) diff --git a/Kernel/Devices/RandomDevice.h b/Kernel/Devices/RandomDevice.h index a11589d310..48065d4c62 100644 --- a/Kernel/Devices/RandomDevice.h +++ b/Kernel/Devices/RandomDevice.h @@ -8,8 +8,6 @@ public: RandomDevice(); virtual ~RandomDevice() override; - static u32 random_value(); - private: // ^CharacterDevice virtual ssize_t read(FileDescription&, u8*, ssize_t) override; diff --git a/Kernel/Makefile b/Kernel/Makefile index f6a253e22c..e36a87f3db 100644 --- a/Kernel/Makefile +++ b/Kernel/Makefile @@ -78,6 +78,7 @@ OBJS = \ ProcessTracer.o \ Profiling.o \ RTC.o \ + Random.o \ Scheduler.o \ SharedBuffer.o \ StdLib.o \ diff --git a/Kernel/Net/TCPSocket.cpp b/Kernel/Net/TCPSocket.cpp index 98cdcb68e9..38e4f1024f 100644 --- a/Kernel/Net/TCPSocket.cpp +++ b/Kernel/Net/TCPSocket.cpp @@ -6,6 +6,7 @@ #include #include #include +#include //#define TCP_SOCKET_DEBUG @@ -358,7 +359,7 @@ int TCPSocket::protocol_allocate_local_port() static const u16 first_ephemeral_port = 32768; static const u16 last_ephemeral_port = 60999; static const u16 ephemeral_port_range_size = last_ephemeral_port - first_ephemeral_port; - u16 first_scan_port = first_ephemeral_port + RandomDevice::random_value() % ephemeral_port_range_size; + u16 first_scan_port = first_ephemeral_port + get_good_random() % ephemeral_port_range_size; LOCKER(sockets_by_tuple().lock()); for (u16 port = first_scan_port;;) { diff --git a/Kernel/Net/UDPSocket.cpp b/Kernel/Net/UDPSocket.cpp index 8d3890f6ab..cec270126a 100644 --- a/Kernel/Net/UDPSocket.cpp +++ b/Kernel/Net/UDPSocket.cpp @@ -4,6 +4,7 @@ #include #include #include +#include void UDPSocket::for_each(Function callback) { @@ -92,7 +93,7 @@ int UDPSocket::protocol_allocate_local_port() static const u16 first_ephemeral_port = 32768; static const u16 last_ephemeral_port = 60999; static const u16 ephemeral_port_range_size = last_ephemeral_port - first_ephemeral_port; - u16 first_scan_port = first_ephemeral_port + RandomDevice::random_value() % ephemeral_port_range_size; + u16 first_scan_port = first_ephemeral_port + get_good_random() % ephemeral_port_range_size; LOCKER(sockets_by_port().lock()); for (u16 port = first_scan_port;;) { diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index df406e92e1..5a4ea76001 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -3612,25 +3613,7 @@ int Process::sys$getrandom(void* buffer, size_t buffer_size, unsigned int flags if (!validate_write(buffer, buffer_size)) return -EFAULT; - // We prefer to get whole words of entropy. - // If the length is unaligned, we can work with bytes instead. - // Mask out the bottom two bits for words. - size_t words_len = buffer_size & ~3; - if (words_len) { - uint32_t* words = (uint32_t*)buffer; - for (size_t i = 0; i < words_len / 4; i++) - words[i] = RandomDevice::random_value(); - } - // The remaining non-whole word bytes we can fill in. - size_t bytes_len = buffer_size & 3; - if (bytes_len) { - uint8_t* bytes = (uint8_t*)buffer + words_len; - // Get a whole word of entropy to use. - uint32_t word = RandomDevice::random_value(); - for (size_t i = 0; i < bytes_len; i++) - bytes[i] = ((uint8_t*)&word)[i]; - } - + get_good_random_bytes((u8*)buffer, buffer_size); return 0; } diff --git a/Kernel/Random.cpp b/Kernel/Random.cpp new file mode 100644 index 0000000000..d517ae5cef --- /dev/null +++ b/Kernel/Random.cpp @@ -0,0 +1,41 @@ +#include +#include +#include + +static u32 random32() +{ + if (g_cpu_supports_rdrand) { + u32 value = 0; + asm volatile( + "1%=:\n" + "rdrand %0\n" + "jnc 1%=\n" + : "=r"(value)); + return value; + } + // FIXME: This sucks lol + static u32 next = 1; + next = next * 1103515245 + 12345; + return next; +} + +void get_good_random_bytes(u8* buffer, size_t buffer_size) +{ + union { + u8 bytes[4]; + u32 value; + } u; + size_t offset = 4; + for (size_t i = 0; i < buffer_size; ++i) { + if (offset >= 4) { + u.value = random32(); + offset = 0; + } + buffer[i] = u.bytes[offset++]; + } +} + +void get_fast_random_bytes(u8* buffer, size_t buffer_size) +{ + return get_good_random_bytes(buffer, buffer_size); +} diff --git a/Kernel/Random.h b/Kernel/Random.h new file mode 100644 index 0000000000..9e02335e89 --- /dev/null +++ b/Kernel/Random.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +// NOTE: These API's are primarily about expressing intent/needs in the calling code. +// We don't make any guarantees about actual fastness or goodness yet. + +void get_fast_random_bytes(u8*, size_t); +void get_good_random_bytes(u8*, size_t); + +template +inline T get_fast_random() +{ + T value; + get_fast_random_bytes(reinterpret_cast(&value), sizeof(T)); + return value; +} + +template +inline T get_good_random() +{ + T value; + get_good_random_bytes(reinterpret_cast(&value), sizeof(T)); + return value; +} + diff --git a/Kernel/init.cpp b/Kernel/init.cpp index 24f1fd0ea8..b01d496abe 100644 --- a/Kernel/init.cpp +++ b/Kernel/init.cpp @@ -307,6 +307,12 @@ extern "C" [[noreturn]] void init(u32 physical_address_for_kernel_page_tables) kprintf("x86: RDTSC support restricted\n"); } + if (g_cpu_supports_rdrand) { + kprintf("x86: Using RDRAND for good randomness\n"); + } else { + kprintf("x86: No RDRAND support detected. Randomness will be shitty\n"); + } + RTC::initialize(); PIC::initialize(); gdt_init();