1 | /* |
2 | * Copyright (c) Facebook, Inc. and its affiliates. |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | |
17 | #include <folly/Random.h> |
18 | |
19 | #include <array> |
20 | #include <atomic> |
21 | #include <mutex> |
22 | #include <random> |
23 | |
24 | #include <folly/CppAttributes.h> |
25 | #include <folly/File.h> |
26 | #include <folly/FileUtil.h> |
27 | #include <folly/SingletonThreadLocal.h> |
28 | #include <folly/ThreadLocal.h> |
29 | #include <folly/detail/FileUtilDetail.h> |
30 | #include <folly/portability/Config.h> |
31 | #include <folly/portability/SysTime.h> |
32 | #include <folly/portability/Unistd.h> |
33 | #include <glog/logging.h> |
34 | |
35 | #ifdef _MSC_VER |
36 | #include <wincrypt.h> // @manual |
37 | #endif |
38 | |
39 | #if FOLLY_HAVE_GETRANDOM |
40 | #include <sys/random.h> |
41 | #endif |
42 | |
43 | namespace folly { |
44 | |
45 | namespace { |
46 | |
47 | void readRandomDevice(void* data, size_t size) { |
48 | #ifdef _MSC_VER |
49 | static auto const cryptoProv = [] { |
50 | HCRYPTPROV prov; |
51 | if (!CryptAcquireContext( |
52 | &prov, nullptr, nullptr, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { |
53 | if (GetLastError() == NTE_BAD_KEYSET) { |
54 | // Mostly likely cause of this is that no key container |
55 | // exists yet, so try to create one. |
56 | PCHECK(CryptAcquireContext( |
57 | &prov, nullptr, nullptr, PROV_RSA_FULL, CRYPT_NEWKEYSET)); |
58 | } else { |
59 | LOG(FATAL) << "Failed to acquire the default crypto context." ; |
60 | } |
61 | } |
62 | return prov; |
63 | }(); |
64 | CHECK(size <= std::numeric_limits<DWORD>::max()); |
65 | PCHECK(CryptGenRandom(cryptoProv, (DWORD)size, (BYTE*)data)); |
66 | #else |
67 | ssize_t bytesRead = 0; |
68 | auto gen = [](int, void* buf, size_t buflen) { |
69 | #if FOLLY_HAVE_GETRANDOM |
70 | auto flags = 0u; |
71 | return ::getrandom(buf, buflen, flags); |
72 | #else |
73 | [](...) {}(buf, buflen); |
74 | errno = ENOSYS; |
75 | return -1; |
76 | #endif |
77 | }; |
78 | bytesRead = fileutil_detail::wrapFull(gen, -1, data, size); |
79 | if (bytesRead == -1 && errno == ENOSYS) { |
80 | // Keep the random device open for the duration of the program. |
81 | static int randomFd = ::open("/dev/urandom" , O_RDONLY | O_CLOEXEC); |
82 | PCHECK(randomFd >= 0); |
83 | bytesRead = readFull(randomFd, data, size); |
84 | } |
85 | PCHECK(bytesRead >= 0); |
86 | CHECK_EQ(size_t(bytesRead), size); |
87 | #endif |
88 | } |
89 | |
90 | class BufferedRandomDevice { |
91 | public: |
92 | static constexpr size_t kDefaultBufferSize = 128; |
93 | |
94 | static void notifyNewGlobalEpoch() { |
95 | globalEpoch_.fetch_add(1, std::memory_order_relaxed); |
96 | } |
97 | |
98 | explicit BufferedRandomDevice(size_t bufferSize = kDefaultBufferSize); |
99 | |
100 | void get(void* data, size_t size) { |
101 | auto const globalEpoch = globalEpoch_.load(std::memory_order_relaxed); |
102 | if (LIKELY(globalEpoch == epoch_ && size <= remaining())) { |
103 | memcpy(data, ptr_, size); |
104 | ptr_ += size; |
105 | } else { |
106 | getSlow(static_cast<unsigned char*>(data), size); |
107 | } |
108 | } |
109 | |
110 | private: |
111 | void getSlow(unsigned char* data, size_t size); |
112 | |
113 | inline size_t remaining() const { |
114 | return size_t(buffer_.get() + bufferSize_ - ptr_); |
115 | } |
116 | |
117 | static std::atomic<size_t> globalEpoch_; |
118 | |
119 | size_t epoch_{size_t(-1)}; // refill on first use |
120 | const size_t bufferSize_; |
121 | std::unique_ptr<unsigned char[]> buffer_; |
122 | unsigned char* ptr_; |
123 | }; |
124 | |
125 | std::atomic<size_t> BufferedRandomDevice::globalEpoch_{0}; |
126 | struct RandomTag {}; |
127 | |
128 | BufferedRandomDevice::BufferedRandomDevice(size_t bufferSize) |
129 | : bufferSize_(bufferSize), |
130 | buffer_(new unsigned char[bufferSize]), |
131 | ptr_(buffer_.get() + bufferSize) { // refill on first use |
132 | FOLLY_MAYBE_UNUSED static auto const init = [] { |
133 | detail::AtFork::registerHandler( |
134 | nullptr, |
135 | /*prepare*/ []() { return true; }, |
136 | /*parent*/ []() {}, |
137 | /*child*/ |
138 | []() { |
139 | // Ensure child and parent do not share same entropy pool. |
140 | BufferedRandomDevice::notifyNewGlobalEpoch(); |
141 | }); |
142 | return 0; |
143 | }(); |
144 | } |
145 | |
146 | void BufferedRandomDevice::getSlow(unsigned char* data, size_t size) { |
147 | auto const globalEpoch = globalEpoch_.load(std::memory_order_relaxed); |
148 | if (globalEpoch != epoch_) { |
149 | epoch_ = globalEpoch_; |
150 | ptr_ = buffer_.get() + bufferSize_; |
151 | } |
152 | |
153 | DCHECK_GT(size, remaining()); |
154 | if (size >= bufferSize_) { |
155 | // Just read directly. |
156 | readRandomDevice(data, size); |
157 | return; |
158 | } |
159 | |
160 | size_t copied = remaining(); |
161 | memcpy(data, ptr_, copied); |
162 | data += copied; |
163 | size -= copied; |
164 | |
165 | // refill |
166 | readRandomDevice(buffer_.get(), bufferSize_); |
167 | ptr_ = buffer_.get(); |
168 | |
169 | memcpy(data, ptr_, size); |
170 | ptr_ += size; |
171 | } |
172 | |
173 | } // namespace |
174 | |
175 | void Random::secureRandom(void* data, size_t size) { |
176 | using Single = SingletonThreadLocal<BufferedRandomDevice, RandomTag>; |
177 | Single::get().get(data, size); |
178 | } |
179 | |
180 | ThreadLocalPRNG::result_type ThreadLocalPRNG::operator()() { |
181 | struct Wrapper { |
182 | Random::DefaultGenerator object{Random::create()}; |
183 | }; |
184 | using Single = SingletonThreadLocal<Wrapper, RandomTag>; |
185 | return Single::get().object(); |
186 | } |
187 | } // namespace folly |
188 | |