// Copyright 2014 The BoringSSL Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #if !defined(_GNU_SOURCE) #define _GNU_SOURCE // needed for syscall() on Linux. #endif #include #include "../bcm_support.h" #include "internal.h" #if defined(OPENSSL_RAND_URANDOM) #include #include #include #include #include #include #include "../internal.h" using namespace bssl; #if defined(OPENSSL_MSAN) extern "C" { void __msan_unpoison(void *, size_t); } #endif static ssize_t boringssl_getrandom(void *buf, size_t buf_len, unsigned flags) { ssize_t ret; do { ret = syscall(__NR_getrandom, buf, buf_len, flags); } while (ret == -1 && errno == EINTR); #if defined(OPENSSL_MSAN) if (ret > 0) { // MSAN doesn't recognise |syscall| and thus doesn't notice that we have // initialised the output buffer. __msan_unpoison(buf, ret); } #endif // OPENSSL_MSAN return ret; } // kHaveGetrandom in |urandom_fd| signals that |getrandom| or |getentropy| is // available and should be used instead. static const int kHaveGetrandom = -3; // urandom_fd is a file descriptor to /dev/urandom. It's protected by |once|. static int urandom_fd; static CRYPTO_once_t rand_once = CRYPTO_ONCE_INIT; // init_once initializes the state of this module to values previously // requested. This is the only function that modifies |urandom_fd|, which may be // read safely after calling the once. static void init_once() { int have_getrandom; uint8_t dummy; ssize_t getrandom_ret = boringssl_getrandom(&dummy, sizeof(dummy), GRND_NONBLOCK); if (getrandom_ret == 1) { have_getrandom = 1; } else if (getrandom_ret == -1 && errno == EAGAIN) { // We have getrandom, but the entropy pool has not been initialized yet. have_getrandom = 1; } else if (getrandom_ret == -1 && errno == ENOSYS) { // Fallthrough to using /dev/urandom, below. have_getrandom = 0; } else { // Other errors are fatal. perror("getrandom"); abort(); } if (have_getrandom) { urandom_fd = kHaveGetrandom; return; } // FIPS builds must support getrandom. #if defined(BORINGSSL_FIPS) perror("getrandom not found"); abort(); #endif int fd; do { fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC); } while (fd == -1 && errno == EINTR); if (fd < 0) { perror("failed to open /dev/urandom"); abort(); } urandom_fd = fd; } void bssl::CRYPTO_init_sysrand() { CRYPTO_once(&rand_once, init_once); } // CRYPTO_sysrand writes |len| bytes of entropy into |out|. void bssl::CRYPTO_sysrand(uint8_t *out, size_t len) { if (len == 0) { return; } CRYPTO_init_sysrand(); // Clear |errno| so it has defined value if |read| or |getrandom| // "successfully" returns zero. errno = 0; while (len > 0) { ssize_t r; if (urandom_fd == kHaveGetrandom) { r = boringssl_getrandom(out, len, 0); } else { do { r = read(urandom_fd, out, len); } while (r == -1 && errno == EINTR); } if (r <= 0) { perror("entropy fill failed"); abort(); } out += r; len -= r; } } #endif // OPENSSL_RAND_URANDOM