unix_urandom.c (2554B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #include <fcntl.h> 6 #include <unistd.h> 7 #include <errno.h> 8 #include "secerr.h" 9 #include "secrng.h" 10 #include "prprf.h" 11 12 /* syscall getentropy() is limited to retrieving 256 bytes */ 13 #define GETENTROPY_MAX_BYTES 256 14 15 void 16 RNG_SystemInfoForRNG(void) 17 { 18 PRUint8 bytes[SYSTEM_RNG_SEED_COUNT]; 19 size_t numBytes = RNG_SystemRNG(bytes, SYSTEM_RNG_SEED_COUNT); 20 if (!numBytes) { 21 /* error is set */ 22 return; 23 } 24 RNG_RandomUpdate(bytes, numBytes); 25 PORT_SafeZero(bytes, sizeof bytes); 26 } 27 28 size_t 29 RNG_SystemRNG(void *dest, size_t maxLen) 30 { 31 int fd; 32 int bytes; 33 size_t fileBytes = 0; 34 unsigned char *buffer = dest; 35 36 #if defined(__OpenBSD__) || (defined(__FreeBSD__) && __FreeBSD_version >= 1200000) || (defined(LINUX) && defined(__GLIBC__) && ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 25)))) 37 int result; 38 39 while (fileBytes < maxLen) { 40 size_t getBytes = maxLen - fileBytes; 41 if (getBytes > GETENTROPY_MAX_BYTES) { 42 getBytes = GETENTROPY_MAX_BYTES; 43 } 44 45 /* get entropy returns 0 on success and always return 46 * getBytes on success */ 47 result = getentropy(buffer, getBytes); 48 if (result == 0) { /* success */ 49 fileBytes += getBytes; 50 buffer += getBytes; 51 } else { 52 break; 53 } 54 } 55 if (fileBytes == maxLen) { /* success */ 56 return maxLen; 57 } 58 /* If we failed with an error other than ENOSYS, it means the destination 59 * buffer is not writeable. We don't need to try writing to it again. */ 60 if (errno != ENOSYS) { 61 PORT_SetError(SEC_ERROR_NEED_RANDOM); 62 return 0; 63 } 64 /* ENOSYS means the kernel doesn't support getentropy()/getrandom(). 65 * Reset the number of bytes to get and fall back to /dev/urandom. */ 66 fileBytes = 0; 67 #endif /* platorm has getentropy */ 68 fd = open("/dev/urandom", O_RDONLY); 69 if (fd < 0) { 70 PORT_SetError(SEC_ERROR_NEED_RANDOM); 71 return 0; 72 } 73 while (fileBytes < maxLen) { 74 bytes = read(fd, buffer, maxLen - fileBytes); 75 if (bytes <= 0) { 76 break; 77 } 78 fileBytes += bytes; 79 buffer += bytes; 80 } 81 (void)close(fd); 82 if (fileBytes != maxLen) { 83 PORT_SetError(SEC_ERROR_NEED_RANDOM); 84 return 0; 85 } 86 return fileBytes; 87 }