diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 0cc40bb2b..18e3208be 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -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 index 000000000..cd83d2f8f --- /dev/null +++ b/grub-core/lib/i386/random.c @@ -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 . + */ + +#include +#include +#include +#include +#include + +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 index 000000000..43b966438 --- /dev/null +++ b/grub-core/lib/random.c @@ -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 . + */ + +#include +#include +#include +#include +#include + +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 index 000000000..4b75c0e2b --- /dev/null +++ b/include/grub/random.h @@ -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 . + */ + +#ifndef GRUB_RANDOM_HEADER +#define GRUB_RANDOM_HEADER 1 + +#include +#include + +/* 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