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#ifdef WIN32
31#define WIN32_LEAN_AND_MEAN
32#include <windows.h>
33#include <ntsecapi.h>
34#else
35#ifdef SSL_USE_OPENSSL
36#include <openssl/rand.h>
37#endif
38#endif
39
40#include "talk/base/base64.h"
41#include "talk/base/logging.h"
42#include "talk/base/scoped_ptr.h"
43#include "talk/base/time.h"
44
45namespace talk_base {
46
47// Base class for RNG implementations.
48class RandomGenerator {
49 public:
50  virtual ~RandomGenerator() {}
51  virtual bool Init(const void* seed, size_t len) = 0;
52  virtual bool Generate(void* buf, size_t len) = 0;
53};
54
55// The real random generators, using either CryptoAPI or OpenSSL.
56// We also support the 'old' generator on Mac/Linux until we have time to
57// fully test the OpenSSL one.
58#ifdef WIN32
59class SecureRandomGenerator : public RandomGenerator {
60 public:
61  SecureRandomGenerator() : advapi32_(NULL), rtl_gen_random_(NULL) {}
62  ~SecureRandomGenerator() {
63    FreeLibrary(advapi32_);
64  }
65
66  virtual bool Init(const void* seed, size_t seed_len) {
67    // We don't do any additional seeding on Win32, we just use the CryptoAPI
68    // RNG (which is exposed as a hidden function off of ADVAPI32 so that we
69    // don't need to drag in all of CryptoAPI)
70    if (rtl_gen_random_) {
71      return true;
72    }
73
74    advapi32_ = LoadLibrary(L"advapi32.dll");
75    if (!advapi32_) {
76      return false;
77    }
78
79    rtl_gen_random_ = reinterpret_cast<RtlGenRandomProc>(
80        GetProcAddress(advapi32_, "SystemFunction036"));
81    if (!rtl_gen_random_) {
82      FreeLibrary(advapi32_);
83      return false;
84    }
85
86    return true;
87  }
88  virtual bool Generate(void* buf, size_t len) {
89    if (!rtl_gen_random_ && !Init(NULL, 0)) {
90      return false;
91    }
92    return (rtl_gen_random_(buf, len) != FALSE);
93  }
94
95 private:
96  typedef BOOL (WINAPI *RtlGenRandomProc)(PVOID, ULONG);
97  HINSTANCE advapi32_;
98  RtlGenRandomProc rtl_gen_random_;
99};
100#else
101#ifndef SSL_USE_OPENSSL
102// The old RNG.
103class SecureRandomGenerator : public RandomGenerator {
104 public:
105  SecureRandomGenerator() : seed_(1) {
106  }
107  ~SecureRandomGenerator() {
108  }
109  virtual bool Init(const void* seed, size_t len) {
110    uint32 hash = 0;
111    for (size_t i = 0; i < len; ++i) {
112      hash = ((hash << 2) + hash) + static_cast<const char*>(seed)[i];
113    }
114
115    seed_ = Time() ^ hash;
116    return true;
117  }
118  virtual bool Generate(void* buf, size_t len) {
119    for (size_t i = 0; i < len; ++i) {
120      static_cast<uint8*>(buf)[i] = static_cast<uint8>(GetRandom());
121    }
122    return true;
123  }
124
125 private:
126  int GetRandom() {
127    return ((seed_ = seed_ * 214013L + 2531011L) >> 16) & 0x7fff;
128  }
129  int seed_;
130};
131#else
132// The OpenSSL RNG. Need to make sure it doesn't run out of entropy.
133class SecureRandomGenerator : public RandomGenerator {
134 public:
135  SecureRandomGenerator() : inited_(false) {
136  }
137  ~SecureRandomGenerator() {
138  }
139  virtual bool Init(const void* seed, size_t len) {
140    // By default, seed from the system state.
141    if (!inited_) {
142      if (RAND_poll() <= 0) {
143        return false;
144      }
145      inited_ = true;
146    }
147    // Allow app data to be mixed in, if provided.
148    if (seed) {
149      RAND_seed(seed, len);
150    }
151    return true;
152  }
153  virtual bool Generate(void* buf, size_t len) {
154    if (!inited_ && !Init(NULL, 0)) {
155      return false;
156    }
157    return (RAND_bytes(reinterpret_cast<unsigned char*>(buf), len) > 0);
158  }
159
160 private:
161  bool inited_;
162};
163#endif  // SSL_USE_OPENSSL
164#endif  // WIN32
165
166// A test random generator, for predictable output.
167class TestRandomGenerator : public RandomGenerator {
168 public:
169  TestRandomGenerator() : seed_(7) {
170  }
171  ~TestRandomGenerator() {
172  }
173  virtual bool Init(const void* seed, size_t len) {
174    return true;
175  }
176  virtual bool Generate(void* buf, size_t len) {
177    for (size_t i = 0; i < len; ++i) {
178      static_cast<uint8*>(buf)[i] = static_cast<uint8>(GetRandom());
179    }
180    return true;
181  }
182
183 private:
184  int GetRandom() {
185    return ((seed_ = seed_ * 214013L + 2531011L) >> 16) & 0x7fff;
186  }
187  int seed_;
188};
189
190// TODO: Use Base64::Base64Table instead.
191static const char BASE64[64] = {
192  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
193  'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
194  'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
195  'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
196  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
197};
198
199static scoped_ptr<RandomGenerator> g_rng(new SecureRandomGenerator());
200
201void SetRandomTestMode(bool test) {
202  if (!test) {
203    g_rng.reset(new SecureRandomGenerator());
204  } else {
205    g_rng.reset(new TestRandomGenerator());
206  }
207}
208
209bool InitRandom(int seed) {
210  return InitRandom(reinterpret_cast<const char*>(&seed), sizeof(seed));
211}
212
213bool InitRandom(const char* seed, size_t len) {
214  if (!g_rng->Init(seed, len)) {
215    LOG(LS_ERROR) << "Failed to init random generator!";
216    return false;
217  }
218  return true;
219}
220
221std::string CreateRandomString(size_t len) {
222  std::string str;
223  CreateRandomString(len, &str);
224  return str;
225}
226
227bool CreateRandomString(size_t len, std::string* str) {
228  str->clear();
229  scoped_array<uint8> bytes(new uint8[len]);
230  if (!g_rng->Generate(bytes.get(), len)) {
231    LOG(LS_ERROR) << "Failed to generate random string!";
232    return false;
233  }
234  str->reserve(len);
235  for (size_t i = 0; i < len; ++i) {
236    str->push_back(BASE64[bytes[i] & 63]);
237  }
238  return true;
239}
240
241uint32 CreateRandomId() {
242  uint32 id;
243  if (!g_rng->Generate(&id, sizeof(id))) {
244    LOG(LS_ERROR) << "Failed to generate random id!";
245  }
246  return id;
247}
248
249uint32 CreateRandomNonZeroId() {
250  uint32 id;
251  do {
252    id = CreateRandomId();
253  } while (id == 0);
254  return id;
255}
256
257}  // namespace talk_base
258