Home | History | Annotate | Line # | Download | only in liblutil
      1 /*	$NetBSD: entropy.c,v 1.4 2025/09/05 21:16:23 christos Exp $	*/
      2 
      3 /* entropy.c -- routines for providing pseudo-random data */
      4 /* $OpenLDAP$ */
      5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      6  *
      7  * Copyright 1999-2024 The OpenLDAP Foundation.
      8  * Portions Copyright 1999-2003 Kurt D. Zeilenga.
      9  * All rights reserved.
     10  *
     11  * Redistribution and use in source and binary forms, with or without
     12  * modification, are permitted only as authorized by the OpenLDAP
     13  * Public License.
     14  *
     15  * A copy of this license is available in the file LICENSE in the
     16  * top-level directory of the distribution or, alternatively, at
     17  * <http://www.OpenLDAP.org/license.html>.
     18  */
     19 /* This work was initially developed by Kurt D. Zeilenga for
     20  * inclusion in OpenLDAP Software based, in part, on publicly
     21  * available works (as noted below).
     22  */
     23 
     24 #include <sys/cdefs.h>
     25 __RCSID("$NetBSD: entropy.c,v 1.4 2025/09/05 21:16:23 christos Exp $");
     26 
     27 #include "portable.h"
     28 
     29 #include <ac/string.h>
     30 #include <ac/time.h>
     31 #include <ac/unistd.h>
     32 
     33 #ifdef HAVE_PROCESS_H
     34 #include <process.h>
     35 #endif
     36 
     37 #include <fcntl.h>
     38 
     39 #include <lutil.h>
     40 #include <lutil_md5.h>
     41 
     42 /*
     43  * lutil_entropy() provides nbytes of entropy in buf.
     44  * Quality offered is suitable for one-time uses, such as "once" keys.
     45  * Values may not be suitable for multi-time uses.
     46  *
     47  * Note:  Callers are encouraged to provide additional bytes of
     48  * of entropy in the buf argument.  This information is used in
     49  * fallback mode to improve the quality of bytes returned.
     50  *
     51  * This routinue should be extended to support additional sources
     52  * of entropy.
     53  */
     54 int lutil_entropy( unsigned char *buf, ber_len_t nbytes )
     55 {
     56 	if( nbytes == 0 ) return 0;
     57 
     58 #ifdef URANDOM_DEVICE
     59 #define URANDOM_NREADS 4
     60 	/* Linux and *BSD offer a urandom device */
     61 	{
     62 		int rc, fd, n=0;
     63 
     64 		fd = open( URANDOM_DEVICE, O_RDONLY );
     65 
     66 		if( fd < 0 ) return -1;
     67 
     68 		do {
     69 			rc = read( fd, buf, nbytes );
     70 			if( rc <= 0 ) break;
     71 
     72 			buf+=rc;
     73 			nbytes-=rc;
     74 
     75 			if( ++n >= URANDOM_NREADS ) break;
     76 		} while( nbytes > 0 );
     77 
     78 		close(fd);
     79 		return nbytes > 0 ? -1 : 0;
     80 	}
     81 #elif defined(PROV_RSA_FULL)
     82 	{
     83 		/* Not used since _WIN32_WINNT not set... */
     84 		HCRYPTPROV hProv = 0;
     85 
     86 		/* Get handle to user default provider */
     87 		if(!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0)) {
     88 		   return -1;
     89 		}
     90 
     91 		/* Generate random initialization vector */
     92 		if(!CryptGenRandom(hProv, (DWORD) nbytes, (BYTE *) buf)) {
     93 		   return -1;
     94 		}
     95 
     96 		/* Release provider handle */
     97 		if(hProv != 0) CryptReleaseContext(hProv, 0);
     98 
     99 		return 0;
    100 	}
    101 #else
    102 	{
    103 		/* based upon Phil Karn's "practical randomness" idea
    104 		 * but implementation 100% OpenLDAP.  So don't blame Phil.
    105 		 *
    106 		 * Worse case is that this is a MD5 hash of a counter, if
    107 		 * MD5 is a strong cryptographic hash, this should be fairly
    108 		 * resistant to attack
    109 		 */
    110 
    111 		/*
    112 		 * the caller may need to provide external synchronization OR
    113 		 * provide entropy (in buf) to ensure quality results as
    114 		 * access to this counter may not be atomic.
    115 		 */
    116 		static int counter = 0;
    117 		ber_len_t n;
    118 
    119 		struct rdata_s {
    120 			int counter;
    121 
    122 			unsigned char *buf;
    123 			struct rdata_s *stack;
    124 
    125 			pid_t	pid;
    126 
    127 #ifdef HAVE_GETTIMEOFDAY
    128 			struct timeval tv;
    129 #else
    130 			time_t	time;
    131 #endif
    132 
    133 			unsigned long	junk;	/* purposely not initialized */
    134 		} rdata;
    135 
    136 		/* make sure rdata differs for each process */
    137 		rdata.pid = getpid();
    138 
    139 		/* make sure rdata differs for each program */
    140 		rdata.buf = buf;
    141 		rdata.stack = &rdata;
    142 
    143 		for( n = 0; n < nbytes; n += 16 ) {
    144 			struct lutil_MD5Context ctx;
    145 			unsigned char digest[16];
    146 
    147 			/* poor resolution */
    148 #ifdef HAVE_GETTIMEOFDAY
    149 			(void) gettimeofday( &rdata.tv, NULL );
    150 #else
    151 			(void) time( &rdata.time );
    152 #endif
    153 
    154 			/* make sure rdata differs */
    155 			rdata.counter = ++counter;
    156 			rdata.pid++;
    157 			rdata.junk++;
    158 
    159 			lutil_MD5Init( &ctx );
    160 			lutil_MD5Update( &ctx, (unsigned char *) &rdata, sizeof( rdata ) );
    161 
    162 			/* allow caller to provided additional entropy */
    163 			lutil_MD5Update( &ctx, buf, nbytes );
    164 
    165 			lutil_MD5Final( digest, &ctx );
    166 
    167 			AC_MEMCPY( &buf[n], digest,
    168 				nbytes - n >= 16 ? 16 : nbytes - n );
    169 		}
    170 
    171 		return 0;
    172 	}
    173 #endif
    174 	return -1;
    175 }
    176