More completely seed UUID::Random()

Generate enuogh randomness with std::random_device to fully seed the
internal state of an std::mt19937. This ensures that even the first
UUID::Random() call after boot has full entropy, so long as
`std::random_device` is non-deterministic.

Also, initialize the random number generator during AOS initialization.

Change-Id: Ie09d85f09f7969bcd47576a577b4415d39186233
Signed-off-by: James Kuszmaul <james.kuszmaul@bluerivertech.com>
diff --git a/aos/uuid.cc b/aos/uuid.cc
index 4d3aa26..9da2cbf 100644
--- a/aos/uuid.cc
+++ b/aos/uuid.cc
@@ -64,24 +64,45 @@
   }
 }
 
-uint32_t RandomSeed() {
-  std::random_device rd;
-  return rd();
-}
 }  // namespace
 
+namespace internal {
+std::mt19937 FullySeededRandomGenerator() {
+  // Total bits that the mt19937 has internally that we could plausibly
+  // initialize with.
+  // The internal state ends up being ~1200 bytes, which is significantly more
+  // than the 128 bits we want for UUIDs, but since we should only need to
+  // generate this randomness once, it should be fine.
+  // If the performance cost ends up causing issues, then we can revisit the
+  // need to *fully* seed the twister.
+  constexpr size_t kInternalEntropy =
+      std::mt19937::state_size * sizeof(std::mt19937::result_type);
+  // Number, rounded up, of random values required.
+  constexpr size_t kSeedsRequired =
+      ((kInternalEntropy - 1) / sizeof(std::random_device::result_type)) + 1;
+  std::random_device random_device;
+// Older LLVM libstdc++'s just return 0 for the random device entropy.
+#if !defined(__clang__) || (__clang_major__ > 13)
+  CHECK_EQ(sizeof(std::random_device::result_type) * 8, random_device.entropy())
+      << ": Does your random_device actually support generating entropy?";
+#endif
+  std::array<std::random_device::result_type, kSeedsRequired> random_data;
+  std::generate(std::begin(random_data), std::end(random_data),
+                std::ref(random_device));
+  std::seed_seq seeds(std::begin(random_data), std::end(random_data));
+  return std::mt19937(seeds);
+}
+}  // namespace internal
+
 UUID UUID::Random() {
   // Note: This only provides 32 bits of randomness to each thread. However, by
   // keeping persistent pseudo-random number generators, we can increase the
   // overall randomness of each call to Random() (since, essentially, the
   // randomness is coming from the combination of the initial seed + the number
   // of times that Random() has been called in the given thread).
-  // TODO(james): Seed with a minimum of 128 bits of randomness, or even the
-  // full 624 bits of the internal mersenne twister state (see
-  // https://codereview.stackexchange.com/questions/109260/seed-stdmt19937-from-stdrandom-device/109266#109266).
   //
   // thread_local to guarantee safe use of the generator itself.
-  thread_local std::mt19937 gen(RandomSeed());
+  thread_local std::mt19937 gen(internal::FullySeededRandomGenerator());
 
   std::uniform_int_distribution<> dis(0, 255);
   UUID result;