proxygen
Random.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2011-present Facebook, Inc.
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/File.h>
25 #include <folly/FileUtil.h>
27 #include <folly/ThreadLocal.h>
31 #include <glog/logging.h>
32 
33 #ifdef _MSC_VER
34 #include <wincrypt.h> // @manual
35 #endif
36 
37 namespace folly {
38 
39 namespace {
40 
41 void readRandomDevice(void* data, size_t size) {
42 #ifdef _MSC_VER
43  static folly::once_flag flag;
44  static HCRYPTPROV cryptoProv;
45  folly::call_once(flag, [&] {
46  if (!CryptAcquireContext(
47  &cryptoProv,
48  nullptr,
49  nullptr,
50  PROV_RSA_FULL,
51  CRYPT_VERIFYCONTEXT)) {
52  if (GetLastError() == NTE_BAD_KEYSET) {
53  // Mostly likely cause of this is that no key container
54  // exists yet, so try to create one.
55  PCHECK(CryptAcquireContext(
56  &cryptoProv, nullptr, nullptr, PROV_RSA_FULL, CRYPT_NEWKEYSET));
57  } else {
58  LOG(FATAL) << "Failed to acquire the default crypto context.";
59  }
60  }
61  });
62  CHECK(size <= std::numeric_limits<DWORD>::max());
63  PCHECK(CryptGenRandom(cryptoProv, (DWORD)size, (BYTE*)data));
64 #else
65  // Keep the random device open for the duration of the program.
66  static int randomFd = ::open("/dev/urandom", O_RDONLY | O_CLOEXEC);
67  PCHECK(randomFd >= 0);
68  auto bytesRead = readFull(randomFd, data, size);
69  PCHECK(bytesRead >= 0 && size_t(bytesRead) == size);
70 #endif
71 }
72 
73 class BufferedRandomDevice {
74  public:
75  static once_flag flag;
76  static constexpr size_t kDefaultBufferSize = 128;
77 
78  static void notifyNewGlobalEpoch() {
79  globalEpoch_.fetch_add(1, std::memory_order_relaxed);
80  }
81 
82  explicit BufferedRandomDevice(size_t bufferSize = kDefaultBufferSize);
83 
84  void get(void* data, size_t size) {
85  auto const globalEpoch = globalEpoch_.load(std::memory_order_relaxed);
86  if (LIKELY(globalEpoch == epoch_ && size <= remaining())) {
87  memcpy(data, ptr_, size);
88  ptr_ += size;
89  } else {
90  getSlow(static_cast<unsigned char*>(data), size);
91  }
92  }
93 
94  private:
95  void getSlow(unsigned char* data, size_t size);
96 
97  inline size_t remaining() const {
98  return size_t(buffer_.get() + bufferSize_ - ptr_);
99  }
100 
101  static std::atomic<size_t> globalEpoch_;
102 
103  size_t epoch_{size_t(-1)}; // refill on first use
104  const size_t bufferSize_;
105  std::unique_ptr<unsigned char[]> buffer_;
106  unsigned char* ptr_;
107 };
108 
110 std::atomic<size_t> BufferedRandomDevice::globalEpoch_{0};
111 struct RandomTag {};
112 
113 BufferedRandomDevice::BufferedRandomDevice(size_t bufferSize)
114  : bufferSize_(bufferSize),
115  buffer_(new unsigned char[bufferSize]),
116  ptr_(buffer_.get() + bufferSize) { // refill on first use
117  call_once(flag, [this]() {
119  this,
120  /*prepare*/ []() { return true; },
121  /*parent*/ []() {},
122  /*child*/
123  []() {
124  // Ensure child and parent do not share same entropy pool.
125  BufferedRandomDevice::notifyNewGlobalEpoch();
126  });
127  });
128 }
129 
130 void BufferedRandomDevice::getSlow(unsigned char* data, size_t size) {
131  auto const globalEpoch = globalEpoch_.load(std::memory_order_relaxed);
132  if (globalEpoch != epoch_) {
134  ptr_ = buffer_.get() + bufferSize_;
135  }
136 
137  DCHECK_GT(size, remaining());
138  if (size >= bufferSize_) {
139  // Just read directly.
140  readRandomDevice(data, size);
141  return;
142  }
143 
144  size_t copied = remaining();
145  memcpy(data, ptr_, copied);
146  data += copied;
147  size -= copied;
148 
149  // refill
150  readRandomDevice(buffer_.get(), bufferSize_);
151  ptr_ = buffer_.get();
152 
153  memcpy(data, ptr_, size);
154  ptr_ += size;
155 }
156 
157 } // namespace
158 
159 void Random::secureRandom(void* data, size_t size) {
161  Single::get().get(data, size);
162 }
163 
165  struct Wrapper {
167  };
169  return Single::get().object();
170 }
171 } // namespace folly
const size_t bufferSize_
Definition: Random.cpp:104
LogLevel max
Definition: LogLevel.cpp:31
static std::enable_if< std::is_integral< T >::value &&!std::is_same< T, bool >::value, T >::type secureRandom()
Definition: Random.h:112
uint32_t result_type
Definition: Random.h:54
#define LIKELY(x)
Definition: Likely.h:47
ssize_t readFull(int fd, void *buf, size_t count)
Definition: FileUtil.cpp:126
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
static constexpr size_t kDefaultBufferSize
Definition: Random.cpp:76
static once_flag flag
Definition: Random.cpp:75
size_t epoch_
Definition: Random.cpp:103
FOLLY_ALWAYS_INLINE void call_once(basic_once_flag< Mutex, Atom > &flag, F &&f, Args &&...args)
Definition: CallOnce.h:56
constexpr auto size(C const &c) -> decltype(c.size())
Definition: Access.h:45
static std::atomic< size_t > globalEpoch_
Definition: Random.cpp:101
constexpr auto data(C &c) -> decltype(c.data())
Definition: Access.h:71
basic_once_flag< SharedMutex > once_flag
Definition: CallOnce.h:109
std::mt19937 DefaultGenerator
Definition: Random.h:97
result_type operator()()
Definition: Random.cpp:164
static RNG create()
PUSHMI_INLINE_VAR constexpr detail::get_fn< T > get
Definition: submit.h:391
std::unique_ptr< unsigned char[]> buffer_
Definition: Random.cpp:105
static void registerHandler(void *object, folly::Function< bool()> prepare, folly::Function< void()> parent, folly::Function< void()> child)
Definition: AtFork.cpp:108
unsigned char * ptr_
Definition: Random.cpp:106