]> git.sur5r.net Git - openldap/blobdiff - libraries/liblutil/entropy.c
Merge remote-tracking branch 'origin/mdb.master' into OPENLDAP_REL_ENG_2_4
[openldap] / libraries / liblutil / entropy.c
index 4a15a789d462e429c41ee294ab07d5380a47ecdc..57db8c281760a91cd9eb94c344c63fc8e9d69e8a 100644 (file)
@@ -1,11 +1,34 @@
+/* entropy.c -- routines for providing pseudo-random data */
 /* $OpenLDAP$ */
-/*
- * Copyright 1998-1999 The OpenLDAP Foundation, All Rights Reserved.
- * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2013 The OpenLDAP Foundation.
+ * Portions Copyright 1999-2003 Kurt D. Zeilenga.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* This work was initially developed by Kurt D. Zeilenga for
+ * inclusion in OpenLDAP Software based, in part, on publically
+ * available works (as noted below).
  */
 
 #include "portable.h"
 
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+
+#ifdef HAVE_PROCESS_H
+#include <process.h>
+#endif
+
 #include <fcntl.h>
 
 #include <lutil.h>
 /*
  * lutil_entropy() provides nbytes of entropy in buf.
  * Quality offerred is suitable for one-time uses, such as "once" keys.
+ * Values may not be suitable for multi-time uses.
+ *
+ * Note:  Callers are encouraged to provide additional bytes of
+ * of entropy in the buf argument.  This information is used in
+ * fallback mode to improve the quality of bytes returned.
+ *
+ * This routinue should be extended to support additional sources
+ * of entropy.
  */
-int lutil_entropy( char *buf, int nbytes )
+int lutil_entropy( unsigned char *buf, ber_len_t nbytes )
 {
-       if( nbytes < 0 ) return -1;
        if( nbytes == 0 ) return 0;
 
-#undef URANDOM_DEVICE
 #ifdef URANDOM_DEVICE
+#define URANDOM_NREADS 4
        /* Linux and *BSD offer a urandom device */
        {
-               int rc, fd;
+               int rc, fd, n=0;
 
                fd = open( URANDOM_DEVICE, O_RDONLY );
 
                if( fd < 0 ) return -1;
 
-               rc = read( fd, buf, nbytes );
+               do {
+                       rc = read( fd, buf, nbytes );
+                       if( rc <= 0 ) break;
+
+                       buf+=rc;
+                       nbytes-=rc;
+
+                       if( ++n >= URANDOM_NREADS ) break;
+               } while( nbytes > 0 );
+
                close(fd);
+               return nbytes > 0 ? -1 : 0;
+       }
+#elif defined(PROV_RSA_FULL)
+       {
+               /* Not used since _WIN32_WINNT not set... */
+               HCRYPTPROV hProv = 0;
 
-               /* should return nbytes */
-               if( rc < nbytes ) return -1;
+               /* Get handle to user default provider */
+               if(!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0)) {
+                  return -1;
+               }
+
+               /* Generate random initialization vector */
+               if(!CryptGenRandom(hProv, (DWORD) nbytes, (BYTE *) buf)) {
+                  return -1;
+               }
+
+               /* Release provider handle */
+               if(hProv != 0) CryptReleaseContext(hProv, 0);
 
                return 0;
        }
 #else
        {
                /* based upon Phil Karn's "practical randomness" idea
-                * but implementation 100% OpenLDAP.  So don't blame Phil. */
-               /* worse case is this is a MD5 hash of a counter, if
-                *      MD5 is a strong cryptographic hash, this should
-                *      be fairly resistant to attack
+                * but implementation 100% OpenLDAP.  So don't blame Phil.
+                *
+                * Worse case is that this is a MD5 hash of a counter, if
+                * MD5 is a strong cryptographic hash, this should be fairly
+                * resistant to attack
+                */
+
+               /*
+                * the caller may need to provide external synchronization OR
+                * provide entropy (in buf) to ensure quality results as
+                * access to this counter may not be atomic.
                 */
                static int counter = 0;
-               int n;
+               ber_len_t n;
 
                struct rdata_s {
                        int counter;
 
-                       char *buf;
+                       unsigned char *buf;
                        struct rdata_s *stack;
 
                        pid_t   pid;
 
 #ifdef HAVE_GETTIMEOFDAY
-                       struct timeval *tv;
+                       struct timeval tv;
 #else
                        time_t  time;
 #endif
-                       unsigned long   junk;
+
+                       unsigned long   junk;   /* purposely not initialized */
                } rdata;
 
                /* make sure rdata differs for each process */
@@ -74,11 +137,11 @@ int lutil_entropy( char *buf, int nbytes )
 
                for( n = 0; n < nbytes; n += 16 ) {
                        struct lutil_MD5Context ctx;
-                       char digest[16];
+                       unsigned char digest[16];
 
-                       /* hopefully has good resolution */
+                       /* poor resolution */
 #ifdef HAVE_GETTIMEOFDAY
-                       (void) gettimeofday( &rdata.tv, sizeof( rdata.tv ) );
+                       (void) gettimeofday( &rdata.tv, NULL );
 #else
                        (void) time( &rdata.time );
 #endif
@@ -89,11 +152,15 @@ int lutil_entropy( char *buf, int nbytes )
                        rdata.junk++;
 
                        lutil_MD5Init( &ctx );
-                       lutil_MD5Update( &ctx, (char *) &rdata, sizeof( rdata ) );
+                       lutil_MD5Update( &ctx, (unsigned char *) &rdata, sizeof( rdata ) );
+
+                       /* allow caller to provided additional entropy */
+                       lutil_MD5Update( &ctx, buf, nbytes );
+
                        lutil_MD5Final( digest, &ctx );
 
-                       memcpy( &buf[n], digest,
-                               nbytes - n > 16 ? 16 : nbytes - n );
+                       AC_MEMCPY( &buf[n], digest,
+                               nbytes - n >= 16 ? 16 : nbytes - n );
                }
 
                return 0;