randseed.C
上传用户:zbbssh
上传日期:2007-01-08
资源大小:196k
文件大小:11k
源码类别:

CA认证

开发平台:

C/C++

  1. /*
  2. ------------------------------------------------------------------
  3.   Copyright
  4.   Sun Microsystems, Inc.
  5.   Copyright (C) 1994, 1995, 1996 Sun Microsystems, Inc.  All Rights
  6.   Reserved.
  7.   Permission is hereby granted, free of charge, to any person
  8.   obtaining a copy of this software and associated documentation
  9.   files (the "Software"), to deal in the Software without
  10.   restriction, including without limitation the rights to use,
  11.   copy, modify, merge, publish, distribute, sublicense, and/or sell
  12.   copies of the Software or derivatives of the Software, and to 
  13.   permit persons to whom the Software or its derivatives is furnished 
  14.   to do so, subject to the following conditions:
  15.   The above copyright notice and this permission notice shall be
  16.   included in all copies or substantial portions of the Software.
  17.   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  18.   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  19.   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  20.   NONINFRINGEMENT.  IN NO EVENT SHALL SUN MICROSYSTEMS, INC., BE LIABLE
  21.   FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  22.   OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  23.   CONNECTION WITH THE SOFTWARE OR DERIVATES OF THIS SOFTWARE OR 
  24.   THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  25.   Except as contained in this notice, the name of Sun Microsystems, Inc.
  26.   shall not be used in advertising or otherwise to promote
  27.   the sale, use or other dealings in this Software or its derivatives 
  28.   without prior written authorization from Sun Microsystems, Inc.
  29. */
  30. #pragma ident "@(#)randseed.C 1.11 96/01/29 Sun Microsystems"
  31. #include <sys/types.h> /* For pid_t, getpid(), getppid() */
  32. #include <stdio.h> /* For FILE, popen(), etc. */
  33. #include <stdlib.h>
  34. #include <string.h> /* For memset(), memcpy() */
  35. #include <unistd.h>
  36. #include <time.h>
  37. #include <sys/time.h> /* For gettimeofday() */
  38. #include "Bstream.h"
  39. #include "sha.h"
  40. /*
  41.  * Random number generator theory of operation.
  42.  *
  43.  * This derives its seed values from running some "ps"-like commands,
  44.  * using SHA to mix everything together.  The results are accumulated
  45.  * into a static buffer which, hopefully, becomes less and less
  46.  * predictible as time goes by.  The buffer is used to generate the
  47.  * random output.  The actual commands are (tried with an explicit
  48.  * path that should be good first, then implicit $PATH):
  49.  * last (Initialization only)
  50.  * pstat -fp
  51.  * netstat -n
  52.  * vmstat -s
  53.  * netstat -in
  54.  * pstat -isS
  55.  * netstat -sn
  56.  * iostat -I
  57.  * netstat -rn
  58.  *
  59.  * It hashes the output into an SHA hash context, and then calls
  60.  * "randpool_update" with the SHA hash context.  randpool_update then
  61.  * does the following:
  62.  * - hashes the pool into the SHA hash context to make the state depend
  63.  *   on the pool and thus, all previous bytes.
  64.  * - copies the HSA hash context, finalizes the copy, and XORs the
  65.  *   resultant 20-byte hash into the hash context at a location
  66.  *   that is moved forward through the pool each time.  (Thus
  67.  *   eventually updating the whole pool.)
  68.  * - hashes the 20-byte hash into the original SHA hash context, ensuring
  69.  *   (through the one-way property) that the output of this hash doesn't
  70.  *   reveal anything about the the value XORed into the hash context and
  71.  *   thus, indirectly, the state of the random number pool.
  72.  *
  73.  * The SHA context can then be finalized and the output used as a
  74.  * random number.
  75.  *
  76.  * Actually, initially the run command--hash output--randpool_update
  77.  * cycle is performed for all of the above-mentioned commands, plus
  78.  * gettimeofday(), getpid() and getppid(), before the final hash is used
  79.  * for output.
  80.  *
  81.  * After that, bytes are taken from the output buffer as needed.  If
  82.  * it becomes empty, it is refilled.
  83.  * 
  84.  * If gettimeofday() shows that "a while" has passed since the last time
  85.  * one of the commands was run, the next one is run in rotating order and
  86.  * passed to randpool_update.  (Yes, the code works in the face of time
  87.  * warping backwards.)
  88.  * Then the gettimeofday() output is itself hashed and fed to randpool_update,
  89.  * and the result of that used to refill the buffer.
  90.  *
  91.  * "A while" is defined as THROTTLE seconds, tunable to keep system load
  92.  * down.  If multiples of THROTTLE seconds have passed, multiple commands
  93.  * are run, within limits.
  94.  *
  95.  * Output can be produced by randpool_getbytes (which fills a supplied buffer)
  96.  * or get_random_bytes (which returns a Bstream).
  97.  *
  98.  * You can also invoke this with external data if desired, by calling
  99.  * randpool_addbytes and passing in a buffer.  This is used by
  100.  * prompt_user_for_randomness, which timestamps each keystroke and
  101.  * hashes in the character and the time.
  102.  */
  103. /*
  104.  * Don't run more than one entropy-gathering command per this many seconds.
  105.  * (This only matters to the keymgrd, since it's the only thing that
  106.  * runs for any appreciable amount of time.)
  107.  */
  108. #define THROTTLE 300
  109. /* Number of SHA_DIGESTSIZE chunks there are in the randpool. */
  110. #define RANDPOOL_MULTIPLE 20
  111. /*
  112.  * This takes an SHAContext and updates the random pool with it, returning
  113.  * it in a state which is ready to have SHAFinal() called on it.
  114.  * It hashes the pool into the context, clones the context, and
  115.  * finalizes the clone.  That final hash is then also hashed into the
  116.  * original context, and XORed into the pool at position "randpoolindex",
  117.  * which is subsequently incremented.
  118.  *
  119.  * This modifies the random number pool in a way that depends on all of
  120.  * the past history data, and returns a number also depending on every
  121.  * call to randpool_update so far, but it is computationally infeasible
  122.  * to derive the randpool's internal state from the output data.
  123.  */
  124. static void
  125. randpool_update(struct SHAContext *sha)
  126. {
  127. static unsigned char randpool[SHA_DIGESTSIZE*RANDPOOL_MULTIPLE];
  128. static unsigned char randpoolindex = 0;
  129. unsigned char buffer[SHA_DIGESTSIZE];
  130. struct SHAContext sha2;
  131. unsigned i;
  132. shaUpdate(sha, randpool, sizeof(randpool));
  133. sha2 = *sha;
  134. shaFinal(&sha2, buffer);
  135. shaUpdate(sha, buffer, sizeof(buffer));
  136. for (i = 0; i < sizeof(buffer); i++)
  137. randpool[randpoolindex+i] = buffer[i];
  138. randpoolindex += i;
  139. if (randpoolindex >= sizeof(randpool))
  140. randpoolindex = 0;
  141. memset(buffer, 0, sizeof(buffer));
  142. }
  143. // Get entropy from system statistics.  Pick the next available system
  144. // statistics info from a list of commands and run it.
  145. // (Try the usual location first, then search the $PATH.)
  146. // Hash the output into the given SHAContext and call randpool_update.
  147. //
  148. // Note that these commands are for SunOS 4.1.3; other systems will be
  149. // slightly different and need them changed.
  150. static void
  151. randpool_getentropy(struct SHAContext *sha)
  152. {
  153. FILE *f;
  154. static char const * const commands[] = {
  155. #ifdef SYSV // Actually SOLARIS
  156. "(/usr/bin/last || last) 2>&1", /* Initialization only */
  157. "(/usr/ucb/netstat -k || netstat -k) 2>&1",
  158. "(/usr/ucb/netstat -n || netstat -n) 2>&1",
  159. "(/usr/ucb/vmstat -s || vmstat -s) 2>&1",
  160. "(/usr/ucb/netstat -in || netstat -in) 2>&1",
  161. "(/usr/etc/pstat -isS || pstat -isS) 2>&1",
  162. "(/usr/ucb/netstat -sn || netstat -sn) 2>&1",
  163. "(/usr/bin/iostat -I || iostat -I) 2>&1",
  164. "(/usr/ucb/netstat -rn || netstat -rn) 2>&1"
  165. #else
  166. "(/usr/ucb/last || last) 2>&1", /* Initialization only */
  167. "(/usr/etc/pstat -fp || pstat -fp) 2>&1",
  168. "(/usr/ucb/netstat -n || netstat -n) 2>&1",
  169. "(/usr/ucb/vmstat -s || vmstat -s) 2>&1",
  170. "(/usr/ucb/netstat -in || netstat -in) 2>&1",
  171. "(/usr/etc/pstat -isS || pstat -isS) 2>&1",
  172. "(/usr/ucb/netstat -sn || netstat -sn) 2>&1",
  173. "(/usr/bin/iostat -I || iostat -I) 2>&1",
  174. "(/usr/ucb/netstat -rn || netstat -rn) 2>&1"
  175. #endif
  176. };
  177. static int cmdindex = 0;
  178. int i;
  179. int status;
  180. char buffer[256];
  181. i = cmdindex;
  182. status = 1;
  183. do {
  184. f = popen(commands[i++], "r");
  185. if (i >= sizeof(commands)/sizeof(commands[0]))
  186. i = 1; /* last(1) is only useful once. */
  187. if (!f)
  188. continue;
  189. if (i == cmdindex)
  190. break;
  191. while (!feof(f)) {
  192. status = fread(buffer, 1, sizeof(buffer), f);
  193. if (status < 0)
  194. break;
  195. shaUpdate(sha, (unsigned char *)buffer,
  196.           (unsigned)status);
  197. }
  198. status = pclose(f);
  199. }  while (status); /* Repeat until one succeeds */
  200. cmdindex = i;
  201. memset(buffer, 0, sizeof(buffer));
  202. randpool_update(sha);
  203. }
  204. // Buffer of pre-accumulated random bytes.
  205. static unsigned char randbuf[SHA_DIGESTSIZE];
  206. static unsigned randbufindex = SHA_DIGESTSIZE;
  207. // Insert some noise of external origin into the random number pool.
  208. static void
  209. randpool_addbytes(unsigned char const *buf, unsigned len)
  210. {
  211. struct SHAContext sha;
  212. shaInit(&sha);
  213. shaUpdate(&sha, buf, len);
  214. randpool_update(&sha);
  215. shaFinal(&sha, randbuf);
  216. randbufindex = 0;
  217. }
  218. // Return the desired number of bytes.  Consumes data left in the randbuf
  219. // first, then hashes in the current time and, if it isn't too soon,
  220. // a bunch of system statistics, to the randpool.
  221. void
  222. randpool_getbytes(unsigned char *buf, unsigned len)
  223. {
  224. struct timeval tv;
  225. unsigned avail;
  226. struct SHAContext sha;
  227. static time_t prev = 0;
  228. if (!prev) { // First time, do full initialization
  229. pid_t p;
  230. gettimeofday(&tv, 0);
  231. shaInit(&sha);
  232. shaUpdate(&sha, (unsigned char *)&tv, sizeof(tv));
  233. prev = tv.tv_sec;
  234. memset(&tv, 0, sizeof(tv));
  235. p = getpid();
  236. shaUpdate(&sha, (unsigned char *)&p, sizeof(p));
  237. p = getppid();
  238. shaUpdate(&sha, (unsigned char *)&p, sizeof(p));
  239. randpool_update(&sha);
  240. for (avail = 0; avail < 9; avail++)
  241. randpool_getentropy(&sha);
  242. shaFinal(&sha, randbuf);
  243. randbufindex = 0;
  244. }
  245. avail = SHA_DIGESTSIZE-randbufindex;
  246. if (avail > len)
  247. avail = len;
  248. if (avail) {
  249. memcpy(buf, randbuf+randbufindex, avail);
  250. len -= avail;
  251. buf += avail;
  252. memset(randbuf+randbufindex, 0, avail);
  253. }
  254. if (!len) {
  255. randbufindex += avail;
  256. return;
  257. }
  258. gettimeofday(&tv, 0);
  259. shaInit(&sha);
  260. // If it's been at least THROTTLE seconds since last
  261. // time, get more system statistics.  If it's been a lot more,
  262. // get multiple statistics.
  263. // NOTE the comparison which deals properly with time warping
  264. // backwards by considering time to have expired.
  265. if ((unsigned)(tv.tv_sec-prev) > THROTTLE) {
  266. avail = 5; /* Maximum to hash in */
  267. do {
  268. randpool_getentropy(&sha);
  269. prev += THROTTLE;
  270. } while ((unsigned)(tv.tv_sec-prev) > THROTTLE && --avail);
  271. prev = tv.tv_sec;
  272. }
  273. shaUpdate(&sha, (unsigned char *)&tv, sizeof(tv));
  274. memset(&tv, 0, sizeof(tv));
  275. while (len > SHA_DIGESTSIZE) {
  276. randpool_update(&sha);
  277. shaFinal(&sha, buf);
  278. buf += SHA_DIGESTSIZE;
  279. len -= SHA_DIGESTSIZE;
  280. shaInit(&sha);
  281. }
  282. shaFinal(&sha, randbuf);
  283. memcpy(buf, randbuf, len);
  284. memset(randbuf, 0, len);
  285. randbufindex = len;
  286. }
  287. // In interactive applications, this function can be used to
  288. // augment available randomness by getting some from the user.
  289. void
  290. prompt_user_for_randomness()
  291. {
  292. struct timeval t;
  293. int count = 0;
  294. char c;
  295. printf("It would help the quality of the random numbers if you wouldn"
  296.        "type 50-100 random keys on the keyboard.  Hit return whenn"
  297.        "you are done.n");
  298. system("stty raw -echo");
  299. for (;;) {
  300. if (read(0, &c, 1) != 1) {
  301. perror("read");
  302. break;
  303. }
  304. gettimeofday(&t, (struct timezone *) 0);
  305. // Place c into timeval to save a call to randpool_addbytes
  306. t.tv_usec += c << 24;
  307. randpool_addbytes((unsigned char *)&t, sizeof(t));
  308. if (c == 'n' || c == 'r')
  309.   break;
  310. printf("r%-5d", ++count);
  311. fflush(stdout);
  312. }
  313. printf("rn");
  314. system("stty sane");
  315. }
  316. Bstream
  317. get_random_bytes(int len)
  318. {
  319. byte c;
  320. Bstream output(len, &c); // Ugly hack...
  321. randpool_getbytes(output.getdatap(), len);
  322. return output;
  323. }