Browse Source

Kernel: Replace existing random implementation with Fortuna

Peter Elliott 5 years ago
parent
commit
0f32155fa4
3 changed files with 49 additions and 30 deletions
  1. 6 0
      Kernel/CMakeLists.txt
  2. 23 24
      Kernel/Random.cpp
  3. 20 6
      Kernel/Random.h

+ 6 - 0
Kernel/CMakeLists.txt

@@ -148,12 +148,18 @@ set(KEYBOARD_SOURCES
     ../Libraries/LibKeyboard/CharacterMap.cpp
 )
 
+set(CRYPTO_SOURCES
+    ../Libraries/LibCrypto/Cipher/AES.cpp
+    ../Libraries/LibCrypto/Hash/SHA2.cpp
+)
+
 set(SOURCES
     ${KERNEL_SOURCES}
     ${AK_SOURCES}
     ${ELF_SOURCES}
     ${VT_SOURCES}
     ${KEYBOARD_SOURCES}
+    ${CRYPTO_SOURCES}
 )
 
 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DKERNEL")

+ 23 - 24
Kernel/Random.cpp

@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2020, Peter Elliott <pelliott@ualberta.ca>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -30,39 +31,37 @@
 
 namespace Kernel {
 
-static u32 random32()
+static KernelRng* s_the;
+
+KernelRng& KernelRng::the()
 {
-    if (g_cpu_supports_rdrand) {
-        u32 value = 0;
-        asm volatile(
-            "1:\n"
-            "rdrand %0\n"
-            "jnc 1b\n"
-            : "=r"(value));
-        return value;
+    if (!s_the) {
+        s_the = new KernelRng;
     }
-    // FIXME: This sucks lol
-    static u32 next = 1;
-    next = next * 1103515245 + 12345;
-    return next;
+    return *s_the;
 }
 
-void get_good_random_bytes(u8* buffer, size_t buffer_size)
+KernelRng::KernelRng()
 {
-    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;
+    if (g_cpu_supports_rdrand) {
+        for (size_t i = 0; i < KernelRng::pool_count * KernelRng::reseed_threshold; ++i) {
+            u32 value = 0;
+            asm volatile(
+                "1:\n"
+                "rdseed %0\n"
+                "jnc 1b\n"
+                : "=r"(value));
+
+            this->add_random_event(value, i % 32);
         }
-        buffer[i] = u.bytes[offset++];
     }
 }
 
+void get_good_random_bytes(u8* buffer, size_t buffer_size)
+{
+    KernelRng::the().get_random_bytes(buffer, buffer_size);
+}
+
 void get_fast_random_bytes(u8* buffer, size_t buffer_size)
 {
     return get_good_random_bytes(buffer, buffer_size);

+ 20 - 6
Kernel/Random.h

@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
- * Copyright (c) 2020, Peter Elliott <pelliott@ualberta.ca
+ * Copyright (c) 2020, Peter Elliott <pelliott@ualberta.ca>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -32,11 +32,14 @@
 #include <AK/Types.h>
 #include <Kernel/StdLib.h>
 #include <LibCrypto/Cipher/Cipher.h>
+#include <LibCrypto/Cipher/AES.h>
+#include <LibCrypto/Hash/SHA2.h>
 
 namespace Kernel {
 
 template<typename CipherT, typename HashT, int KeySize>
 class FortunaPRNG {
+public:
     constexpr static size_t pool_count = 32;
     constexpr static size_t reseed_threshold = 16;
 
@@ -45,7 +48,6 @@ class FortunaPRNG {
     using HashType = HashT;
     using DigestType = HashT::DigestType;
 
-public:
     void get_random_bytes(u8* buffer, size_t n)
     {
         if (m_p0_len >= reseed_threshold) {
@@ -57,7 +59,7 @@ public:
         // FIXME: More than 2^20 bytes cannot be generated without refreshing the key.
         ASSERT(n < (1 << 20));
 
-        CipherType cipher(m_key, m_key.size());
+        CipherType cipher(m_key, KeySize);
 
         size_t block_size = CipherType::BlockSizeInBits / 8;
 
@@ -67,9 +69,10 @@ public:
 
         // Extract a new key from the prng stream.
 
-        for (size_t i = 0; i < KeySize; i += block_size) {
+        for (size_t i = 0; i < KeySize/8; i += block_size) {
             this->generate_block(cipher, &(m_key[i]), min(block_size, KeySize - i));
         }
+
     }
 
     template<typename T>
@@ -79,13 +82,14 @@ public:
         if (pool == 0) {
             m_p0_len++;
         }
-        m_pools[pool].update(reinterpret_cast<u8*>(&event_data), sizeof(T));
+        m_pools[pool].update(reinterpret_cast<const u8*>(&event_data), sizeof(T));
     }
 
 private:
     void generate_block(CipherType cipher, u8* buffer, size_t size)
     {
-        BlockType input((u8*)m_counter, sizeof(m_counter));
+
+        BlockType input((u8*)&m_counter, sizeof(m_counter));
         BlockType output;
         cipher.encrypt_block(input, output);
         m_counter++;
@@ -119,6 +123,16 @@ private:
     HashType m_pools[pool_count];
 };
 
+class KernelRng : public FortunaPRNG<Crypto::Cipher::AESCipher, Crypto::Hash::SHA256, 256> {
+    AK_MAKE_ETERNAL;
+public:
+    static KernelRng& the();
+
+private:
+    KernelRng();
+
+};
+
 // 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.