]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
Add RNG module.
authorVladimir Serbinenko <phcoder@gmail.com>
Fri, 12 Feb 2016 11:39:38 +0000 (12:39 +0100)
committerVladimir Serbinenko <phcoder@gmail.com>
Fri, 12 Feb 2016 11:39:38 +0000 (12:39 +0100)
grub-core/Makefile.core.def
grub-core/lib/i386/random.c [new file with mode: 0644]
grub-core/lib/random.c [new file with mode: 0644]
include/grub/random.h [new file with mode: 0644]

index 0cc40bb2b596e5d2dd1cf61ba3018f7f4a92ff9e..18e3208be724ead5722a09e09c1a820e6fb525ef 100644 (file)
@@ -1686,6 +1686,22 @@ module = {
   x86 = loader/xnu.c;
 
   enable = x86;
+}
+
+module = {
+  name = random;
+  x86 = lib/i386/random.c;
+  common = lib/random.c;
+
+  i386_multiboot = kern/i386/tsc_pmtimer.c;
+  i386_coreboot = kern/i386/tsc_pmtimer.c;
+  i386_pc = kern/i386/tsc_pmtimer.c;
+
+  enable = i386_multiboot;
+  enable = i386_coreboot;
+  enable = i386_pc;
+  enable = i386_efi;
+  enable = x86_64_efi;
 };
 
 module = {
diff --git a/grub-core/lib/i386/random.c b/grub-core/lib/i386/random.c
new file mode 100644 (file)
index 0000000..cd83d2f
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2016 Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/random.h>
+#include <grub/i386/io.h>
+#include <grub/i386/tsc.h>
+#include <grub/i386/pmtimer.h>
+#include <grub/acpi.h>
+
+static int have_tsc = -1, have_pmtimer = -1;
+static grub_port_t pmtimer_port;
+
+static int
+detect_pmtimer (void)
+{
+  struct grub_acpi_fadt *fadt;
+  fadt = grub_acpi_find_fadt ();
+  if (!fadt)
+    return 0;
+  pmtimer_port = fadt->pmtimer;
+  if (!pmtimer_port)
+    return 0;
+  return 1;
+}
+
+static int
+pmtimer_tsc_get_random_bit (void)
+{
+  /* It's hard to come up with figures about pmtimer and tsc jitter but
+     50 ppm seems to be typical. So we need 10^6/50 tsc cycles to get drift
+     of one tsc cycle. With TSC at least of 800 MHz it means 1/(50*800)
+     = 1/40000 s or about 3579545 / 40000 = 90 pmtimer ticks.
+     This gives us rate of 40000 bit/s or 5 kB/s.
+   */
+  grub_uint64_t tsc_diff;
+  tsc_diff = grub_pmtimer_wait_count_tsc (pmtimer_port, 90);
+  if (tsc_diff == 0)
+    {
+      have_pmtimer = 0;
+      return -1;
+    }
+  return tsc_diff & 1;
+}
+
+static int
+pmtimer_tsc_get_random_byte (void)
+{
+  grub_uint8_t ret = 0;
+  int i, c;
+  for (i = 0; i < 8; i++)
+    {
+      c = pmtimer_tsc_get_random_bit ();
+      if (c < 0)
+       return -1;
+      ret |= c << i;
+    }
+  return ret;
+}
+
+static int
+pmtimer_fill_buffer (void *buffer, grub_size_t sz)
+{
+  grub_uint8_t *p = buffer;
+  int c;
+  while (sz)
+    {
+      c = pmtimer_tsc_get_random_byte ();
+      if (c < 0)
+       return 0;
+      *p++ = c;
+      sz--;
+    }
+  return 1;
+}
+
+int
+grub_crypto_arch_get_random (void *buffer, grub_size_t sz)
+{
+  if (have_tsc == -1)
+    have_tsc = grub_cpu_is_tsc_supported ();
+  if (!have_tsc)
+    return 0;
+  if (have_pmtimer == -1)
+    have_pmtimer = detect_pmtimer ();
+  if (!have_pmtimer)
+    return 0;
+  return pmtimer_fill_buffer (buffer, sz);
+}
diff --git a/grub-core/lib/random.c b/grub-core/lib/random.c
new file mode 100644 (file)
index 0000000..43b9664
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2016 Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/random.h>
+#include <grub/dl.h>
+#include <grub/lib/hexdump.h>
+#include <grub/command.h>
+#include <grub/mm.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+grub_err_t
+grub_crypto_get_random (void *buffer, grub_size_t sz)
+{
+  /* This is an arbitrer between different methods.
+     TODO: Add more methods in the future.  */
+  /* TODO: Add some PRNG smartness to reduce damage from bad entropy. */
+  if (grub_crypto_arch_get_random (buffer, sz))
+    return GRUB_ERR_NONE;
+  return grub_error (GRUB_ERR_IO, "no random sources found");
+}
+
+static int
+get_num_digits (int val)
+{
+  int ret = 0;
+  while (val != 0)
+    {
+      ret++;
+      val /= 10;
+    }
+  if (ret == 0)
+    return 1;
+  return ret;
+}
+
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+
+static grub_err_t
+grub_cmd_hexdump_random (grub_command_t cmd __attribute__ ((unused)), int argc, char **args)
+{
+  grub_size_t length = 64;
+  grub_err_t err;
+  void *buffer;
+  grub_uint8_t *ptr;
+  int stats[256];
+  int i, digits = 2;
+  char template[10];
+
+  if (argc >= 1)
+    length = grub_strtoull (args[0], 0, 0);
+
+  if (length == 0)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "length pust be positive");
+
+  buffer = grub_malloc (length);
+  if (!buffer)
+    return grub_errno;
+
+  err = grub_crypto_get_random (buffer, length);
+  if (err)
+    {
+      grub_free (buffer);
+      return err;
+    }
+
+  hexdump (0, buffer, length);
+  grub_memset(stats, 0, sizeof(stats));
+  for (ptr = buffer; ptr < (grub_uint8_t *) buffer + length; ptr++)
+    stats[*ptr]++;
+  grub_printf ("Statistics:\n");
+  for (i = 0; i < 256; i++)
+    {
+      int z = get_num_digits (stats[i]);
+      if (z > digits)
+       digits = z;
+    }
+
+  grub_snprintf (template, sizeof (template), "%%0%dd ", digits);
+
+  for (i = 0; i < 256; i++)
+    {
+      grub_printf ("%s", template);//, stats[i]);
+      if ((i & 0xf) == 0xf)
+       grub_printf ("\n");
+    }
+
+  grub_free (buffer);
+
+  return 0;
+}
+
+static grub_command_t cmd;
+
+GRUB_MOD_INIT (random)
+{
+  cmd = grub_register_command ("hexdump_random", grub_cmd_hexdump_random,
+                             N_("[LENGTH]"),
+                             N_("Hexdump random data."));
+}
+
+GRUB_MOD_FINI (random)
+{
+  grub_unregister_command (cmd);
+}
diff --git a/include/grub/random.h b/include/grub/random.h
new file mode 100644 (file)
index 0000000..4b75c0e
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2016 Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_RANDOM_HEADER
+#define GRUB_RANDOM_HEADER 1
+
+#include <grub/types.h>
+#include <grub/err.h>
+
+/* Not peer-reviewed. May not be any better than string of zeros.  */
+grub_err_t
+grub_crypto_get_random (void *buffer, grub_size_t sz);
+
+/* Do not use directly. Use grub_crypto_get_random instead.  */
+int
+grub_crypto_arch_get_random (void *buffer, grub_size_t sz);
+
+#endif