Repository: randombit/botan
Pull request #2627 (2021-02-18 10:19:08)

Backport #2600
Fixing #2595


Patch:
diff --git a/src/lib/ffi/ffi.h b/src/lib/ffi/ffi.h
index cf8931acf..c9511e97e 100644
--- a/src/lib/ffi/ffi.h
+++ b/src/lib/ffi/ffi.h
@@ -1,6 +1,7 @@
 /*
 * FFI (C89 API)
 * (C) 2015,2017 Jack Lloyd
+* (C) 2021 René Fischer
 *
 * Botan is released under the Simplified BSD License (see license.txt)
 */
@@ -200,6 +201,20 @@ typedef struct botan_rng_struct* botan_rng_t;
 */
 BOTAN_PUBLIC_API(2,0) int botan_rng_init(botan_rng_t* rng, const char* rng_type);
 
+/**
+* Initialize a custom random number generator from a set of callback functions
+* @param rng rng object
+* @param rng_name name of the rng
+* @param context An application-specific context passed to the callback functions
+* @param get_cb Callback for getting random bytes from the rng, return 0 for success
+* @param add_entry_cb Callback for adding entropy to the rng, return 0 for success, may be NULL
+* @param destroy_cb Callback called when rng is destroyed, may be NULL
+*/
+BOTAN_PUBLIC_API(2,17) int botan_rng_init_custom(botan_rng_t* rng_out, const char* rng_name, void* context,
+                                                 int(* get_cb)(void* context, uint8_t* out, size_t out_len),
+                                                 int(* add_entropy_cb)(void* context, const uint8_t input[], size_t length),
+                                                 void(* destroy_cb)(void* context));
+
 /**
 * Get random bytes from a random number generator
 * @param rng rng object
diff --git a/src/lib/ffi/ffi_rng.cpp b/src/lib/ffi/ffi_rng.cpp
index 617723cdd..7bb365a24 100644
--- a/src/lib/ffi/ffi_rng.cpp
+++ b/src/lib/ffi/ffi_rng.cpp
@@ -1,5 +1,6 @@
 /*
 * (C) 2015,2017 Jack Lloyd
+* (C) 2021 René Fischer
 *
 * Botan is released under the Simplified BSD License (see license.txt)
 */
@@ -10,6 +11,8 @@
 #include <botan/system_rng.h>
 #include <botan/auto_rng.h>
 
+#include <functional>
+
 #if defined(BOTAN_HAS_PROCESSOR_RNG)
    #include <botan/processor_rng.h>
 #endif
@@ -57,6 +60,101 @@ int botan_rng_init(botan_rng_t* rng_out, const char* rng_type)
       });
    }
 
+int botan_rng_init_custom(botan_rng_t* rng_out, const char* rng_name, void* context,
+                          int(* get_cb)(void* context, uint8_t* out, size_t out_len),
+                          int(* add_entropy_cb)(void* context, const uint8_t input[], size_t length),
+                          void(* destroy_cb)(void* context))
+{
+return ffi_guard_thunk(__func__,[=]() -> int {
+   if(rng_out == nullptr)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+
+   if(rng_name == nullptr)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+
+   if(get_cb == nullptr)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+
+   class Custom_RNG : public Botan::RandomNumberGenerator
+      {
+      public:
+         Custom_RNG(const std::string& name, void* context,
+                    int(* get_cb)(void* context, uint8_t* out, size_t out_len),
+                    int(* add_entropy_cb)(void* context, const uint8_t input[], size_t length),
+                    void(* destroy_cb)(void* context)) :
+            m_name(name)
+            {
+               m_context = context;
+               m_get_cb = get_cb;
+               m_add_entropy_cb = add_entropy_cb;
+               m_destroy_cb = destroy_cb;
+            }
+
+         ~Custom_RNG()
+         {
+            if(m_destroy_cb)
+            {
+               m_destroy_cb(m_context);
+            }
+         }
+
+         void randomize(uint8_t output[], size_t length) override
+         {
+            int rc = m_get_cb(m_context, output, length);
+            if(rc)
+            {
+               throw Botan::Invalid_State("Failed to get random from C callback, rc=" + std::to_string(rc));
+            }
+         }
+
+         bool accepts_input() const override
+         {
+            return m_add_entropy_cb != nullptr;
+         }
+
+         void add_entropy(const uint8_t input[], size_t length) override
+         {
+            if(m_add_entropy_cb == nullptr)
+            {
+               return;
+            }
+
+            int rc = m_add_entropy_cb(m_context, input, length);
+            if(rc)
+            {
+               throw Botan::Invalid_State("Failed to add entropy via C callback, rc=" + std::to_string(rc));
+            }
+         }
+
+         std::string name() const override
+         {
+            return m_name;
+         }
+
+         void clear() override
+         {
+         }
+
+         bool is_seeded() const override
+         {
+            return true;
+         }
+
+      private:
+         std::string m_name;
+         void* m_context;
+         std::function<int(void* context, uint8_t* out, size_t out_len)> m_get_cb;
+         std::function<int(void* context, const uint8_t input[], size_t length)> m_add_entropy_cb;
+         std::function<void(void* context)> m_destroy_cb;
+   };
+
+   std::unique_ptr<Botan::RandomNumberGenerator> rng(new Custom_RNG(rng_name, context, get_cb, add_entropy_cb, destroy_cb));
+
+   *rng_out = new botan_rng_struct(rng.release());
+   return BOTAN_FFI_SUCCESS;
+   });
+}
+
 int botan_rng_destroy(botan_rng_t rng)
    {
    return BOTAN_FFI_CHECKED_DELETE(rng);


Test patch:
diff --git a/src/tests/test_ffi.cpp b/src/tests/test_ffi.cpp
index eef8a39d7..90a3a0760 100644
--- a/src/tests/test_ffi.cpp
+++ b/src/tests/test_ffi.cpp
@@ -201,6 +201,7 @@ class FFI_Unit_Tests final : public Test
          botan_rng_t system_rng;
          botan_rng_t rdrand_rng = nullptr;
          botan_rng_t null_rng;
+         botan_rng_t custom_rng;
 
          TEST_FFI_FAIL("invalid rng type", botan_rng_init, (&rng, "invalid_type"));
 
@@ -247,6 +248,50 @@ class FFI_Unit_Tests final : public Test
             TEST_FFI_OK(botan_rng_add_entropy, (rng, not_really_entropy, 32));
             }
 
+         size_t cb_counter = 0;
+
+         auto custom_get_cb = [](void* context, uint8_t* out, size_t out_len) -> int
+            {
+            Botan::set_mem(out, out_len, 0x12);
+            BOTAN_UNUSED(out, out_len);
+            (*(static_cast<size_t*>(context)))++;
+            return 0;
+            };
+
+         auto custom_add_entropy_cb = [](void* context, const uint8_t input[], size_t length) -> int
+            {
+            BOTAN_UNUSED(input, length);
+            (*(static_cast<size_t*>(context)))++;
+            return 0;
+            };
+
+         auto custom_destroy_cb = [](void* context) -> void
+            {
+            (*(static_cast<size_t*>(context)))++;
+            };
+
+         if(TEST_FFI_OK(botan_rng_init_custom, (&custom_rng, "custom rng", &cb_counter, custom_get_cb, custom_add_entropy_cb, custom_destroy_cb)))
+            {
+               Botan::clear_mem(outbuf.data(), outbuf.size());
+               TEST_FFI_OK(botan_rng_get, (custom_rng, outbuf.data(), outbuf.size()));
+               result.test_eq("custom_get_cb called", cb_counter, 1);
+               std::vector<uint8_t> pattern(outbuf.size(), 0x12);
+               result.test_eq("custom_get_cb returned bytes", pattern, outbuf);
+
+               TEST_FFI_OK(botan_rng_reseed, (custom_rng, 256));
+               result.test_eq("custom_add_entropy_cb called", cb_counter, 2);
+
+               TEST_FFI_OK(botan_rng_reseed_from_rng, (custom_rng, system_rng, 256));
+               result.test_eq("custom_add_entropy_cb called", cb_counter, 3);
+
+               uint8_t not_really_entropy[32] = { 0 };
+               TEST_FFI_OK(botan_rng_add_entropy, (custom_rng, not_really_entropy, 32));
+               result.test_eq("custom_add_entropy_cb called", cb_counter, 4);
+
+               TEST_FFI_OK(botan_rng_destroy, (custom_rng));
+               result.test_eq("custom_destroy_cb called", cb_counter, 5);
+            }
+
          TEST_FFI_OK(botan_rng_destroy, (rng));
          TEST_FFI_OK(botan_rng_destroy, (null_rng));
          TEST_FFI_OK(botan_rng_destroy, (system_rng));