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