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