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/hw_rng_collector.h>
20 #include <lib/crypto/entropy/jitterentropy_collector.h>
21 #include <lib/crypto/entropy/quality_test.h>
22 #include <lib/crypto/prng.h>
23 #include <lk/init.h>
24 #include <new>
25 #include <string.h>
26 #include <trace.h>
27
28 #define LOCAL_TRACE 0
29
30 namespace crypto {
31
32 namespace GlobalPRNG {
33
34 static PRNG* kGlobalPrng = nullptr;
35
GetInstance()36 PRNG* 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.
IntegrateCmdlineEntropy()46 static 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.
SeedFrom(entropy::Collector * collector)84 static 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.
EarlyBootSeed(uint level)115 static 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.
BecomeThreadSafe(uint level)166 static void BecomeThreadSafe(uint level) {
167 GetInstance()->BecomeThreadSafe();
168 }
169
170 } //namespace GlobalPRNG
171
172 } // namespace crypto
173
174
175 LK_INIT_HOOK(global_prng_seed, crypto::GlobalPRNG::EarlyBootSeed,
176 LK_INIT_LEVEL_TARGET_EARLY);
177
178 LK_INIT_HOOK(global_prng_thread_safe, crypto::GlobalPRNG::BecomeThreadSafe,
179 LK_INIT_LEVEL_THREADING - 1)
180