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