Back to index

openldap  2.4.31
entropy.c
Go to the documentation of this file.
00001 /* entropy.c -- routines for providing pseudo-random data */
00002 /* $OpenLDAP$ */
00003 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00004  *
00005  * Copyright 1999-2012 The OpenLDAP Foundation.
00006  * Portions Copyright 1999-2003 Kurt D. Zeilenga.
00007  * All rights reserved.
00008  *
00009  * Redistribution and use in source and binary forms, with or without
00010  * modification, are permitted only as authorized by the OpenLDAP
00011  * Public License.
00012  *
00013  * A copy of this license is available in the file LICENSE in the
00014  * top-level directory of the distribution or, alternatively, at
00015  * <http://www.OpenLDAP.org/license.html>.
00016  */
00017 /* This work was initially developed by Kurt D. Zeilenga for
00018  * inclusion in OpenLDAP Software based, in part, on publically
00019  * available works (as noted below).
00020  */
00021 
00022 #include "portable.h"
00023 
00024 #include <ac/string.h>
00025 #include <ac/time.h>
00026 #include <ac/unistd.h>
00027 
00028 #ifdef HAVE_PROCESS_H
00029 #include <process.h>
00030 #endif
00031 
00032 #include <fcntl.h>
00033 
00034 #include <lutil.h>
00035 #include <lutil_md5.h>
00036 
00037 /*
00038  * lutil_entropy() provides nbytes of entropy in buf.
00039  * Quality offerred is suitable for one-time uses, such as "once" keys.
00040  * Values may not be suitable for multi-time uses.
00041  *
00042  * Note:  Callers are encouraged to provide additional bytes of
00043  * of entropy in the buf argument.  This information is used in
00044  * fallback mode to improve the quality of bytes returned.
00045  *
00046  * This routinue should be extended to support additional sources
00047  * of entropy.
00048  */
00049 int lutil_entropy( unsigned char *buf, ber_len_t nbytes )
00050 {
00051        if( nbytes == 0 ) return 0;
00052 
00053 #ifdef URANDOM_DEVICE
00054 #define URANDOM_NREADS 4
00055        /* Linux and *BSD offer a urandom device */
00056        {
00057               int rc, fd, n=0;
00058 
00059               fd = open( URANDOM_DEVICE, O_RDONLY );
00060 
00061               if( fd < 0 ) return -1;
00062 
00063               do {
00064                      rc = read( fd, buf, nbytes );
00065                      if( rc <= 0 ) break;
00066 
00067                      buf+=rc;
00068                      nbytes-=rc;
00069 
00070                      if( ++n >= URANDOM_NREADS ) break;
00071               } while( nbytes > 0 );
00072 
00073               close(fd);
00074               return nbytes > 0 ? -1 : 0;
00075        }
00076 #elif defined(PROV_RSA_FULL)
00077        {
00078               /* Not used since _WIN32_WINNT not set... */
00079               HCRYPTPROV hProv = 0;
00080 
00081               /* Get handle to user default provider */
00082               if(!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0)) {
00083                  return -1;
00084               }
00085 
00086               /* Generate random initialization vector */
00087               if(!CryptGenRandom(hProv, (DWORD) nbytes, (BYTE *) buf)) {
00088                  return -1;
00089               }
00090 
00091               /* Release provider handle */
00092               if(hProv != 0) CryptReleaseContext(hProv, 0);
00093 
00094               return 0;
00095        }
00096 #else
00097        {
00098               /* based upon Phil Karn's "practical randomness" idea
00099                * but implementation 100% OpenLDAP.  So don't blame Phil.
00100                *
00101                * Worse case is that this is a MD5 hash of a counter, if
00102                * MD5 is a strong cryptographic hash, this should be fairly
00103                * resistant to attack
00104                */
00105 
00106               /*
00107                * the caller may need to provide external synchronization OR
00108                * provide entropy (in buf) to ensure quality results as
00109                * access to this counter may not be atomic.
00110                */
00111               static int counter = 0;
00112               ber_len_t n;
00113 
00114               struct rdata_s {
00115                      int counter;
00116 
00117                      unsigned char *buf;
00118                      struct rdata_s *stack;
00119 
00120                      pid_t  pid;
00121 
00122 #ifdef HAVE_GETTIMEOFDAY
00123                      struct timeval tv;
00124 #else
00125                      time_t time;
00126 #endif
00127 
00128                      unsigned long junk;  /* purposely not initialized */
00129               } rdata;
00130 
00131               /* make sure rdata differs for each process */
00132               rdata.pid = getpid();
00133 
00134               /* make sure rdata differs for each program */
00135               rdata.buf = buf;
00136               rdata.stack = &rdata;
00137 
00138               for( n = 0; n < nbytes; n += 16 ) {
00139                      struct lutil_MD5Context ctx;
00140                      unsigned char digest[16];
00141 
00142                      /* poor resolution */
00143 #ifdef HAVE_GETTIMEOFDAY
00144                      (void) gettimeofday( &rdata.tv, NULL );
00145 #else
00146                      (void) time( &rdata.time );
00147 #endif
00148 
00149                      /* make sure rdata differs */
00150                      rdata.counter = ++counter;
00151                      rdata.pid++;
00152                      rdata.junk++;
00153 
00154                      lutil_MD5Init( &ctx );
00155                      lutil_MD5Update( &ctx, (unsigned char *) &rdata, sizeof( rdata ) );
00156 
00157                      /* allow caller to provided additional entropy */
00158                      lutil_MD5Update( &ctx, buf, nbytes );
00159 
00160                      lutil_MD5Final( digest, &ctx );
00161 
00162                      AC_MEMCPY( &buf[n], digest,
00163                             nbytes - n >= 16 ? 16 : nbytes - n );
00164               }
00165 
00166               return 0;
00167        }
00168 #endif
00169        return -1;
00170 }