Skip to content
File

Blob: src/workerd/util/entropy.c++

2.1 KB
1// Copyright (c) 2017-2022 Cloudflare, Inc.
2// Licensed under the Apache 2.0 license found in the LICENSE file or at:
3// https://opensource.org/licenses/Apache-2.0
4 
5#include "entropy.h"
6 
7#include <ncrypto.h>
8#include <openssl/crypto.h>
9#include <openssl/rand.h>
10 
11#include <kj/debug.h>
12#include <kj/exception.h>
13 
14#ifdef __unix__
15#include <unistd.h>
16#endif
17 
18namespace workerd {
19 
20void getEntropy(kj::ArrayPtr<kj::byte> output) {
21 static constexpr size_t BUFFER_SIZE = 4096;
22 struct BufferState {
23 kj::FixedArray<kj::byte, BUFFER_SIZE> store;
24 kj::ArrayPtr<kj::byte> data; // Starts empty to trigger initial fill
25#if defined(KJ_DEBUG) && defined(__unix__)
26 // Track the PID separately to detect cross-fork usage.
27 // This should be preserved across fork so we can detect PID changes.
28 pid_t lastSeenPid = 0;
29#endif
30 };
31 
32 thread_local BufferState state{};
33 
34#if defined(KJ_DEBUG) && defined(__unix__)
35 // Verify that getpid() hasn't changed. This code should be called strictly post-fork.
36 // If we see crashes here in tests, it means there's some pre-fork call to getEntropy()
37 // that needs to be removed.
38 pid_t currentPid = getpid();
39 if (state.lastSeenPid == 0) {
40 state.lastSeenPid = currentPid;
41 } else {
42 KJ_ASSERT(state.lastSeenPid == currentPid,
43 "PID changed from previous call to getEntropy() - this indicates a pre-fork call "
44 "to getEntropy() that should be removed",
45 state.lastSeenPid, currentPid);
46 }
47#endif
48 
49 while (output != nullptr) {
50 if (state.data == nullptr) {
51 ncrypto::ClearErrorOnReturn clearErrorOnReturn;
52 if (RAND_bytes(state.store.begin(), BUFFER_SIZE) != 1) {
53 KJ_FAIL_REQUIRE("RAND_bytes failed to generate random data");
54 }
55 
56 state.data = state.store.asPtr();
57 }
58 
59 size_t toCopy = kj::min(state.data.size(), output.size());
60 output.first(toCopy).copyFrom(state.data.first(toCopy));
61 // Zero out the source buffer after copying to prevent sensitive data from remaining in memory
62 OPENSSL_cleanse(state.data.first(toCopy).begin(), toCopy);
63 state.data = state.data.slice(toCopy);
64 output = output.slice(toCopy);
65 }
66}
67 
68} // namespace workerd