1 /* $NetBSD: prng.c,v 1.5 2021/05/04 21:13:38 khorben Exp $ */ 2 3 /* 4 * Copyright (c) 2017-2020 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 size_t count = 0; 88 89 biml = 90 (struct btinfo_modulelist *)prng_lookup_bootinfo(BTINFO_MODULELIST); 91 if (biml == NULL) { 92 return; 93 } 94 95 bi = (struct bi_modulelist_entry *)((uint8_t *)biml + sizeof(*biml)); 96 bimax = bi + biml->num; 97 for (; bi < bimax; bi++) { 98 if (bi->type != BI_MODULE_RND) { 99 continue; 100 } 101 if (bi->len != sizeof(rndsave_t)) { 102 print_state(STATE_WARNING, 103 "size mismatch in entropy file"); 104 continue; 105 } 106 rndsave = (rndsave_t *)(vaddr_t)bi->base; 107 108 /* check the signature */ 109 SHA1Init(&sig); 110 SHA1Update(&sig, (uint8_t *)&rndsave->entropy, 111 sizeof(rndsave->entropy)); 112 SHA1Update(&sig, rndsave->data, sizeof(rndsave->data)); 113 SHA1Final(digest, &sig); 114 if (memcmp(digest, rndsave->digest, sizeof(digest))) { 115 print_state(STATE_WARNING, 116 "bad SHA1 checksum in entropy file"); 117 continue; 118 } 119 120 SHA512_Update(ctx, rndsave->data, sizeof(rndsave->data)); 121 count++; 122 } 123 if (count == 0) 124 print_state(STATE_WARNING, "No entropy file could be loaded"); 125 } 126 127 /* 128 * Add 32 bytes of rdseed/rdrand and 8 bytes of rdtsc to the context. 129 */ 130 static void 131 prng_get_entropy_data(SHA512_CTX *ctx) 132 { 133 uint64_t buf[8], val; 134 size_t i; 135 136 if (has_rdseed) { 137 for (i = 0; i < 8; i++) { 138 if (rdseed(&buf[i]) == -1) { 139 break; 140 } 141 } 142 SHA512_Update(ctx, (uint8_t *)buf, i * sizeof(uint64_t)); 143 } else if (has_rdrand) { 144 for (i = 0; i < 8; i++) { 145 if (rdrand(&buf[i]) == -1) { 146 break; 147 } 148 } 149 SHA512_Update(ctx, (uint8_t *)buf, i * sizeof(uint64_t)); 150 } 151 152 val = rdtsc(); 153 SHA512_Update(ctx, (uint8_t *)&val, sizeof(val)); 154 } 155 156 void 157 prng_init(void) 158 { 159 extern int cpuid_level; 160 uint8_t digest[SHA512_DIGEST_LENGTH]; 161 SHA512_CTX ctx; 162 u_int descs[4]; 163 164 memset(&rng, 0, sizeof(rng)); 165 166 /* detect cpu features */ 167 if (cpuid_level >= 0x07) { 168 cpuid(0x07, 0x00, descs); 169 has_rdseed = (descs[1] & CPUID_SEF_RDSEED) != 0; 170 } 171 if (cpuid_level >= 0x01) { 172 cpuid(0x01, 0x00, descs); 173 has_rdrand = (descs[2] & CPUID2_RDRAND) != 0; 174 } 175 if (!has_rdseed && !has_rdrand) 176 print_state(STATE_WARNING, "No CPU entropy feature detected"); 177 178 SHA512_Init(&ctx); 179 prng_get_entropy_file(&ctx); 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 187 static void 188 prng_round(void) 189 { 190 uint8_t digest[SHA512_DIGEST_LENGTH]; 191 SHA512_CTX ctx; 192 193 SHA512_Init(&ctx); 194 SHA512_Update(&ctx, rng.state, RNGSTATE_SIZE); 195 prng_get_entropy_data(&ctx); 196 SHA512_Final(digest, &ctx); 197 198 memcpy(rng.state, digest, RNGSTATE_SIZE); 199 memcpy(rng.data, digest + RNGSTATE_SIZE, RNGDATA_SIZE); 200 201 rng.nused = 0; 202 } 203 204 void 205 prng_get_rand(void *buf, size_t sz) 206 { 207 uint8_t *ptr = (uint8_t *)buf; 208 size_t consumed; 209 210 ASSERT(sz <= RNGDATA_SIZE); 211 if (rng.nused + sz > RNGDATA_SIZE) { 212 /* Fill what can be */ 213 consumed = RNGDATA_SIZE - rng.nused; 214 memcpy(ptr, &rng.data[rng.nused], consumed); 215 216 /* Go through another round */ 217 prng_round(); 218 219 /* Fill the rest */ 220 memcpy(ptr + consumed, &rng.data[rng.nused], 221 sz - consumed); 222 223 rng.nused += (sz - consumed); 224 } else { 225 memcpy(ptr, &rng.data[rng.nused], sz); 226 rng.nused += sz; 227 } 228 } 229