// 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.

#include <assert.h>
#include <limits.h>
#include <string.h>

#if defined(BORINGSSL_FIPS)
#include <unistd.h>
#endif

#include <openssl/chacha.h>
#include <openssl/ctrdrbg.h>
#include <openssl/mem.h>
#include <openssl/rand.h>
#include <openssl/sha.h>

#include "../../bcm_support.h"
#include "../../mem_internal.h"
#include "../bcm_interface.h"
#include "../delocate.h"
#include "internal.h"

#if defined(BORINGSSL_FIPS)
#include "../entropy/internal.h"
#endif


using namespace bssl;

// # Entropy design
//
// Each thread gets its own, thread-local DRBG. These are `rand_thread_state`
// objects. In non-FIPS mode these are seeded from the operating system. It's
// assumed that the operating system always has an unfailing source of entropy
// which is accessed via `CRYPTO_sysrand`. (If the operating system entropy
// source fails, it's up to `CRYPTO_sysrand` to abort the process—we don't try
// to handle it.)
//
// If running in FIPS mode, a compliant entropy source must be used to seed the
// thread-local DRBGs instead. That is either the BoringCrypto jitter source or,
// on Android, a source provided by the OS which is accessed via a UNIX domain
// socket. When seeding from these sources, OS entropy is still used via the
// "additional data" input to the DRBG. But, from a compliance perspective,
// this input is irrelevent.
//
// Since the jitter source takes some time to run, there is a single jitter
// source per address space. It's used to seed a DRBG (`core_drbg`) which, in
// turn, seeds the thread-local DRBGs. Android also uses this core DRBG design
// in order to keep things more uniform across platforms.
//
// The core DRBG will be reseeded in accordance with NIST's reseeding
// requirements. However, since our DRBG configuration allows for 2**48
// generate calls before reseeding, it's unlikely that any process would
// observe a reseed of that DRBG.
//
// The thread-local DRBGs mean that we have to worry about the process fork()ing
// or the underlying VM getting cloned. Thus we try to mix entropy into each
// generation from the thread-local DRBGs. The hardware may provide a
// low-latency RNG. (Intel's rdrand instruction is the canonical example of
// this.) If available, that'll be used for this additional data. Otherwise
// we'll draw from the OS if it doesn't provide other fork-detection APIs.
//
// (We assume that the OS entropy is safe from fork()ing and VM duplication.
// This might be a bit of a leap of faith, esp on Windows, but there's nothing
// that we can do about it.)

#if defined(BORINGSSL_FIPS)
// This #if-block needs to be outside of the anonymous namespace:

struct core_drbg {
  CTR_DRBG_STATE drbg;
  // calls is the number of generate calls made on `drbg` since it was last
  // (re)seeded. This is bound by `kCoreReseedInterval`.
  uint64_t calls = 0;
};

DEFINE_BSS_GET(struct core_drbg *, g_core_drbg, = nullptr)
DEFINE_STATIC_MUTEX(g_core_drbg_lock)
#endif  // FIPS

namespace {

#if defined(BORINGSSL_FIPS)

// The number of times we'll generate from the core DRBG before reseeding.
// This is within the permitted bounds but so high that this effectively means
// that we'll never reseed it. See SP 800-90Ar1 table 3.
constexpr uint64_t kCoreReseedInterval = (UINT64_C(1) << 48) - 1;
constexpr size_t kCoreDRBGEntropySize = 48;

#if defined(OPENSSL_ANDROID)
// Android seeds from a system daemon which is responsible for getting
// entropy from a validated source.
void core_drbg_get_entropy(uint8_t entropy[kCoreDRBGEntropySize]) {
  uint8_t daemon_buffer[BSSL_ENTROPY_DAEMON_RESPONSE_LEN];
  size_t daemon_buffer_len = sizeof(daemon_buffer);
  if (!bssl_get_seed_from_daemon(daemon_buffer, &daemon_buffer_len)) {
    // If the daemon isn't running then fallback to system entropy.
    CRYPTO_sysrand(entropy, kCoreDRBGEntropySize);
    return;
  }
  SHA384(daemon_buffer, daemon_buffer_len, entropy);
}
#else
// Non-Android platforms use the jitter source.
void core_drbg_get_entropy(uint8_t entropy[kCoreDRBGEntropySize]) {
  // This runs the jitter source and takes some milliseconds.
  for (unsigned i = 0; i < 100; i++) {
    // The internal health checks of the jitter source may cause spurious
    // failures, therefore we retry a number of times because failure
    // to get entropy is fatal.
    if (entropy::GetSeed(entropy)) {
      return;
    }
  }
  fprintf(stderr, "Persistent jitter source failure.\n");
  BORINGSSL_FIPS_abort();
}
#endif

struct core_drbg *core_drbg_get_locked() {
  struct core_drbg **core = g_core_drbg_bss_get();
  if (*core != nullptr) {
    return *core;
  }

  uint8_t entropy[kCoreDRBGEntropySize];
  core_drbg_get_entropy(entropy);

  *core = New<core_drbg>();
  if (!*core) {
    BORINGSSL_FIPS_abort();
  }

  uint8_t nonce[CTR_DRBG_NONCE_LEN] = {0};
  static constexpr char kPersonalization[] = "BoringSSL";
  if (!CTR_DRBG_init(&(*core)->drbg, /*df=*/1, entropy, sizeof(entropy), nonce,
                     reinterpret_cast<const uint8_t *>(kPersonalization),
                     sizeof(kPersonalization))) {
    BORINGSSL_FIPS_abort();
  }

  return *core;
}

// Draw a seed for a thread-local DRBG from the core DRBG.
void core_drbg_draw_seed(uint8_t *out_seed, size_t len) {
  MutexWriteLock lock(g_core_drbg_lock_bss_get());

  struct core_drbg *const core = core_drbg_get_locked();
  if (core->calls > kCoreReseedInterval) {
    uint8_t reseed_entropy[kCoreDRBGEntropySize];
    core_drbg_get_entropy(reseed_entropy);
    if (!CTR_DRBG_reseed(&core->drbg, reseed_entropy,
                         /*additional_data=*/nullptr, 0)) {
      BORINGSSL_FIPS_abort();
    }
    core->calls = 0;
  }

  if (!CTR_DRBG_generate(&core->drbg, out_seed, len,
                         /*additional_data=*/nullptr, 0)) {
    BORINGSSL_FIPS_abort();
  }
  core->calls++;
}

#endif  // BORINGSSL_FIPS

// kReseedInterval is the number of generate calls made to a thread-local
// CTR-DRBG before reseeding.
static const unsigned kReseedInterval = 4096;

// rand_thread_state contains the per-thread state for the RNG.
struct rand_thread_state {
  CTR_DRBG_STATE drbg;
  uint64_t fork_generation;
  // calls is the number of generate calls made on `drbg` since it was last
  // (re)seeded. This is bound by `kReseedInterval`.
  unsigned calls;
  // fork_unsafe_buffering is non-zero iff, when `drbg` was last (re)seeded,
  // fork-unsafe buffering was enabled.
  int fork_unsafe_buffering;

#if defined(BORINGSSL_FIPS)
  // next and prev form a nullptr-terminated, double-linked list of all states
  // in a process.
  struct rand_thread_state *next, *prev;
  // clear_drbg_lock synchronizes between uses of `drbg` and
  // `rand_thread_state_clear_all` clearing it. This lock should be uncontended
  // in the common case, except on shutdown.
  Mutex clear_drbg_lock;
#endif
};

}  // namespace

#if defined(BORINGSSL_FIPS)
// thread_states_list is the head of a linked-list of all `rand_thread_state`
// objects in the process, one per thread. This is needed because FIPS requires
// the ability to zero them on demand (AS09.28). BoringSSL triggers this with a
// destructor function.
DEFINE_BSS_GET(struct rand_thread_state *, thread_states_list, = nullptr)
DEFINE_STATIC_MUTEX(thread_states_list_lock)

static void rand_thread_state_clear_all() __attribute__((destructor));
static void rand_thread_state_clear_all() {
  thread_states_list_lock_bss_get()->LockWrite();
  for (struct rand_thread_state *cur = *thread_states_list_bss_get();
       cur != nullptr; cur = cur->next) {
    cur->clear_drbg_lock.LockWrite();
    CTR_DRBG_clear(&cur->drbg);
  }

  g_core_drbg_lock_bss_get()->LockWrite();
  struct core_drbg **core = g_core_drbg_bss_get();
  if (*core) {
    CTR_DRBG_clear(&(*core)->drbg);
  }

  // The locks are deliberately left locked so that any threads that are still
  // running will hang if they try to call `BCM_rand_bytes`. It also ensures
  // `rand_thread_state_free` cannot free any thread state while we've taken the
  // lock.
}
#endif

// rand_thread_state_free frees a `rand_thread_state`. This is called when a
// thread exits.
static void rand_thread_state_free(void *state_in) {
  struct rand_thread_state *state =
      reinterpret_cast<rand_thread_state *>(state_in);

  if (state_in == nullptr) {
    return;
  }

#if defined(BORINGSSL_FIPS)
  thread_states_list_lock_bss_get()->LockWrite();

  if (state->prev != nullptr) {
    state->prev->next = state->next;
  } else if (*thread_states_list_bss_get() == state) {
    // `state->prev` may be nullptr either if it is the head of the list,
    // or if `state` is freed before it was added to the list at all.
    // Compare against the head of the list to distinguish these cases.
    *thread_states_list_bss_get() = state->next;
  }

  if (state->next != nullptr) {
    state->next->prev = state->prev;
  }

  thread_states_list_lock_bss_get()->UnlockWrite();

  CTR_DRBG_clear(&state->drbg);
#endif

  Delete(state);
}

#if defined(OPENSSL_X86_64) && !defined(OPENSSL_NO_ASM) && \
    !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
// rdrand should only be called if either `have_rdrand` or `have_fast_rdrand`
// returned true.
static int rdrand(uint8_t *buf, const size_t len) {
  const size_t len_multiple8 = len & ~7;
  if (!CRYPTO_rdrand_multiple8_buf(buf, len_multiple8)) {
    return 0;
  }
  const size_t remainder = len - len_multiple8;

  if (remainder != 0) {
    assert(remainder < 8);

    uint8_t rand_buf[8];
    if (!CRYPTO_rdrand(rand_buf)) {
      return 0;
    }
    OPENSSL_memcpy(buf + len_multiple8, rand_buf, remainder);
  }

  return 1;
}

#else

static int rdrand(uint8_t *buf, size_t len) { return 0; }

#endif

bcm_status bssl::BCM_rand_bytes_hwrng(uint8_t *buf, const size_t len) {
  if (!have_rdrand()) {
    return bcm_status::failure;
  }
  if (rdrand(buf, len)) {
    return bcm_status::not_approved;
  }
  return bcm_status::failure;
}

#if defined(BORINGSSL_FIPS)

// rand_get_seed fills `seed` with entropy. Since, in FIPS mode, this entropy
// comes from the jitter source / system daemon, `additional_input` will also be
// filled with system entropy.
static void rand_get_seed(struct rand_thread_state *state,
                          uint8_t seed[CTR_DRBG_ENTROPY_LEN],
                          uint8_t additional_input[CTR_DRBG_SEED_LEN],
                          size_t *out_additional_input_len) {
  core_drbg_draw_seed(seed, CTR_DRBG_ENTROPY_LEN);
  CRYPTO_sysrand(additional_input, CTR_DRBG_SEED_LEN);
  *out_additional_input_len = CTR_DRBG_SEED_LEN;
}

#else

// rand_get_seed fills `seed` with system entropy in a non-FIPS build.
static void rand_get_seed(struct rand_thread_state *state,
                          uint8_t seed[CTR_DRBG_ENTROPY_LEN],
                          uint8_t additional_input[CTR_DRBG_SEED_LEN],
                          size_t *out_additional_input_len) {
  CRYPTO_sysrand(seed, CTR_DRBG_ENTROPY_LEN);
  *out_additional_input_len = 0;
}

#endif

bcm_infallible bssl::BCM_rand_bytes_with_additional_data(
    uint8_t *out, size_t out_len, const uint8_t user_additional_data[32]) {
  if (out_len == 0) {
    return bcm_infallible::approved;
  }

  const uint64_t fork_generation = CRYPTO_get_fork_generation();
  const int fork_unsafe_buffering = rand_fork_unsafe_buffering_enabled();

  // Additional data is mixed into every CTR-DRBG call to protect, as best we
  // can, against forks & VM clones. We do not over-read this information and
  // don't reseed with it so, from the point of view of FIPS, this doesn't
  // provide “prediction resistance”. But, in practice, it does.
  uint8_t additional_data[32];
  // Intel chips have fast RDRAND instructions while, in other cases, RDRAND can
  // be _slower_ than a system call.
  if (!have_fast_rdrand() ||
      !rdrand(additional_data, sizeof(additional_data))) {
    // Without a hardware RNG to save us from address-space duplication, the OS
    // entropy is used. This can be expensive (one read per `RAND_bytes` call)
    // and so is disabled when we have fork detection, or if the application has
    // promised not to fork.
    if (fork_generation != 0 || fork_unsafe_buffering) {
      OPENSSL_memset(additional_data, 0, sizeof(additional_data));
    } else {
      CRYPTO_sysrand(additional_data, sizeof(additional_data));
    }
  }

  for (size_t i = 0; i < sizeof(additional_data); i++) {
    additional_data[i] ^= user_additional_data[i];
  }

  struct rand_thread_state stack_state;
  struct rand_thread_state *state = reinterpret_cast<rand_thread_state *>(
      CRYPTO_get_thread_local(OPENSSL_THREAD_LOCAL_RAND));

  if (state == nullptr) {
    state = New<rand_thread_state>();
    if (state == nullptr ||
        !CRYPTO_set_thread_local(OPENSSL_THREAD_LOCAL_RAND, state,
                                 rand_thread_state_free)) {
      // If the system is out of memory, use an ephemeral state on the
      // stack.
      Delete(state);
      state = &stack_state;
    }

    uint8_t seed[CTR_DRBG_ENTROPY_LEN];
    uint8_t personalization[CTR_DRBG_SEED_LEN] = {0};
    size_t personalization_len = 0;
    rand_get_seed(state, seed, personalization, &personalization_len);

    if (!CTR_DRBG_init(&state->drbg, /*df=*/true, seed,
                       sizeof(seed) - CTR_DRBG_NONCE_LEN,
                       seed + sizeof(seed) - CTR_DRBG_NONCE_LEN,
                       personalization, personalization_len)) {
      abort();
    }
    state->calls = 0;
    state->fork_generation = fork_generation;
    state->fork_unsafe_buffering = fork_unsafe_buffering;

#if defined(BORINGSSL_FIPS)
    if (state != &stack_state) {
      MutexWriteLock lock(thread_states_list_lock_bss_get());
      struct rand_thread_state **states_list = thread_states_list_bss_get();
      state->next = *states_list;
      if (state->next != nullptr) {
        state->next->prev = state;
      }
      state->prev = nullptr;
      *states_list = state;
    }
#endif
  }

  if (state->calls >= kReseedInterval ||
      // If we've forked since `state` was last seeded, reseed.
      state->fork_generation != fork_generation ||
      // If `state` was seeded from a state with different fork-safety
      // preferences, reseed. Suppose `state` was fork-safe, then forked into
      // two children, but each of the children never fork and disable fork
      // safety. The children must reseed to avoid working from the same PRNG
      // state.
      state->fork_unsafe_buffering != fork_unsafe_buffering) {
    uint8_t seed[CTR_DRBG_ENTROPY_LEN];
    uint8_t reseed_additional_data[CTR_DRBG_SEED_LEN] = {0};
    size_t reseed_additional_data_len = 0;
    rand_get_seed(state, seed, reseed_additional_data,
                  &reseed_additional_data_len);
#if defined(BORINGSSL_FIPS)
    // Take a read lock around accesses to `state->drbg`. This is needed to
    // avoid returning bad entropy if we race with
    // `rand_thread_state_clear_all`.
    state->clear_drbg_lock.LockRead();
#endif
    if (!CTR_DRBG_reseed_ex(&state->drbg, seed, sizeof(seed),
                            reseed_additional_data,
                            reseed_additional_data_len)) {
      abort();
    }
    state->calls = 0;
    state->fork_generation = fork_generation;
    state->fork_unsafe_buffering = fork_unsafe_buffering;
  } else {
#if defined(BORINGSSL_FIPS)
    state->clear_drbg_lock.LockRead();
#endif
  }

  int first_call = 1;
  while (out_len > 0) {
    size_t todo = out_len;
    if (todo > CTR_DRBG_MAX_GENERATE_LENGTH) {
      todo = CTR_DRBG_MAX_GENERATE_LENGTH;
    }

    if (!CTR_DRBG_generate(&state->drbg, out, todo, additional_data,
                           first_call ? sizeof(additional_data) : 0)) {
      abort();
    }

    out += todo;
    out_len -= todo;
    // Though we only check before entering the loop, this cannot add enough to
    // overflow a `size_t`.
    state->calls++;
    first_call = 0;
  }

  if (state == &stack_state) {
    CTR_DRBG_clear(&state->drbg);
  }

#if defined(BORINGSSL_FIPS)
  state->clear_drbg_lock.UnlockRead();
#endif
  return bcm_infallible::approved;
}

bcm_infallible bssl::BCM_rand_bytes(uint8_t *out, size_t out_len) {
  static const uint8_t kZeroAdditionalData[32] = {0};
  BCM_rand_bytes_with_additional_data(out, out_len, kZeroAdditionalData);
  return bcm_infallible::approved;
}
