entropy.c revision 1.3
1/* $NetBSD: entropy.c,v 1.3 2021/08/14 16:14:58 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-2021 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.3 2021/08/14 16:14:58 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 */ 54int 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