1/*
2 * libjingle
3 * Copyright 2004--2005, Google Inc.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 *  1. Redistributions of source code must retain the above copyright notice,
9 *     this list of conditions and the following disclaimer.
10 *  2. Redistributions in binary form must reproduce the above copyright notice,
11 *     this list of conditions and the following disclaimer in the documentation
12 *     and/or other materials provided with the distribution.
13 *  3. The name of the author may not be used to endorse or promote products
14 *     derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include "talk/base/helpers.h"
29
30#include <limits>
31
32#include "talk/base/sslconfig.h"
33#if defined(SSL_USE_OPENSSL)
34#include <openssl/rand.h>
35#elif defined(SSL_USE_NSS_RNG)
36#include "pk11func.h"
37#else
38#ifdef WIN32
39#define WIN32_LEAN_AND_MEAN
40#include <windows.h>
41#include <ntsecapi.h>
42#endif  // WIN32
43#endif
44
45#include "talk/base/base64.h"
46#include "talk/base/basictypes.h"
47#include "talk/base/logging.h"
48#include "talk/base/scoped_ptr.h"
49#include "talk/base/timeutils.h"
50
51// Protect against max macro inclusion.
52#undef max
53
54namespace talk_base {
55
56// Base class for RNG implementations.
57class RandomGenerator {
58 public:
59  virtual ~RandomGenerator() {}
60  virtual bool Init(const void* seed, size_t len) = 0;
61  virtual bool Generate(void* buf, size_t len) = 0;
62};
63
64#if defined(SSL_USE_OPENSSL)
65// The OpenSSL RNG. Need to make sure it doesn't run out of entropy.
66class SecureRandomGenerator : public RandomGenerator {
67 public:
68  SecureRandomGenerator() : inited_(false) {
69  }
70  ~SecureRandomGenerator() {
71  }
72  virtual bool Init(const void* seed, size_t len) {
73    // By default, seed from the system state.
74    if (!inited_) {
75      if (RAND_poll() <= 0) {
76        return false;
77      }
78      inited_ = true;
79    }
80    // Allow app data to be mixed in, if provided.
81    if (seed) {
82      RAND_seed(seed, len);
83    }
84    return true;
85  }
86  virtual bool Generate(void* buf, size_t len) {
87    if (!inited_ && !Init(NULL, 0)) {
88      return false;
89    }
90    return (RAND_bytes(reinterpret_cast<unsigned char*>(buf), len) > 0);
91  }
92
93 private:
94  bool inited_;
95};
96
97#elif defined(SSL_USE_NSS_RNG)
98// The NSS RNG.
99class SecureRandomGenerator : public RandomGenerator {
100 public:
101  SecureRandomGenerator() {}
102  ~SecureRandomGenerator() {}
103  virtual bool Init(const void* seed, size_t len) {
104    return true;
105  }
106  virtual bool Generate(void* buf, size_t len) {
107    return (PK11_GenerateRandom(reinterpret_cast<unsigned char*>(buf),
108                                static_cast<int>(len)) == SECSuccess);
109  }
110};
111
112#else
113#ifdef WIN32
114class SecureRandomGenerator : public RandomGenerator {
115 public:
116  SecureRandomGenerator() : advapi32_(NULL), rtl_gen_random_(NULL) {}
117  ~SecureRandomGenerator() {
118    FreeLibrary(advapi32_);
119  }
120
121  virtual bool Init(const void* seed, size_t seed_len) {
122    // We don't do any additional seeding on Win32, we just use the CryptoAPI
123    // RNG (which is exposed as a hidden function off of ADVAPI32 so that we
124    // don't need to drag in all of CryptoAPI)
125    if (rtl_gen_random_) {
126      return true;
127    }
128
129    advapi32_ = LoadLibrary(L"advapi32.dll");
130    if (!advapi32_) {
131      return false;
132    }
133
134    rtl_gen_random_ = reinterpret_cast<RtlGenRandomProc>(
135        GetProcAddress(advapi32_, "SystemFunction036"));
136    if (!rtl_gen_random_) {
137      FreeLibrary(advapi32_);
138      return false;
139    }
140
141    return true;
142  }
143  virtual bool Generate(void* buf, size_t len) {
144    if (!rtl_gen_random_ && !Init(NULL, 0)) {
145      return false;
146    }
147    return (rtl_gen_random_(buf, static_cast<int>(len)) != FALSE);
148  }
149
150 private:
151  typedef BOOL (WINAPI *RtlGenRandomProc)(PVOID, ULONG);
152  HINSTANCE advapi32_;
153  RtlGenRandomProc rtl_gen_random_;
154};
155
156#else
157
158#error No SSL implementation has been selected!
159
160#endif  // WIN32
161#endif
162
163// A test random generator, for predictable output.
164class TestRandomGenerator : public RandomGenerator {
165 public:
166  TestRandomGenerator() : seed_(7) {
167  }
168  ~TestRandomGenerator() {
169  }
170  virtual bool Init(const void* seed, size_t len) {
171    return true;
172  }
173  virtual bool Generate(void* buf, size_t len) {
174    for (size_t i = 0; i < len; ++i) {
175      static_cast<uint8*>(buf)[i] = static_cast<uint8>(GetRandom());
176    }
177    return true;
178  }
179
180 private:
181  int GetRandom() {
182    return ((seed_ = seed_ * 214013L + 2531011L) >> 16) & 0x7fff;
183  }
184  int seed_;
185};
186
187// TODO: Use Base64::Base64Table instead.
188static const char BASE64[64] = {
189  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
190  'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
191  'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
192  'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
193  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
194};
195
196namespace {
197
198// This round about way of creating a global RNG is to safe-guard against
199// indeterminant static initialization order.
200scoped_ptr<RandomGenerator>& GetGlobalRng() {
201  LIBJINGLE_DEFINE_STATIC_LOCAL(scoped_ptr<RandomGenerator>, global_rng,
202                                (new SecureRandomGenerator()));
203  return global_rng;
204}
205
206RandomGenerator& Rng() {
207  return *GetGlobalRng();
208}
209
210}  // namespace
211
212void SetRandomTestMode(bool test) {
213  if (!test) {
214    GetGlobalRng().reset(new SecureRandomGenerator());
215  } else {
216    GetGlobalRng().reset(new TestRandomGenerator());
217  }
218}
219
220bool InitRandom(int seed) {
221  return InitRandom(reinterpret_cast<const char*>(&seed), sizeof(seed));
222}
223
224bool InitRandom(const char* seed, size_t len) {
225  if (!Rng().Init(seed, len)) {
226    LOG(LS_ERROR) << "Failed to init random generator!";
227    return false;
228  }
229  return true;
230}
231
232std::string CreateRandomString(size_t len) {
233  std::string str;
234  CreateRandomString(len, &str);
235  return str;
236}
237
238bool CreateRandomString(size_t len,
239                        const char* table, int table_size,
240                        std::string* str) {
241  str->clear();
242  scoped_array<uint8> bytes(new uint8[len]);
243  if (!Rng().Generate(bytes.get(), len)) {
244    LOG(LS_ERROR) << "Failed to generate random string!";
245    return false;
246  }
247  str->reserve(len);
248  for (size_t i = 0; i < len; ++i) {
249    str->push_back(table[bytes[i] % table_size]);
250  }
251  return true;
252}
253
254bool CreateRandomString(size_t len, std::string* str) {
255  return CreateRandomString(len, BASE64, 64, str);
256}
257
258bool CreateRandomString(size_t len, const std::string& table,
259                        std::string* str) {
260  return CreateRandomString(len, table.c_str(),
261                            static_cast<int>(table.size()), str);
262}
263
264uint32 CreateRandomId() {
265  uint32 id;
266  if (!Rng().Generate(&id, sizeof(id))) {
267    LOG(LS_ERROR) << "Failed to generate random id!";
268  }
269  return id;
270}
271
272uint64 CreateRandomId64() {
273  return static_cast<uint64>(CreateRandomId()) << 32 | CreateRandomId();
274}
275
276uint32 CreateRandomNonZeroId() {
277  uint32 id;
278  do {
279    id = CreateRandomId();
280  } while (id == 0);
281  return id;
282}
283
284double CreateRandomDouble() {
285  return CreateRandomId() / (std::numeric_limits<uint32>::max() +
286      std::numeric_limits<double>::epsilon());
287}
288
289}  // namespace talk_base
290