1// Copyright 2016 The Fuchsia Authors 2// 3// Use of this source code is governed by a MIT-style 4// license that can be found in the LICENSE file or at 5// https://opensource.org/licenses/MIT 6 7#include <lib/crypto/global_prng.h> 8 9#include <assert.h> 10#include <ctype.h> 11#include <err.h> 12#include <explicit-memory/bytes.h> 13#include <fbl/algorithm.h> 14#include <kernel/auto_lock.h> 15#include <kernel/cmdline.h> 16#include <kernel/mutex.h> 17#include <lib/crypto/cryptolib.h> 18#include <lib/crypto/entropy/collector.h> 19#include <lib/crypto/entropy/jitterentropy_collector.h> 20#include <lib/crypto/entropy/hw_rng_collector.h> 21#include <lib/crypto/entropy/quality_test.h> 22#include <lib/crypto/prng.h> 23#include <zxcpp/new.h> 24#include <lk/init.h> 25#include <string.h> 26#include <trace.h> 27 28#define LOCAL_TRACE 0 29 30namespace crypto { 31 32namespace GlobalPRNG { 33 34static PRNG* kGlobalPrng = nullptr; 35 36PRNG* GetInstance() { 37 ASSERT(kGlobalPrng); 38 return kGlobalPrng; 39} 40 41// Returns true if the kernel cmdline provided at least PRNG::kMinEntropy bytes 42// of entropy, and false otherwise. 43// 44// TODO(security): Remove this in favor of virtio-rng once it is available and 45// we decide we don't need it for getting entropy from elsewhere. 46static bool IntegrateCmdlineEntropy() { 47 const char* entropy = cmdline_get("kernel.entropy-mixin"); 48 if (!entropy) { 49 return false; 50 } 51 52 const size_t kMaxEntropyArgumentLen = 128; 53 const size_t hex_len = fbl::min(strlen(entropy), kMaxEntropyArgumentLen); 54 55 for (size_t i = 0; i < hex_len; ++i) { 56 if (!isxdigit(entropy[i])) { 57 panic("Invalid entropy string: idx %zu is not an ASCII hex digit\n", i); 58 } 59 } 60 61 uint8_t digest[clSHA256_DIGEST_SIZE]; 62 clSHA256(entropy, static_cast<int>(hex_len), digest); 63 kGlobalPrng->AddEntropy(digest, sizeof(digest)); 64 65 // We have a pointer to const, but it's actually a pointer to the 66 // mutable global state in __kernel_cmdline that is still live (it 67 // will be copied into the userboot bootstrap message later). So 68 // it's fully well-defined to cast away the const and mutate this 69 // here so the bits can't leak to userboot. While we're at it, 70 // prettify the result a bit so it's obvious what one is looking at. 71 mandatory_memset(const_cast<char*>(entropy), 'x', hex_len); 72 if (hex_len >= sizeof(".redacted=") - 1) { 73 memcpy(const_cast<char*>(entropy) - 1, 74 ".redacted=", sizeof(".redacted=") - 1); 75 } 76 77 const size_t entropy_added = fbl::max(hex_len / 2, sizeof(digest)); 78 LTRACEF("Collected %zu bytes of entropy from the kernel cmdline.\n", 79 entropy_added); 80 return (entropy_added >= PRNG::kMinEntropy); 81} 82 83// Returns true on success, false on failure. 84static bool SeedFrom(entropy::Collector* collector) { 85 uint8_t buf[PRNG::kMinEntropy] = {0}; 86 size_t remaining = collector->BytesNeeded(8 * PRNG::kMinEntropy); 87#if LOCAL_TRACE 88 { 89 char name[ZX_MAX_NAME_LEN]; 90 collector->get_name(name, sizeof(name)); 91 LTRACEF("About to collect %zu bytes of entropy from '%s'.\n", 92 remaining, name); 93 } 94#endif 95 while (remaining > 0) { 96 size_t result = collector->DrawEntropy( 97 buf, fbl::min(sizeof(buf), remaining)); 98 if (result == 0) { 99 LTRACEF("Collected 0 bytes; aborting. " 100 "There were %zu bytes remaining to collect.\n", 101 remaining); 102 return false; 103 } 104 // TODO(ZX-1007): don't assume that every byte of entropy that's added 105 // has a full 8 bits worth of entropy 106 kGlobalPrng->AddEntropy(buf, result); 107 mandatory_memset(buf, 0, sizeof(buf)); 108 remaining -= result; 109 } 110 LTRACEF("Successfully collected entropy.\n"); 111 return true; 112} 113 114// Instantiates the global PRNG (in non-thread-safe mode) and seeds it. 115static void EarlyBootSeed(uint level) { 116 ASSERT(kGlobalPrng == nullptr); 117 118 // Before doing anything else, test our entropy collector. This is 119 // explicitly called here rather than in another init hook to ensure 120 // ordering (at level LK_INIT_LEVEL_TARGET_EARLY, but before the rest of 121 // EarlyBootSeed). 122 entropy::EarlyBootTest(); 123 124 // Statically allocate an array of bytes to put the PRNG into. We do this 125 // to control when the PRNG constructor is called. 126 // TODO(security): This causes the PRNG state to be in a fairly predictable 127 // place. Some aspects of KASLR will help with this, but we may 128 // additionally want to remap where this is later. 129 alignas(alignof(PRNG))static uint8_t prng_space[sizeof(PRNG)]; 130 kGlobalPrng = new (&prng_space) PRNG(nullptr, 0, PRNG::NonThreadSafeTag()); 131 132 // TODO(security): Have the PRNG reseed based on usage 133 134 unsigned int successful = 0; // number of successful entropy sources 135 entropy::Collector* collector; 136 if (entropy::HwRngCollector::GetInstance(&collector) == ZX_OK && 137 SeedFrom(collector)) { 138 successful++; 139 } 140 if (entropy::JitterentropyCollector::GetInstance(&collector) == ZX_OK && 141 SeedFrom(collector)) { 142 successful++; 143 } 144 145 if (IntegrateCmdlineEntropy()) { 146 successful++; 147 } 148 if (successful == 0) { 149 printf("WARNING: System has insufficient randomness. It is completely " 150 "unsafe to use this system for any cryptographic applications." 151 "\n"); 152 // TODO(security): *CRITICAL* This is a fallback for systems without RNG 153 // hardware that we should remove and attempt to do better. If this 154 // fallback is used, it breaks all cryptography used on the system. 155 // *CRITICAL* 156 uint8_t buf[PRNG::kMinEntropy] = {0}; 157 kGlobalPrng->AddEntropy(buf, sizeof(buf)); 158 return; 159 } else { 160 LTRACEF("Successfully collected entropy from %u sources.\n", 161 successful); 162 } 163} 164 165// Migrate the global PRNG to enter thread-safe mode. 166static void BecomeThreadSafe(uint level) { 167 GetInstance()->BecomeThreadSafe(); 168} 169 170} //namespace GlobalPRNG 171 172} // namespace crypto 173 174 175LK_INIT_HOOK(global_prng_seed, crypto::GlobalPRNG::EarlyBootSeed, 176 LK_INIT_LEVEL_TARGET_EARLY); 177 178LK_INIT_HOOK(global_prng_thread_safe, crypto::GlobalPRNG::BecomeThreadSafe, 179 LK_INIT_LEVEL_THREADING - 1) 180