xref: /netbsd-src/external/bsd/openldap/dist/libraries/liblutil/entropy.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
1*549b59edSchristos /*	$NetBSD: entropy.c,v 1.3 2021/08/14 16:14:58 christos Exp $	*/
24e6df137Slukem 
32de962bdSlukem /* entropy.c -- routines for providing pseudo-random data */
4d11b170bStron /* $OpenLDAP$ */
52de962bdSlukem /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
62de962bdSlukem  *
7*549b59edSchristos  * Copyright 1999-2021 The OpenLDAP Foundation.
82de962bdSlukem  * Portions Copyright 1999-2003 Kurt D. Zeilenga.
92de962bdSlukem  * All rights reserved.
102de962bdSlukem  *
112de962bdSlukem  * Redistribution and use in source and binary forms, with or without
122de962bdSlukem  * modification, are permitted only as authorized by the OpenLDAP
132de962bdSlukem  * Public License.
142de962bdSlukem  *
152de962bdSlukem  * A copy of this license is available in the file LICENSE in the
162de962bdSlukem  * top-level directory of the distribution or, alternatively, at
172de962bdSlukem  * <http://www.OpenLDAP.org/license.html>.
182de962bdSlukem  */
192de962bdSlukem /* This work was initially developed by Kurt D. Zeilenga for
20*549b59edSchristos  * inclusion in OpenLDAP Software based, in part, on publicly
212de962bdSlukem  * available works (as noted below).
222de962bdSlukem  */
232de962bdSlukem 
24376af7d7Schristos #include <sys/cdefs.h>
25*549b59edSchristos __RCSID("$NetBSD: entropy.c,v 1.3 2021/08/14 16:14:58 christos Exp $");
26376af7d7Schristos 
272de962bdSlukem #include "portable.h"
282de962bdSlukem 
292de962bdSlukem #include <ac/string.h>
302de962bdSlukem #include <ac/time.h>
312de962bdSlukem #include <ac/unistd.h>
322de962bdSlukem 
332de962bdSlukem #ifdef HAVE_PROCESS_H
342de962bdSlukem #include <process.h>
352de962bdSlukem #endif
362de962bdSlukem 
372de962bdSlukem #include <fcntl.h>
382de962bdSlukem 
392de962bdSlukem #include <lutil.h>
402de962bdSlukem #include <lutil_md5.h>
412de962bdSlukem 
422de962bdSlukem /*
432de962bdSlukem  * lutil_entropy() provides nbytes of entropy in buf.
44*549b59edSchristos  * Quality offered is suitable for one-time uses, such as "once" keys.
452de962bdSlukem  * Values may not be suitable for multi-time uses.
462de962bdSlukem  *
472de962bdSlukem  * Note:  Callers are encouraged to provide additional bytes of
482de962bdSlukem  * of entropy in the buf argument.  This information is used in
492de962bdSlukem  * fallback mode to improve the quality of bytes returned.
502de962bdSlukem  *
512de962bdSlukem  * This routinue should be extended to support additional sources
522de962bdSlukem  * of entropy.
532de962bdSlukem  */
lutil_entropy(unsigned char * buf,ber_len_t nbytes)542de962bdSlukem int lutil_entropy( unsigned char *buf, ber_len_t nbytes )
552de962bdSlukem {
562de962bdSlukem 	if( nbytes == 0 ) return 0;
572de962bdSlukem 
582de962bdSlukem #ifdef URANDOM_DEVICE
592de962bdSlukem #define URANDOM_NREADS 4
602de962bdSlukem 	/* Linux and *BSD offer a urandom device */
612de962bdSlukem 	{
622de962bdSlukem 		int rc, fd, n=0;
632de962bdSlukem 
642de962bdSlukem 		fd = open( URANDOM_DEVICE, O_RDONLY );
652de962bdSlukem 
662de962bdSlukem 		if( fd < 0 ) return -1;
672de962bdSlukem 
682de962bdSlukem 		do {
692de962bdSlukem 			rc = read( fd, buf, nbytes );
702de962bdSlukem 			if( rc <= 0 ) break;
712de962bdSlukem 
722de962bdSlukem 			buf+=rc;
732de962bdSlukem 			nbytes-=rc;
742de962bdSlukem 
752de962bdSlukem 			if( ++n >= URANDOM_NREADS ) break;
762de962bdSlukem 		} while( nbytes > 0 );
772de962bdSlukem 
782de962bdSlukem 		close(fd);
792de962bdSlukem 		return nbytes > 0 ? -1 : 0;
802de962bdSlukem 	}
81d11b170bStron #elif defined(PROV_RSA_FULL)
822de962bdSlukem 	{
832de962bdSlukem 		/* Not used since _WIN32_WINNT not set... */
842de962bdSlukem 		HCRYPTPROV hProv = 0;
852de962bdSlukem 
862de962bdSlukem 		/* Get handle to user default provider */
872de962bdSlukem 		if(!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0)) {
882de962bdSlukem 		   return -1;
892de962bdSlukem 		}
902de962bdSlukem 
912de962bdSlukem 		/* Generate random initialization vector */
922de962bdSlukem 		if(!CryptGenRandom(hProv, (DWORD) nbytes, (BYTE *) buf)) {
932de962bdSlukem 		   return -1;
942de962bdSlukem 		}
952de962bdSlukem 
962de962bdSlukem 		/* Release provider handle */
972de962bdSlukem 		if(hProv != 0) CryptReleaseContext(hProv, 0);
982de962bdSlukem 
992de962bdSlukem 		return 0;
1002de962bdSlukem 	}
1012de962bdSlukem #else
1022de962bdSlukem 	{
1032de962bdSlukem 		/* based upon Phil Karn's "practical randomness" idea
1042de962bdSlukem 		 * but implementation 100% OpenLDAP.  So don't blame Phil.
1052de962bdSlukem 		 *
1062de962bdSlukem 		 * Worse case is that this is a MD5 hash of a counter, if
1072de962bdSlukem 		 * MD5 is a strong cryptographic hash, this should be fairly
1082de962bdSlukem 		 * resistant to attack
1092de962bdSlukem 		 */
1102de962bdSlukem 
1112de962bdSlukem 		/*
1122de962bdSlukem 		 * the caller may need to provide external synchronization OR
1132de962bdSlukem 		 * provide entropy (in buf) to ensure quality results as
1142de962bdSlukem 		 * access to this counter may not be atomic.
1152de962bdSlukem 		 */
1162de962bdSlukem 		static int counter = 0;
1172de962bdSlukem 		ber_len_t n;
1182de962bdSlukem 
1192de962bdSlukem 		struct rdata_s {
1202de962bdSlukem 			int counter;
1212de962bdSlukem 
1222de962bdSlukem 			unsigned char *buf;
1232de962bdSlukem 			struct rdata_s *stack;
1242de962bdSlukem 
1252de962bdSlukem 			pid_t	pid;
1262de962bdSlukem 
1272de962bdSlukem #ifdef HAVE_GETTIMEOFDAY
1282de962bdSlukem 			struct timeval tv;
1292de962bdSlukem #else
1302de962bdSlukem 			time_t	time;
1312de962bdSlukem #endif
1322de962bdSlukem 
1332de962bdSlukem 			unsigned long	junk;	/* purposely not initialized */
1342de962bdSlukem 		} rdata;
1352de962bdSlukem 
1362de962bdSlukem 		/* make sure rdata differs for each process */
1372de962bdSlukem 		rdata.pid = getpid();
1382de962bdSlukem 
1392de962bdSlukem 		/* make sure rdata differs for each program */
1402de962bdSlukem 		rdata.buf = buf;
1412de962bdSlukem 		rdata.stack = &rdata;
1422de962bdSlukem 
1432de962bdSlukem 		for( n = 0; n < nbytes; n += 16 ) {
1442de962bdSlukem 			struct lutil_MD5Context ctx;
1452de962bdSlukem 			unsigned char digest[16];
1462de962bdSlukem 
1472de962bdSlukem 			/* poor resolution */
1482de962bdSlukem #ifdef HAVE_GETTIMEOFDAY
1492de962bdSlukem 			(void) gettimeofday( &rdata.tv, NULL );
1502de962bdSlukem #else
1512de962bdSlukem 			(void) time( &rdata.time );
1522de962bdSlukem #endif
1532de962bdSlukem 
1542de962bdSlukem 			/* make sure rdata differs */
1552de962bdSlukem 			rdata.counter = ++counter;
1562de962bdSlukem 			rdata.pid++;
1572de962bdSlukem 			rdata.junk++;
1582de962bdSlukem 
1592de962bdSlukem 			lutil_MD5Init( &ctx );
1602de962bdSlukem 			lutil_MD5Update( &ctx, (unsigned char *) &rdata, sizeof( rdata ) );
1612de962bdSlukem 
1622de962bdSlukem 			/* allow caller to provided additional entropy */
1632de962bdSlukem 			lutil_MD5Update( &ctx, buf, nbytes );
1642de962bdSlukem 
1652de962bdSlukem 			lutil_MD5Final( digest, &ctx );
1662de962bdSlukem 
1672de962bdSlukem 			AC_MEMCPY( &buf[n], digest,
1682de962bdSlukem 				nbytes - n >= 16 ? 16 : nbytes - n );
1692de962bdSlukem 		}
1702de962bdSlukem 
1712de962bdSlukem 		return 0;
1722de962bdSlukem 	}
1732de962bdSlukem #endif
1742de962bdSlukem 	return -1;
1752de962bdSlukem }
176