random-platform.c (3210B)
1/* 2 * QEMU Crypto random number provider 3 * 4 * Copyright (c) 2015-2016 Red Hat, Inc. 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, see <http://www.gnu.org/licenses/>. 18 * 19 */ 20 21#include "qemu/osdep.h" 22 23#include "crypto/random.h" 24#include "qapi/error.h" 25 26#ifdef _WIN32 27#include <wincrypt.h> 28static HCRYPTPROV hCryptProv; 29#else 30# ifdef CONFIG_GETRANDOM 31# include <sys/random.h> 32# endif 33/* This is -1 for getrandom(), or a file handle for /dev/{u,}random. */ 34static int fd; 35#endif 36 37int qcrypto_random_init(Error **errp) 38{ 39#ifdef _WIN32 40 if (!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, 41 CRYPT_SILENT | CRYPT_VERIFYCONTEXT)) { 42 error_setg_win32(errp, GetLastError(), 43 "Unable to create cryptographic provider"); 44 return -1; 45 } 46#else 47# ifdef CONFIG_GETRANDOM 48 if (getrandom(NULL, 0, 0) == 0) { 49 /* Use getrandom() */ 50 fd = -1; 51 return 0; 52 } 53 /* Fall through to /dev/urandom case. */ 54# endif 55 fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC); 56 if (fd == -1 && errno == ENOENT) { 57 fd = open("/dev/random", O_RDONLY | O_CLOEXEC); 58 } 59 if (fd < 0) { 60 error_setg_errno(errp, errno, "No /dev/urandom or /dev/random"); 61 return -1; 62 } 63#endif 64 return 0; 65} 66 67int qcrypto_random_bytes(void *buf, 68 size_t buflen, 69 Error **errp) 70{ 71#ifdef _WIN32 72 if (!CryptGenRandom(hCryptProv, buflen, buf)) { 73 error_setg_win32(errp, GetLastError(), 74 "Unable to read random bytes"); 75 return -1; 76 } 77#else 78# ifdef CONFIG_GETRANDOM 79 if (likely(fd < 0)) { 80 while (1) { 81 ssize_t got = getrandom(buf, buflen, 0); 82 if (likely(got == buflen)) { 83 return 0; 84 } 85 if (got >= 0) { 86 buflen -= got; 87 buf += got; 88 } else if (errno != EINTR) { 89 error_setg_errno(errp, errno, "getrandom"); 90 return -1; 91 } 92 } 93 } 94 /* Fall through to /dev/urandom case. */ 95# endif 96 while (1) { 97 ssize_t got = read(fd, buf, buflen); 98 if (likely(got == buflen)) { 99 return 0; 100 } 101 if (got > 0) { 102 buflen -= got; 103 buf += got; 104 } else if (got == 0) { 105 error_setg(errp, "Unexpected EOF reading random bytes"); 106 return -1; 107 } else if (errno != EINTR) { 108 error_setg_errno(errp, errno, "Unable to read random bytes"); 109 return -1; 110 } 111 } 112#endif 113 return 0; 114}