1 /* $NetBSD: prng.c,v 1.2 2017/11/26 11:08:34 maxv Exp $ */ 2 3 /* 4 * Copyright (c) 2017 The NetBSD Foundation, Inc. All rights reserved. 5 * 6 * This code is derived from software contributed to The NetBSD Foundation 7 * by Maxime Villard. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 19 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 20 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 21 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 22 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 * POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include "prekern.h" 32 #include <sys/sha1.h> 33 #include <sys/sha2.h> 34 35 #define _KERNEL 36 #include <machine/bootinfo.h> 37 #undef _KERNEL 38 39 #define CPUID_SEF_RDSEED __BIT(18) 40 #define CPUID2_RDRAND 0x40000000 41 static bool has_rdrand = false; 42 static bool has_rdseed = false; 43 44 #define RND_SAVEWORDS 128 45 typedef struct { 46 uint32_t entropy; 47 uint8_t data[RND_SAVEWORDS * sizeof(uint32_t)]; 48 uint8_t digest[SHA1_DIGEST_LENGTH]; 49 } rndsave_t; 50 51 #define RNGSTATE_SIZE (SHA512_DIGEST_LENGTH / 2) 52 #define RNGDATA_SIZE (SHA512_DIGEST_LENGTH / 2) 53 struct { 54 uint8_t state[RNGSTATE_SIZE]; 55 uint8_t data[RNGDATA_SIZE]; 56 size_t nused; 57 } rng; 58 59 static struct btinfo_common * 60 prng_lookup_bootinfo(int type) 61 { 62 extern struct bootinfo bootinfo; 63 struct btinfo_common *bic; 64 bool found; 65 int i; 66 67 bic = (struct btinfo_common *)(bootinfo.bi_data); 68 found = false; 69 for (i = 0; i < bootinfo.bi_nentries && !found; i++) { 70 if (bic->type == type) 71 found = true; 72 else 73 bic = (struct btinfo_common *) 74 ((uint8_t *)bic + bic->len); 75 } 76 return found ? bic : NULL; 77 } 78 79 static void 80 prng_get_entropy_file(SHA512_CTX *ctx) 81 { 82 struct bi_modulelist_entry *bi, *bimax; 83 struct btinfo_modulelist *biml; 84 uint8_t digest[SHA1_DIGEST_LENGTH]; 85 rndsave_t *rndsave; 86 SHA1_CTX sig; 87 88 biml = 89 (struct btinfo_modulelist *)prng_lookup_bootinfo(BTINFO_MODULELIST); 90 if (biml == NULL) { 91 return; 92 } 93 94 bi = (struct bi_modulelist_entry *)((uint8_t *)biml + sizeof(*biml)); 95 bimax = bi + biml->num; 96 for (; bi < bimax; bi++) { 97 if (bi->type != BI_MODULE_RND) { 98 continue; 99 } 100 if (bi->len != sizeof(rndsave_t)) { 101 fatal("rndsave_t size mismatch"); 102 } 103 rndsave = (rndsave_t *)(vaddr_t)bi->base; 104 105 /* check the signature */ 106 SHA1Init(&sig); 107 SHA1Update(&sig, (uint8_t *)&rndsave->entropy, 108 sizeof(rndsave->entropy)); 109 SHA1Update(&sig, rndsave->data, sizeof(rndsave->data)); 110 SHA1Final(digest, &sig); 111 if (memcmp(digest, rndsave->digest, sizeof(digest))) { 112 fatal("bad SHA1 checksum"); 113 } 114 115 SHA512_Update(ctx, rndsave->data, sizeof(rndsave->data)); 116 } 117 } 118 119 /* 120 * Add 32 bytes of rdseed/rdrand and 8 bytes of rdtsc to the context. 121 */ 122 static void 123 prng_get_entropy_data(SHA512_CTX *ctx) 124 { 125 uint64_t buf[8], val; 126 size_t i; 127 128 if (has_rdseed) { 129 for (i = 0; i < 8; i++) { 130 if (rdseed(&buf[i]) == -1) { 131 break; 132 } 133 } 134 SHA512_Update(ctx, (uint8_t *)buf, i * sizeof(uint64_t)); 135 } else if (has_rdrand) { 136 for (i = 0; i < 8; i++) { 137 if (rdrand(&buf[i]) == -1) { 138 break; 139 } 140 } 141 SHA512_Update(ctx, (uint8_t *)buf, i * sizeof(uint64_t)); 142 } 143 144 val = rdtsc(); 145 SHA512_Update(ctx, (uint8_t *)&val, sizeof(val)); 146 } 147 148 void 149 prng_init(void) 150 { 151 uint8_t digest[SHA512_DIGEST_LENGTH]; 152 SHA512_CTX ctx; 153 u_int descs[4]; 154 155 memset(&rng, 0, sizeof(rng)); 156 157 /* detect cpu features */ 158 cpuid(0x07, 0x00, descs); 159 has_rdseed = (descs[1] & CPUID_SEF_RDSEED) != 0; 160 cpuid(0x01, 0x00, descs); 161 has_rdrand = (descs[2] & CPUID2_RDRAND) != 0; 162 163 SHA512_Init(&ctx); 164 prng_get_entropy_file(&ctx); 165 prng_get_entropy_data(&ctx); 166 SHA512_Final(digest, &ctx); 167 168 memcpy(rng.state, digest, RNGSTATE_SIZE); 169 memcpy(rng.data, digest + RNGSTATE_SIZE, RNGDATA_SIZE); 170 } 171 172 static void 173 prng_round(void) 174 { 175 uint8_t digest[SHA512_DIGEST_LENGTH]; 176 SHA512_CTX ctx; 177 178 SHA512_Init(&ctx); 179 SHA512_Update(&ctx, rng.state, RNGSTATE_SIZE); 180 prng_get_entropy_data(&ctx); 181 SHA512_Final(digest, &ctx); 182 183 memcpy(rng.state, digest, RNGSTATE_SIZE); 184 memcpy(rng.data, digest + RNGSTATE_SIZE, RNGDATA_SIZE); 185 186 rng.nused = 0; 187 } 188 189 void 190 prng_get_rand(void *buf, size_t sz) 191 { 192 uint8_t *ptr = (uint8_t *)buf; 193 size_t consumed; 194 195 ASSERT(sz <= RNGDATA_SIZE); 196 if (rng.nused + sz > RNGDATA_SIZE) { 197 /* Fill what can be */ 198 consumed = RNGDATA_SIZE - rng.nused; 199 memcpy(ptr, &rng.data[rng.nused], consumed); 200 201 /* Go through another round */ 202 prng_round(); 203 204 /* Fill the rest */ 205 memcpy(ptr + consumed, &rng.data[rng.nused], 206 sz - consumed); 207 208 rng.nused += (sz - consumed); 209 } else { 210 memcpy(ptr, &rng.data[rng.nused], sz); 211 rng.nused += sz; 212 } 213 } 214