reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
Random.cxx
Go to the documentation of this file.
1#if defined(HAVE_CONFIG_H)
2#include "config.h"
3#endif
4
5#include "rutil/ResipAssert.h"
6#include <stdlib.h>
7
8#ifdef WIN32
9#include "rutil/Socket.hxx"
10#include "rutil/DataStream.hxx"
11#include "rutil/Data.hxx"
12#else
13#include <unistd.h>
14#include <sys/types.h>
15#include <sys/stat.h>
16#include <fcntl.h>
17#endif
18
19#include "rutil/Random.hxx"
20#include "rutil/Timer.hxx"
21#include "rutil/Mutex.hxx"
22#include "rutil/Lock.hxx"
23#include "rutil/Logger.hxx"
24
25
26#ifdef USE_SSL
27#ifdef WIN32
28//hack for name collision of OCSP_RESPONSE and wincrypt.h in latest openssl release 0.9.8h
29//http://www.google.com/search?q=OCSP%5fRESPONSE+wincrypt%2eh
30//continue to watch this issue for a real fix.
31#undef OCSP_RESPONSE
32#endif
34# define USE_OPENSSL 1
35#else
36# define USE_OPENSSL 0
37#endif
38
39#if ( USE_OPENSSL == 1 )
40# include <openssl/opensslv.h>
41#if !defined(LIBRESSL_VERSION_NUMBER)
42# include <openssl/e_os2.h>
43#endif
44# include <openssl/rand.h>
45# include <openssl/err.h>
46#endif
47
48using namespace resip;
49#define RESIPROCATE_SUBSYSTEM Subsystem::SIP
50
51Mutex Random::mMutex;
52bool Random::mIsInitialized = false;
53
54#ifdef WIN32
55Random::Initializer Random::mInitializer;
56#ifdef RESIP_RANDOM_WIN32_RTL
57BOOLEAN (APIENTRY *Random::RtlGenRandom)(void*, ULONG) = 0;
58#endif
59#endif //WIN32
60
61#ifdef RESIP_RANDOM_THREAD_MUTEX
62struct random_data* Random::sRandomState = 0;
63#endif
64
65#ifdef RESIP_RANDOM_THREAD_LOCAL
66ThreadIf::TlsKey Random::sRandomStateKey = 0;
67namespace
68{
69 // Process-wide entropy read from /dev/urandom in initialize(), XORed into
70 // each thread's PRNG seed so per-thread state is not derived solely from
71 // time+pid (CWE-338).
72 unsigned int sThreadSeedBase = 0;
73}
74#endif
75
76#define RANDOM_STATE_SIZE 128
77
78const char*
80{
81#ifdef WIN32
82#if defined(RESIP_RANDOM_WIN32_RTL)
83 return "win32_rtl";
84#else
85 return "win32_rand";
86#endif
87#else // WIN32
88#if defined(RESIP_RANDOM_THREAD_LOCAL)
89 return "posix_thread_local";
90#elif defined(RESIP_RANDOM_THREAD_MUTEX)
91 return "posix_thread_mutex";
92#else
93 return "posix_random";
94#endif
95#endif // not WIN32
96}
97
101unsigned
103{
104 // !cj! need to find a better way - use pentium random commands?
105 Data buffer;
106 {
107 DataStream strm(buffer);
108#ifdef WIN32
109 strm << GetTickCount() << ":";
110 strm << GetCurrentProcessId() << ":";
111 strm << GetCurrentThreadId();
112#else
113 // .kw. previously just used the lower 32bits of getTimeMs()
114 strm << ResipClock::getTimeMicroSec() << ":";
115 strm << getpid();
116#if defined(RESIP_RANDOM_THREAD_LOCAL)
117 strm << ":" << ThreadIf::selfId();
118#endif
119#endif
120 }
121 return (unsigned int)buffer.hash();
122}
123
124void
126{
127#ifdef WIN32
128//#if defined(USE_SSL)
129#if 0
130 //to re-work openssl initialization
131 if ( !Random::mIsInitialized)
132 {
133 Lock lock(mMutex);
134 if (!Random::mIsInitialized)
135 {
136 mIsInitialized = true;
137 RAND_screen ();
138 }
139 }
140#else
141 if (!Random::mInitializer.isInitialized())
142 {
143 Lock lock(mMutex);
144 if (!Random::mInitializer.isInitialized())
145 {
146 Random::mInitializer.setInitialized();
147
148 unsigned seed = getSimpleSeed();
149 srand(seed);
150
151#ifdef RESIP_RANDOM_WIN32_RTL
152 // .jjg. from http://blogs.msdn.com/michael_howard/archive/2005/01/14/353379.aspx
153 // srand(..) and rand() have proven to be insufficient sources of randomness,
154 // leading to transaction id collisions in resip.
155 // SystemFunction036 maps to RtlGenRandom, which is used by rand_s() (which is available
156 // only with the VC 8.0 runtime or later) and is the Microsoft-recommended way of getting
157 // a random number. This code allows that functionality to be accessed even from VC 7.1.
158 // However, SystemFunction036 only exists in Windows XP and later, so we may need to fallback
159 // to the old method using rand().
160 HMODULE hLib = GetModuleHandle("ADVAPI32.DLL");
161 if (hLib)
162 {
163 Random::RtlGenRandom =
164 (BOOLEAN(APIENTRY*)(void*, ULONG))GetProcAddress(hLib, "SystemFunction036");
165 if (!Random::RtlGenRandom)
166 {
167 WarningLog(<< "Not linked with ADVAPI32.DLL, using srand(..) and rand() for random numbers");
168 }
169 }
170#endif // RESIP_RANDOM_WIN32_RTL
171
172 mIsInitialized = true;
173 }
174 }
175#endif // not dead code
176
177#else // WIN32
178 // ?dcm? -- OpenSSL will transparently initialize PRNG if /dev/urandom is
179 // present. In any case, will move into OpenSSLInit
180 if ( !Random::mIsInitialized)
181 {
182 Lock lock(mMutex);
183 if (!Random::mIsInitialized)
184 {
185 mIsInitialized = true;
187
188 unsigned seed = getSimpleSeed();
189
190 int fd = open("/dev/urandom", O_RDONLY);
191 // !ah! blocks on embedded devices -- not enough entropy.
192 if ( fd != -1 )
193 {
194 // Mix kernel entropy into the PRNG seed before seeding so that
195 // random()/rand() are not seeded solely from time+pid, which an
196 // attacker can approximate (CWE-338). XOR preserves the per-thread
197 // and per-process distinction that getSimpleSeed() provides.
198 unsigned urandomSeed = 0;
199 int s = read( fd,&urandomSeed,sizeof(urandomSeed) );
200
201 if ( s == sizeof(urandomSeed) )
202 {
203 seed ^= urandomSeed;
204 }
205 else
206 {
207 ErrLog( << "System is short of randomness" ); // !ah! never prints
208 }
209 }
210 else
211 {
212 ErrLog( << "Could not open /dev/urandom" );
213 }
214
215#if defined(RESIP_RANDOM_THREAD_LOCAL)
216 sThreadSeedBase = seed;
217 ThreadIf::tlsKeyCreate(sRandomStateKey, ::free);
218#elif defined(RESIP_RANDOM_THREAD_MUTEX)
219 struct random_data *buf;
220 size_t sz = sizeof(*buf)+RANDOM_STATE_SIZE;
221 buf = (struct random_data*) ::malloc(sz);
222 memset( buf, 0, sz); // .kw. strange segfaults without this
223 initstate_r(seed, ((char*)buf)+sizeof(*buf), RANDOM_STATE_SIZE, buf);
224 sRandomState = buf;
225#else
226 srandom(seed);
227#endif
228
229#if defined(USE_SSL)
230 if (fd == -1 )
231 {
232 // really bad sign - /dev/random does not exist so need to intialize
233 // OpenSSL some other way
234
235 // !cj! need to fix assert(0);
236 }
237 else
238 {
239 char buf[1024/8]; // size is number byes used for OpenSSL init
240
241 int s = read( fd,&buf,sizeof(buf) );
242
243 if ( s != sizeof(buf) )
244 {
245 ErrLog( << "System is short of randomness" );
246 }
247
248 RAND_add(buf,sizeof(buf),double(s*8));
249 }
250#endif // SSL
251 if (fd != -1 )
252 {
253 ::close(fd);
254 }
255 }
256 }
257#endif // not WIN32
258}
259
260int
262{
263 initialize();
264
265#ifdef WIN32
266
267 int ret = 0;
268
269#ifdef RESIP_RANDOM_WIN32_RTL
270 // see comment in initialize()
271 if (Random::RtlGenRandom)
272 {
273 unsigned long buff[1];
274 ULONG ulCbBuff = sizeof(buff);
275 if (Random::RtlGenRandom(buff,ulCbBuff))
276 {
277 // .kw. all other impls here return positive number, so do the same...
278 ret = buff[0] & (~(1<<31));
279 return ret;
280 }
281 }
282 // fallback to using rand() if this is a Windows version previous to XP
283#endif // RESIP_RANDOM_WIN32_RTL
284 {
285 // rand() returns [0,RAND_MAX], which on Windows is 15 bits and positive
286 // code below gets 30bits of randomness; with bit31 and bit15
287 // always zero; result is always positive
288 resip_assert( RAND_MAX == 0x7fff );
289 // WATCHOUT: on Linux, rand() returns 31bits, and assert above will fail
290 int r1 = rand();
291 int r2 = rand();
292 ret = (r1<<16) + r2;
293 }
294
295 return ret;
296#else // WIN32
297
298#if defined(RESIP_RANDOM_THREAD_LOCAL)
299 struct random_data *buf = (struct random_data*) ThreadIf::tlsGetValue(sRandomStateKey);
300 if ( buf==NULL ) {
301 size_t sz = sizeof(*buf)+RANDOM_STATE_SIZE;
302 buf = (struct random_data*) ::malloc(sz);
303 memset( buf, 0, sz); // .kw. strange segfaults without this
304 // Mix in the /dev/urandom-derived base captured at initialize() time so
305 // per-thread seeds are not derived solely from time+pid (CWE-338).
306 unsigned seed = getSimpleSeed() ^ sThreadSeedBase;
307 initstate_r(seed, ((char*)buf)+sizeof(*buf), RANDOM_STATE_SIZE, buf);
308 ThreadIf::tlsSetValue(sRandomStateKey, buf);
309 }
310 int32_t ret;
311 random_r(buf, &ret);
312 return ret;
313#elif defined(RESIP_RANDOM_THREAD_MUTEX)
314 int32_t ret;
315 {
316 Lock statelock(mMutex);
317 random_r(sRandomState, &ret);
318 }
319 return ret;
320#else
321 // random returns [0,RAN_MAX]. On Linux, this is 31 bits and positive.
322 // On some platforms it might be on 15 bits, and will need to do something.
323 // assert( RAND_MAX == ((1<<31)-1) ); // ?slg? commented out assert since, RAND_MAX is not used in random(), it applies to rand() only
324 return random();
325#endif // THREAD_LOCAL
326#endif // WIN32
327}
328
329int
331{
332 initialize();
333
334#if USE_OPENSSL
335 int ret;
336 int e = RAND_bytes( (unsigned char*)&ret , sizeof(ret) );
337 if ( e < 0 )
338 {
339 // error of some type - likely not enough rendomness to dod this
340 long err = ERR_get_error();
341
342 char buf[1024];
343 ERR_error_string_n(err,buf,sizeof(buf));
344
345 ErrLog( << buf );
346 resip_assert(0);
347 }
348 return ret;
349#else
350 return getRandom();
351#endif
352}
353
354Data
355Random::getRandom(unsigned int len)
356{
357 initialize();
359
360 union
361 {
362 char cbuf[Random::maxLength+1];
363 unsigned int ibuf[(Random::maxLength+1)/sizeof(int)];
364 };
365
366 for (unsigned int count=0; count<(len+sizeof(int)-1)/sizeof(int); ++count)
367 {
368 ibuf[count] = Random::getRandom();
369 }
370 return Data(cbuf, len);
371}
372
373Data
374Random::getCryptoRandom(unsigned int len)
375{
376 unsigned char* buf = new unsigned char[len];
377 getCryptoRandom(buf, len); // USE_SSL check is in here
378 return Data(Data::Take, (char*)buf, len);
379}
380
381Data
382Random::getRandomHex(unsigned int numBytes)
383{
384 return Random::getRandom(numBytes).hex();
385}
386
387Data
388Random::getRandomBase64(unsigned int numBytes)
389{
390 return Random::getRandom(numBytes).base64encode();
391}
392
393Data
394Random::getCryptoRandomHex(unsigned int numBytes)
395{
396 return Random::getCryptoRandom(numBytes).hex();
397}
398
399Data
400Random::getCryptoRandomBase64(unsigned int numBytes)
401{
402 return Random::getCryptoRandom(numBytes).base64encode();
403}
404
405/*
406 [From RFC 4122]
407
408 The version 4 UUID is meant for generating UUIDs from truly-random or
409 pseudo-random numbers.
410
411 The algorithm is as follows:
412
413 o Set the two most significant bits (bits 6 and 7) of the
414 clock_seq_hi_and_reserved to zero and one, respectively.
415
416 o Set the four most significant bits (bits 12 through 15) of the
417 time_hi_and_version field to the 4-bit version number from
418 Section 4.1.3. (0 1 0 0)
419
420 o Set all the other bits to randomly (or pseudo-randomly) chosen
421 values.
422
423 UUID = time-low "-" time-mid "-"
424 time-high-and-version "-"
425 clock-seq-and-reserved
426 clock-seq-low "-" node
427 time-low = 4hexOctet
428 time-mid = 2hexOctet
429 time-high-and-version = 2hexOctet
430 clock-seq-and-reserved = hexOctet
431 clock-seq-low = hexOctet
432 node = 6hexOctet
433 hexOctet = hexDigit hexDigit
434*/
435Data
437{
438 Data urn ("urn:uuid:");
439 urn += getCryptoRandomHex(4); // time-low
440 urn += "-";
441 urn += getCryptoRandomHex(2); // time-mid
442 urn += "-";
443
444 Data time_hi_and_version = Random::getCryptoRandom(2);
445 time_hi_and_version[0] &= 0x0f;
446 time_hi_and_version[0] |= 0x40;
447 urn += time_hi_and_version.hex();
448
449 urn += "-";
450
451 Data clock_seq_hi_and_reserved = Random::getCryptoRandom(1);
452 clock_seq_hi_and_reserved[0] &= 0x3f;
453 clock_seq_hi_and_reserved[0] |= 0x40;
454 urn += clock_seq_hi_and_reserved.hex();
455
456 urn += getCryptoRandomHex(1); // clock-seq-low
457 urn += "-";
458 urn += getCryptoRandomHex(6); // node
459 return urn;
460}
461
462void
463Random::getCryptoRandom(unsigned char* buf, unsigned int numBytes)
464{
465 resip_assert(numBytes < Random::maxLength+1);
466
467#if USE_OPENSSL
468 initialize();
469 int e = RAND_bytes( (unsigned char*)buf , numBytes );
470 if ( e < 0 )
471 {
472 // error of some type - likely not enough rendomness to dod this
473 long err = ERR_get_error();
474
475 char buf[1024];
476 ERR_error_string_n(err,buf,sizeof(buf));
477
478 ErrLog( << buf );
479 resip_assert(0);
480 }
481#else
482 // !bwc! Should optimize this.
483 Data temp=Random::getRandom(numBytes);
484 memcpy(buf, temp.data(), numBytes);
485#endif
486}
487
488#ifdef WIN32
489Random::Initializer::Initializer() : mThreadStorage(::TlsAlloc())
490{
491 resip_assert(mThreadStorage != TLS_OUT_OF_INDEXES);
492}
493
494Random::Initializer::~Initializer()
495{
496 ::TlsFree(mThreadStorage);
497}
498
499void
500Random::Initializer::setInitialized()
501{
502 ::TlsSetValue(mThreadStorage, (LPVOID) TRUE);
503}
504
505bool
506Random::Initializer::isInitialized()
507{
508#pragma warning ( disable:4311)
509#pragma warning ( disable:4302)
510 // Note: if value is not set yet then 0 (false) is returned
511 return (BOOL) ::TlsGetValue(mThreadStorage) == TRUE;
512}
513#endif
514
515
516/* ====================================================================
517 * The Vovida Software License, Version 1.0
518 *
519 * Copyright (c) 2005. All rights reserved.
520 *
521 * Redistribution and use in source and binary forms, with or without
522 * modification, are permitted provided that the following conditions
523 * are met:
524 *
525 * 1. Redistributions of source code must retain the above copyright
526 * notice, this list of conditions and the following disclaimer.
527 *
528 * 2. Redistributions in binary form must reproduce the above copyright
529 * notice, this list of conditions and the following disclaimer in
530 * the documentation and/or other materials provided with the
531 * distribution.
532 *
533 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
534 * and "Vovida Open Communication Application Library (VOCAL)" must
535 * not be used to endorse or promote products derived from this
536 * software without prior written permission. For written
537 * permission, please contact vocal@vovida.org.
538 *
539 * 4. Products derived from this software may not be called "VOCAL", nor
540 * may "VOCAL" appear in their name, without prior written
541 * permission of Vovida Networks, Inc.
542 *
543 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
544 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
545 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
546 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
547 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
548 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
549 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
550 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
551 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
552 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
553 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
554 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
555 * DAMAGE.
556 *
557 * ====================================================================
558 *
559 * This software consists of voluntary contributions made by Vovida
560 * Networks, Inc. and many individuals on behalf of Vovida Networks,
561 * Inc. For more information on Vovida Networks, Inc., please see
562 * <http://www.vovida.org/>.
563 *
564 * vi: set shiftwidth=3 expandtab:
565 */
#define WarningLog(args_)
Definition Logger.hxx:134
#define ErrLog(args_)
Definition Logger.hxx:137
#define RANDOM_STATE_SIZE
Definition Random.cxx:76
#define resip_assert(x)
This file needs to work for both C and C++ code.
Handles cross-platform sockets compatibility.
An iostream that operates on an existing Data.
An alternative to std::string, encapsulates an arbitrary buffer of bytes.
Definition Data.hxx:90
@ Take
The Data instance takes complete ownership of the buffer.
Definition Data.hxx:272
Data base64encode(bool useUrlSafe=false) const
Performs RFC 3548 Base 64 encoding of the contents of this data.
Definition Data.cxx:2682
Data hex() const
Returns a hexadecimal representation of the contents of this Data.
Definition Data.cxx:1577
size_t hash() const
Creates a 32-bit hash based on the contents of this Data.
Definition Data.cxx:2355
const char * data() const noexcept
Returns a pointer to the contents of this Data.
Definition Data.hxx:608
static Data getCryptoRandomBase64(unsigned int numBytes)
Definition Random.cxx:400
static int getRandom()
Returns a postive integer (31 bits) of randomness.
Definition Random.cxx:261
static Data getVersion4UuidUrn()
Returns a version 4 (random) UUID as defined in RFC 4122.
Definition Random.cxx:436
static int getCryptoRandom()
Definition Random.cxx:330
static const char * getImplName()
Definition Random.cxx:79
static Data getCryptoRandomHex(unsigned int numBytes)
Definition Random.cxx:394
static void initialize()
Definition Random.cxx:125
static Data getRandomHex(unsigned int numBytes)
Definition Random.cxx:382
static Data getRandomBase64(unsigned int numBytes)
Definition Random.cxx:388
static unsigned getSimpleSeed()
Key goal is to make sure that each thread has distinct seed.
Definition Random.cxx:102
static uint64_t getTimeMicroSec()
Returns the current clock time in microseconds.
Definition Time.hxx:61
static void * tlsGetValue(TlsKey key)
This function follows pthread_getspecific() signature.
Definition ThreadIf.cxx:179
static int tlsKeyCreate(TlsKey &key, TlsDestructor *destructor)
This function follows pthread_key_create() signature.
Definition ThreadIf.cxx:130
pthread_key_t TlsKey
Definition ThreadIf.hxx:85
static Id selfId()
Definition ThreadIf.cxx:124
static int tlsSetValue(TlsKey key, const void *val)
This function follows pthread_setspecific() signature.
Definition ThreadIf.cxx:169
static void setupTimeOffsets()
Definition Timer.hxx:61
dcm! – add UnusedChecking(_enum) below;
std::mutex Mutex
A semaphore that can be locked by only one thread at a time.
Definition Mutex.hxx:37
std::unique_lock< Mutex > Lock
A convenience class to lock a Lockable (such as a Mutex) on object creation, and unlock on destructio...
Definition Lock.hxx:28
signed int int32_t
Definition stdint.h:82