Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/arch/amd64/stand/prekern Add a PRNG for the prekern, bas...



details:   https://anonhg.NetBSD.org/src/rev/abd433417be5
branches:  trunk
changeset: 357764:abd433417be5
user:      maxv <maxv%NetBSD.org@localhost>
date:      Sun Nov 26 11:01:09 2017 +0000

description:
Add a PRNG for the prekern, based on SHA512. The formula is basically:

        Y0   = SHA512(entropy-file, 256bit rdseed, 64bit rdtsc)
        Yn+1 = SHA512(256bit lowerhalf(Yn), 256bit rdseed, 64bit rdtsc)

On each round, random values are taken from the higher half of Yn. If
rdseed is not available, rdrand is used.

The SHA1 checksum of entropy-file is verified. However, the rndsave_t::data
field is not updated by the prekern, because the area is accessed via the
read-only view we created in locore. I like this design, so it will have
to be updated differently.

diffstat:

 sys/arch/amd64/stand/prekern/Makefile  |    4 +-
 sys/arch/amd64/stand/prekern/mm.c      |   15 +-
 sys/arch/amd64/stand/prekern/prekern.c |    7 +-
 sys/arch/amd64/stand/prekern/prekern.h |    6 +-
 sys/arch/amd64/stand/prekern/prng.c    |  212 +++++++++++++++++++++++++++++++++
 5 files changed, 229 insertions(+), 15 deletions(-)

diffs (truncated from 317 to 300 lines):

diff -r a1d64b95cfe9 -r abd433417be5 sys/arch/amd64/stand/prekern/Makefile
--- a/sys/arch/amd64/stand/prekern/Makefile     Sun Nov 26 10:21:20 2017 +0000
+++ b/sys/arch/amd64/stand/prekern/Makefile     Sun Nov 26 11:01:09 2017 +0000
@@ -1,7 +1,7 @@
-#      $NetBSD: Makefile,v 1.4 2017/11/17 07:07:52 maxv Exp $
+#      $NetBSD: Makefile,v 1.5 2017/11/26 11:01:09 maxv Exp $
 
 PROG=          prekern
-SRCS=          locore.S trap.S prekern.c mm.c console.c elf.c
+SRCS=          locore.S trap.S prekern.c mm.c console.c elf.c prng.c
 
 NOSSP=         # defined
 NOPIE=         # defined
diff -r a1d64b95cfe9 -r abd433417be5 sys/arch/amd64/stand/prekern/mm.c
--- a/sys/arch/amd64/stand/prekern/mm.c Sun Nov 26 10:21:20 2017 +0000
+++ b/sys/arch/amd64/stand/prekern/mm.c Sun Nov 26 11:01:09 2017 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: mm.c,v 1.18 2017/11/21 07:56:05 maxv Exp $     */
+/*     $NetBSD: mm.c,v 1.19 2017/11/26 11:01:09 maxv Exp $     */
 
 /*
  * Copyright (c) 2017 The NetBSD Foundation, Inc. All rights reserved.
@@ -196,13 +196,6 @@
        }
 }
 
-static uint64_t
-mm_rand_num64(void)
-{
-       /* XXX: yes, this is ridiculous, will be fixed soon */
-       return rdtsc();
-}
-
 static vaddr_t
 mm_randva_kregion(size_t size, size_t pagesz)
 {
@@ -213,7 +206,7 @@
        bool ok;
 
        while (1) {
-               rnd = mm_rand_num64();
+               prng_get_rand(&rnd, sizeof(rnd));
                randva = rounddown(KASLR_WINDOW_BASE +
                    rnd % (KASLR_WINDOW_SIZE - size), pagesz);
 
@@ -298,7 +291,7 @@
                return 0;
        }
 
-       rnd = mm_rand_num64();
+       prng_get_rand(&rnd, sizeof(rnd));
        offset = roundup(rnd % shiftsize, elfalign);
        ASSERT((va + offset) % elfalign == 0);
 
@@ -322,7 +315,7 @@
        size = elf_get_head_size((vaddr_t)kernpa_start);
        npages = size / PAGE_SIZE;
 
-       rnd = mm_rand_num64();
+       prng_get_rand(&rnd, sizeof(rnd));
        randva = rounddown(HEAD_WINDOW_BASE + rnd % (HEAD_WINDOW_SIZE - size),
            PAGE_SIZE);
        mm_map_tree(randva, randva + size);
diff -r a1d64b95cfe9 -r abd433417be5 sys/arch/amd64/stand/prekern/prekern.c
--- a/sys/arch/amd64/stand/prekern/prekern.c    Sun Nov 26 10:21:20 2017 +0000
+++ b/sys/arch/amd64/stand/prekern/prekern.c    Sun Nov 26 11:01:09 2017 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: prekern.c,v 1.6 2017/11/17 07:07:52 maxv Exp $ */
+/*     $NetBSD: prekern.c,v 1.7 2017/11/26 11:01:09 maxv Exp $ */
 
 /*
  * Copyright (c) 2017 The NetBSD Foundation, Inc. All rights reserved.
@@ -336,6 +336,11 @@
        print_state(true, "Prekern loaded");
 
        /*
+        * Init the PRNG.
+        */
+       prng_init();
+
+       /*
         * Relocate the kernel.
         */
        mm_map_kernel();
diff -r a1d64b95cfe9 -r abd433417be5 sys/arch/amd64/stand/prekern/prekern.h
--- a/sys/arch/amd64/stand/prekern/prekern.h    Sun Nov 26 10:21:20 2017 +0000
+++ b/sys/arch/amd64/stand/prekern/prekern.h    Sun Nov 26 11:01:09 2017 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: prekern.h,v 1.17 2017/11/26 10:21:20 maxv Exp $        */
+/*     $NetBSD: prekern.h,v 1.18 2017/11/26 11:01:09 maxv Exp $        */
 
 /*
  * Copyright (c) 2017 The NetBSD Foundation, Inc. All rights reserved.
@@ -111,3 +111,7 @@
 
 /* prekern.c */
 void fatal(char *);
+
+/* prng.c */
+void prng_init(void);
+void prng_get_rand(void *, size_t);
diff -r a1d64b95cfe9 -r abd433417be5 sys/arch/amd64/stand/prekern/prng.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/arch/amd64/stand/prekern/prng.c       Sun Nov 26 11:01:09 2017 +0000
@@ -0,0 +1,212 @@
+/*     $NetBSD: prng.c,v 1.1 2017/11/26 11:01:09 maxv Exp $    */
+
+/*
+ * Copyright (c) 2017 The NetBSD Foundation, Inc. All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Maxime Villard.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "prekern.h"
+#include <sys/sha1.h>
+#include <sys/sha2.h>
+
+#define _KERNEL
+#include <machine/bootinfo.h>
+#undef _KERNEL
+
+#define CPUID_SEF_RDSEED       __BIT(18)
+#define CPUID2_RDRAND  0x40000000
+static bool has_rdrand = false;
+static bool has_rdseed = false;
+
+#define RND_SAVEWORDS  128
+typedef struct {
+       uint32_t entropy;
+       uint8_t data[RND_SAVEWORDS * sizeof(uint32_t)];
+       uint8_t digest[SHA1_DIGEST_LENGTH];
+} rndsave_t;
+
+#define RNGSTATE_SIZE  (SHA512_DIGEST_LENGTH / 2)
+#define RNGDATA_SIZE   (SHA512_DIGEST_LENGTH / 2)
+struct {
+       uint8_t state[RNGSTATE_SIZE];
+       uint8_t data[RNGDATA_SIZE];
+       size_t nused;
+} rng;
+
+static struct btinfo_common *
+prng_lookup_bootinfo(int type)
+{
+       extern struct bootinfo bootinfo;
+       struct btinfo_common *bic;
+       bool found;
+       int i;
+
+       bic = (struct btinfo_common *)(bootinfo.bi_data);
+       found = false;
+       for (i = 0; i < bootinfo.bi_nentries && !found; i++) {
+               if (bic->type == type)
+                       found = true;
+               else
+                       bic = (struct btinfo_common *)
+                           ((uint8_t *)bic + bic->len);
+       }
+       return found ? bic : NULL;
+}
+
+static void
+prng_get_entropy_file(SHA512_CTX *ctx)
+{
+       struct bi_modulelist_entry *bi, *bimax;
+       struct btinfo_modulelist *biml;
+       uint8_t digest[SHA1_DIGEST_LENGTH];
+       rndsave_t *rndsave;
+       SHA1_CTX sig;
+
+       biml =
+           (struct btinfo_modulelist *)prng_lookup_bootinfo(BTINFO_MODULELIST);
+       if (biml == NULL) {
+               return;
+       }
+
+       bi = (struct bi_modulelist_entry *)((uint8_t *)biml + sizeof(*biml));
+       bimax = bi + biml->num;
+       for (; bi < bimax; bi++) {
+               if (bi->type != BI_MODULE_RND) {
+                       continue;
+               }
+               if (bi->len != sizeof(rndsave_t)) {
+                       fatal("rndsave_t size mismatch");
+               }
+               rndsave = (rndsave_t *)(vaddr_t)bi->base;
+
+               /* check the signature */
+               SHA1Init(&sig);
+               SHA1Update(&sig, (uint8_t *)&rndsave->entropy,
+                   sizeof(rndsave->entropy));
+               SHA1Update(&sig, rndsave->data, sizeof(rndsave->data));
+               SHA1Final(digest, &sig);
+               if (memcmp(digest, rndsave->digest, sizeof(digest))) {
+                       fatal("bad SHA1 checksum");
+               }
+
+               SHA512_Update(ctx, rndsave->data, sizeof(rndsave->data));
+       }
+}
+
+/*
+ * Add 32 bytes of rdseed/rdrand and 8 bytes of rdtsc to the context.
+ */
+static void
+prng_get_entropy_data(SHA512_CTX *ctx)
+{
+       uint64_t buf[8], val;
+       size_t i;
+
+       if (has_rdseed) {
+               for (i = 0; i < 8; i++) {
+                       if (rdseed(&buf[i]) == -1) {
+                               break;
+                       }
+               }
+               SHA512_Update(ctx, (uint8_t *)buf, i * sizeof(uint64_t));
+       } else if (has_rdrand) {
+               for (i = 0; i < 8; i++) {
+                       if (rdrand(&buf[i]) == -1) {
+                               break;
+                       }
+               }
+               SHA512_Update(ctx, (uint8_t *)buf, i * sizeof(uint64_t));
+       }
+
+       val = rdtsc();
+       SHA512_Update(ctx, (uint8_t *)&val, sizeof(val));
+}
+
+void
+prng_init(void)
+{
+       uint8_t digest[SHA512_DIGEST_LENGTH];
+       SHA512_CTX ctx;
+       u_int descs[4];
+
+       memset(&rng, 0, sizeof(rng));
+
+       cpuid(0x07, 0x00, descs);
+       has_rdseed = (descs[1] & CPUID_SEF_RDSEED) != 0;
+       cpuid(0x01, 0x00, descs);
+       has_rdrand = (descs[2] & CPUID2_RDRAND) != 0;
+
+       SHA512_Init(&ctx);
+       prng_get_entropy_file(&ctx);
+       prng_get_entropy_data(&ctx);
+       SHA512_Final(digest, &ctx);
+
+       memcpy(rng.state, digest, RNGSTATE_SIZE);
+       memcpy(rng.data, digest + RNGSTATE_SIZE, RNGDATA_SIZE);
+}
+
+static void
+prng_round(void)
+{
+       uint8_t digest[SHA512_DIGEST_LENGTH];
+       SHA512_CTX ctx;
+
+       SHA512_Init(&ctx);
+       SHA512_Update(&ctx, rng.state, RNGSTATE_SIZE);
+       prng_get_entropy_data(&ctx);
+       SHA512_Final(digest, &ctx);
+
+       memcpy(rng.state, digest, RNGSTATE_SIZE);
+       memcpy(rng.data, digest + RNGSTATE_SIZE, RNGDATA_SIZE);
+
+       rng.nused = 0;
+}
+
+void
+prng_get_rand(void *buf, size_t sz)
+{
+       uint8_t *ptr = (uint8_t *)buf;
+       size_t consumed;
+
+       ASSERT(sz <= RNGDATA_SIZE);
+       if (rng.nused + sz > RNGDATA_SIZE) {



Home | Main Index | Thread Index | Old Index