1 /*
2 ** OSSP uuid - Universally Unique Identifier
3 ** Copyright (c) 2004-2008 Ralf S. Engelschall <rse@engelschall.com>
4 ** Copyright (c) 2004-2008 The OSSP Project <http://www.ossp.org/>
5 **
6 ** This file is part of OSSP uuid, a library for the generation
7 ** of UUIDs which can found at http://www.ossp.org/pkg/lib/uuid/
8 **
9 ** Permission to use, copy, modify, and distribute this software for
10 ** any purpose with or without fee is hereby granted, provided that
11 ** the above copyright notice and this permission notice appear in all
12 ** copies.
13 **
14 ** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
15 ** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
16 ** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 ** IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
18 ** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
20 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
21 ** USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 ** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 ** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
24 ** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 ** SUCH DAMAGE.
26 **
27 ** uuid_prng.c: PRNG API implementation
28 */
29
30 /* own headers (part 1/2) */
31 #include "uuid_ac.h"
32
33 /* system headers */
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include <time.h>
38 #include <sys/time.h>
39 #include <fcntl.h>
40 #if defined(WIN32)
41 #define WINVER 0x0500
42 #include <windows.h>
43 #include <wincrypt.h>
44 #endif
45
46 /* own headers (part 2/2) */
47 #include "uuid_time.h"
48 #include "uuid_prng.h"
49 #include "uuid_md5.h"
50
51 struct prng_st {
52 int dev; /* system PRNG device */
53 md5_t *md5; /* local MD5 PRNG engine */
54 long cnt; /* time resolution compensation counter */
55 };
56
prng_create(prng_t ** prng)57 prng_rc_t prng_create(prng_t **prng)
58 {
59 #if !defined(WIN32)
60 int fd = -1;
61 #endif
62 struct timeval tv;
63 pid_t pid;
64 unsigned int i;
65
66 /* sanity check argument(s) */
67 if (prng == NULL)
68 return PRNG_RC_ARG;
69
70 /* allocate object */
71 if ((*prng = (prng_t *)malloc(sizeof(prng_t))) == NULL)
72 return PRNG_RC_MEM;
73
74 /* try to open the system PRNG device */
75 (*prng)->dev = -1;
76 #if !defined(WIN32)
77 if ((fd = open("/dev/urandom", O_RDONLY)) == -1)
78 fd = open("/dev/random", O_RDONLY|O_NONBLOCK);
79 if (fd != -1) {
80 (void)fcntl(fd, F_SETFD, FD_CLOEXEC);
81 (*prng)->dev = fd;
82 }
83 #endif
84
85 /* initialize MD5 engine */
86 if (md5_create(&((*prng)->md5)) != MD5_RC_OK) {
87 free(*prng);
88 return PRNG_RC_INT;
89 }
90
91 /* initialize time resolution compensation counter */
92 (*prng)->cnt = 0;
93
94 /* seed the C library PRNG once */
95 (void)time_gettimeofday(&tv);
96 pid = getpid();
97 srand((unsigned int)(
98 ((unsigned int)pid << 16)
99 ^ (unsigned int)pid
100 ^ (unsigned int)tv.tv_sec
101 ^ (unsigned int)tv.tv_usec));
102 for (i = (unsigned int)((tv.tv_sec ^ tv.tv_usec) & 0x1F); i > 0; i--)
103 (void)rand();
104
105 return PRNG_RC_OK;
106 }
107
prng_data(prng_t * prng,void * data_ptr,size_t data_len)108 prng_rc_t prng_data(prng_t *prng, void *data_ptr, size_t data_len)
109 {
110 size_t n;
111 unsigned char *p;
112 struct {
113 struct timeval tv;
114 long cnt;
115 int rnd;
116 } entropy;
117 unsigned char md5_buf[MD5_LEN_BIN];
118 unsigned char *md5_ptr;
119 size_t md5_len;
120 int retries;
121 int i;
122 #if defined(WIN32)
123 HCRYPTPROV hProv;
124 #endif
125
126 /* sanity check argument(s) */
127 if (prng == NULL || data_len == 0)
128 return PRNG_RC_ARG;
129
130 /* prepare for generation */
131 p = (unsigned char *)data_ptr;
132 n = data_len;
133
134 /* approach 1: try to gather data via stronger system PRNG device */
135 if (prng->dev != -1) {
136 retries = 0;
137 while (n > 0) {
138 i = (int)read(prng->dev, (void *)p, n);
139 if (i <= 0) {
140 if (retries++ > 16)
141 break;
142 continue;
143 }
144 retries = 0;
145 n -= (unsigned int)i;
146 p += (unsigned int)i;
147 }
148 }
149 #if defined(WIN32)
150 else {
151 if (CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0))
152 CryptGenRandom(hProv, n, p);
153 }
154 #endif
155
156 /* approach 2: try to gather data via weaker libc PRNG API. */
157 while (n > 0) {
158 /* gather new entropy */
159 (void)time_gettimeofday(&(entropy.tv)); /* source: libc time */
160 entropy.rnd = rand(); /* source: libc PRNG */
161 entropy.cnt = prng->cnt++; /* source: local counter */
162
163 /* pass entropy into MD5 engine */
164 if (md5_update(prng->md5, (void *)&entropy, sizeof(entropy)) != MD5_RC_OK)
165 return PRNG_RC_INT;
166
167 /* store MD5 engine state as PRN output */
168 md5_ptr = md5_buf;
169 md5_len = sizeof(md5_buf);
170 if (md5_store(prng->md5, (void **)(void *)&md5_ptr, &md5_len) != MD5_RC_OK)
171 return PRNG_RC_INT;
172 for (i = 0; i < MD5_LEN_BIN && n > 0; i++, n--)
173 *p++ ^= md5_buf[i]; /* intentionally no assignment because arbitrary
174 caller buffer content is leveraged, too */
175 }
176
177 return PRNG_RC_OK;
178 }
179
prng_destroy(prng_t * prng)180 prng_rc_t prng_destroy(prng_t *prng)
181 {
182 /* sanity check argument(s) */
183 if (prng == NULL)
184 return PRNG_RC_ARG;
185
186 /* close PRNG device */
187 if (prng->dev != -1)
188 (void)close(prng->dev);
189
190 /* destroy MD5 engine */
191 (void)md5_destroy(prng->md5);
192
193 /* free object */
194 free(prng);
195
196 return PRNG_RC_OK;
197 }
198
199