1 /*
2  *
3  *  Copyright (C) 2018, OFFIS e.V.
4  *  All rights reserved.  See COPYRIGHT file for details.
5  *
6  *  This software and supporting documentation were developed by
7  *
8  *    OFFIS e.V.
9  *    R&D Division Health
10  *    Escherweg 2
11  *    D-26121 Oldenburg, Germany
12  *
13  *
14  *  Module:  ofstd
15  *
16  *  Author:  Marco Eichelberg, based on the reference implementation
17  *   of the ISAAC PRNG by Bob Jenkins (Public Domain).
18  *
19  *  Purpose: Cryptographically secure PRNG based on Bob Jenkins's ISAAC algorithm
20  *
21  */
22 
23 #include "dcmtk/config/osconfig.h"
24 #include "dcmtk/ofstd/ofrand.h"
25 #include "dcmtk/ofstd/ofcast.h"
26 #include "dcmtk/ofstd/ofstd.h"
27 
28 #define INCLUDE_CSTDIO
29 #define INCLUDE_CSTDDEF
30 #define INCLUDE_CTIME
31 #include "dcmtk/ofstd/ofstdinc.h"
32 
33 BEGIN_EXTERN_C
34 #ifdef HAVE_SYS_TIME_H
35 #include <sys/time.h>
36 #endif
37 END_EXTERN_C
38 
39 #ifdef HAVE_WINDOWS_H
40 #define WIN32_LEAN_AND_MEAN
41 #include <windows.h>
42 #endif
43 
44 #define ind(mm,x)  ((mm)[(x>>2)&(OFRandom_SIZ-1)])
45 
46 #define rngstep(mix,a,b,mm,m,m2,r,x) \
47 { \
48   x = *m;  \
49   a = ((a^(mix)) + *(m2++)); \
50   *(m++) = y = (ind(mm,x) + a + b); \
51   *(r++) = b = (ind(mm,y>>OFRandom_SIZL) + x) & 0xffffffff; \
52 }
53 
54 #define mix(a,b,c,d,e,f,g,h) \
55 { \
56    a^=b<<11;              d+=a; b+=c; \
57    b^=(c&0xffffffff)>>2;  e+=b; c+=d; \
58    c^=d<<8;               f+=c; d+=e; \
59    d^=(e&0xffffffff)>>16; g+=d; e+=f; \
60    e^=f<<10;              h+=e; f+=g; \
61    f^=(g&0xffffffff)>>4;  a+=f; g+=h; \
62    g^=h<<8;               b+=g; h+=a; \
63    h^=(a&0xffffffff)>>9;  c+=h; a+=b; \
64 }
65 
OFRandom()66 OFRandom::OFRandom()
67 : randcnt(OFRandom_SIZ)
68 , randrsl()
69 , randmem()
70 , randa(0)
71 , randb(0)
72 , randc(0)
73 {
74    /* initialize randrsl based on the current time, CPU clock count, and process ID */
75    Uint32 tm = OFstatic_cast(Uint32, time(NULL));
76    Uint32 cl = OFstatic_cast(Uint32, clock());
77    Uint32 pi = OFstatic_cast(Uint32, OFStandard::getProcessID());
78 
79 #ifdef _WIN32
80     ULARGE_INTEGER wintm;
81     /* tm is number of 100ns ticks since Jan 01 1601.
82      * We only use the lowest 32 bits of this number.
83      */
84     GetSystemTimeAsFileTime(OFreinterpret_cast(FILETIME *, &wintm));
85     Uint32 tm2 = OFstatic_cast(Uint32, wintm.LowPart);
86 #else
87     struct timeval posixtm;
88     gettimeofday(&posixtm, NULL);
89     /* sub-second part of current time in microseconds */
90     Uint32 tm2 = OFstatic_cast(Uint32, posixtm.tv_usec);
91 #endif
92 
93    for (int i=0; i<OFRandom_SIZ; i += 4)  // OFRandom_SIZ is dividable by 4
94    {
95      randrsl[i] = tm++;
96      randrsl[i+1] = cl++;
97      randrsl[i+2] = pi++;
98      randrsl[i+3] = tm2++;
99    }
100 
101    mixSeed();
102 }
103 
mixSeed()104 void OFRandom::mixSeed()
105 {
106    int i;
107    Uint32 a,b,c,d,e,f,g,h;
108    a=b=c=d=e=f=g=h=0x9e3779b9;  /* the golden ratio */
109 
110    for (i=0; i<4; ++i) /* scramble it */
111    {
112      mix(a,b,c,d,e,f,g,h);
113    }
114 
115    /* initialize using the contents of randrsl[] as the seed */
116    for (i=0; i<OFRandom_SIZ; i+=8)
117    {
118      a+=randrsl[i  ]; b+=randrsl[i+1];
119      c+=randrsl[i+2]; d+=randrsl[i+3];
120      e+=randrsl[i+4]; f+=randrsl[i+5];
121      g+=randrsl[i+6]; h+=randrsl[i+7];
122      mix(a,b,c,d,e,f,g,h);
123      randmem[i  ]=a; randmem[i+1]=b; randmem[i+2]=c; randmem[i+3]=d;
124      randmem[i+4]=e; randmem[i+5]=f; randmem[i+6]=g; randmem[i+7]=h;
125    }
126    /* do a second pass to make all of the seed affect all of m */
127    for (i=0; i<OFRandom_SIZ; i+=8)
128    {
129      a+=randmem[i  ]; b+=randmem[i+1];
130      c+=randmem[i+2]; d+=randmem[i+3];
131      e+=randmem[i+4]; f+=randmem[i+5];
132      g+=randmem[i+6]; h+=randmem[i+7];
133      mix(a,b,c,d,e,f,g,h);
134      randmem[i  ]=a; randmem[i+1]=b; randmem[i+2]=c; randmem[i+3]=d;
135      randmem[i+4]=e; randmem[i+5]=f; randmem[i+6]=g; randmem[i+7]=h;
136    }
137 
138    isaac(); /* fill in the first set of results */}
139 
seed(Uint32 sval)140 void OFRandom::seed(Uint32 sval)
141 {
142    for (int i=0; i<OFRandom_SIZ; i++)
143    {
144      randrsl[i] = sval;
145    }
146    randa = 0;
147    randb = 0;
148    randc = 0;
149    mixSeed();
150 }
151 
isaac()152 void OFRandom::isaac()
153 {
154    Uint32 x,y,*m,*m2,*mend;
155    Uint32 *r = randrsl;
156    Uint32 a = randa;
157    Uint32 b = randb + (++randc);
158    for (m = randmem, mend = m2 = m+(OFRandom_SIZ/2); m<mend; )
159    {
160       rngstep( a<<13, a, b, randmem, m, m2, r, x);
161       rngstep( (a & 0xffffffff) >>6 , a, b, randmem, m, m2, r, x);
162       rngstep( a<<2 , a, b, randmem, m, m2, r, x);
163       rngstep( (a & 0xffffffff) >>16, a, b, randmem, m, m2, r, x);
164    }
165    for (m2 = randmem; m2<mend; )
166    {
167       rngstep( a<<13, a, b, randmem, m, m2, r, x);
168       rngstep( (a & 0xffffffff) >>6 , a, b, randmem, m, m2, r, x);
169       rngstep( a<<2 , a, b, randmem, m, m2, r, x);
170       rngstep( (a & 0xffffffff) >>16, a, b, randmem, m, m2, r, x);
171    }
172    randb = b; randa = a;
173    randcnt = OFRandom_SIZ;
174 }
175 
getRND32()176 Uint32 OFRandom::getRND32()
177 {
178   // check if we still have random numbers left
179   if (randcnt == 0) isaac();
180 
181   // we use the random numbers from last to first
182   return randrsl[--randcnt];
183 }
184 
getRND16()185 Uint16 OFRandom::getRND16()
186 {
187   // use one 32-bit random number and ignore the extra bits
188   return OFstatic_cast(Uint16, getRND32());
189 }
190 
191 #ifndef OF_NO_UINT64
getRND64()192 Uint64 OFRandom::getRND64()
193 {
194   // get a 32-bit random number
195   Uint64 result = getRND32();
196   // shift into upper 32 bits of the 64-bit word
197   result <<= 32;
198   // get a second 32-bit random number
199   result |= getRND32();
200   return result;
201 }
202 #endif
203