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