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