/* Copyright 2020 The BoringSSL Authors * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "../bcm_support.h" #include "../fipsmodule/bcm_interface.h" #include "../internal.h" #if defined(BORINGSSL_FIPS) #include // passive_get_seed_entropy writes |out_entropy_len| bytes of entropy, suitable // for seeding a DRBG, to |out_entropy|. It sets |*out_used_cpu| to one if the // entropy came directly from the CPU and zero if it came from the OS. It // actively obtains entropy from the CPU/OS static void passive_get_seed_entropy(uint8_t *out_entropy, size_t out_entropy_len, int *out_want_additional_input) { *out_want_additional_input = 0; if (bcm_success(BCM_rand_bytes_hwrng(out_entropy, out_entropy_len))) { *out_want_additional_input = 1; } else { CRYPTO_sysrand_for_seed(out_entropy, out_entropy_len); } } #define ENTROPY_READ_LEN \ (/* last_block size */ 16 + CTR_DRBG_ENTROPY_LEN * BORINGSSL_FIPS_OVERREAD) #if defined(OPENSSL_ANDROID) #include #include #include #include #include // socket_history_t enumerates whether the entropy daemon should be contacted // for a given entropy request. Values other than socket_not_yet_attempted are // sticky so if the first attempt to read from the daemon fails it's assumed // that the daemon is not present and no more attempts will be made. If the // first attempt is successful then attempts will be made forever more. enum class socket_history_t { // initial value, no connections to the entropy daemon have been made yet. socket_not_yet_attempted = 0, // reading from the entropy daemon was successful socket_success, // reading from the entropy daemon failed. socket_failed, }; static std::atomic g_socket_history{ socket_history_t::socket_not_yet_attempted}; // DAEMON_RESPONSE_LEN is the number of bytes that the entropy daemon replies // with. #define DAEMON_RESPONSE_LEN 496 static_assert(ENTROPY_READ_LEN == DAEMON_RESPONSE_LEN, "entropy daemon response length mismatch"); static int get_seed_from_daemon(uint8_t *out_entropy, size_t out_entropy_len) { // |RAND_need_entropy| should never call this function for more than // |DAEMON_RESPONSE_LEN| bytes. if (out_entropy_len > DAEMON_RESPONSE_LEN) { abort(); } const socket_history_t socket_history = g_socket_history.load(std::memory_order_acquire); if (socket_history == socket_history_t::socket_failed) { return 0; } int ret = 0; static const char kSocketPath[] = "/dev/socket/prng_seeder"; struct sockaddr_un sun; uint8_t buffer[DAEMON_RESPONSE_LEN]; size_t done = 0; const int sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock < 0) { goto out; } memset(&sun, 0, sizeof(sun)); sun.sun_family = AF_UNIX; static_assert(sizeof(kSocketPath) <= UNIX_PATH_MAX, "kSocketPath too long"); OPENSSL_memcpy(sun.sun_path, kSocketPath, sizeof(kSocketPath)); if (connect(sock, (struct sockaddr *)&sun, sizeof(sun))) { goto out; } while (done < sizeof(buffer)) { ssize_t n; do { n = read(sock, buffer + done, sizeof(buffer) - done); } while (n == -1 && errno == EINTR); if (n < 1) { goto out; } done += n; } if (done != DAEMON_RESPONSE_LEN) { // The daemon should always write |DAEMON_RESPONSE_LEN| bytes on every // connection. goto out; } assert(out_entropy_len <= DAEMON_RESPONSE_LEN); OPENSSL_memcpy(out_entropy, buffer, out_entropy_len); ret = 1; out: if (socket_history == socket_history_t::socket_not_yet_attempted) { socket_history_t expected = socket_history_t::socket_not_yet_attempted; // If another thread has already updated |g_socket_history| then we defer // to their value. g_socket_history.compare_exchange_strong( expected, (ret == 0) ? socket_history_t::socket_failed : socket_history_t::socket_success, std::memory_order_release, std::memory_order_relaxed); } close(sock); return ret; } #else static int get_seed_from_daemon(uint8_t *out_entropy, size_t out_entropy_len) { return 0; } #endif // OPENSSL_ANDROID // RAND_need_entropy is called by the FIPS module when it has blocked because of // a lack of entropy. This signal is used as an indication to feed it more. void RAND_need_entropy(size_t bytes_needed) { uint8_t buf[ENTROPY_READ_LEN]; size_t todo = sizeof(buf); if (todo > bytes_needed) { todo = bytes_needed; } int want_additional_input; if (get_seed_from_daemon(buf, todo)) { want_additional_input = 1; } else { passive_get_seed_entropy(buf, todo, &want_additional_input); } if (boringssl_fips_break_test("CRNG")) { // This breaks the "continuous random number generator test" defined in FIPS // 140-2, section 4.9.2, and implemented in |rand_get_seed|. OPENSSL_memset(buf, 0, todo); } BCM_rand_load_entropy(buf, todo, want_additional_input); } #endif // FIPS