--- /dev/null
+/*
+ * Copyright (c) 2013 Apple Inc. All rights reserved.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
+ *
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
+ */
+
+#include <mach/machine.h>
+#include <mach/processor.h>
+#include <kern/processor.h>
+#include <kern/cpu_data.h>
+#include <kern/cpu_number.h>
+#include <kern/kalloc.h>
+#include <kern/machine.h>
+#include <kern/misc_protos.h>
+#include <kern/startup.h>
+#include <kern/sched.h>
+#include <kern/thread.h>
+#include <kern/thread_call.h>
+#include <machine/cpu_data.h>
+#include <machine/simple_lock.h>
+#include <vm/pmap.h>
+#include <vm/vm_page.h>
+#include <sys/kdebug.h>
+#include <sys/random.h>
+
+#include <prng/random.h>
+#include <corecrypto/ccdrbg.h>
+#include <corecrypto/ccsha1.h>
+
+#include <pexpert/pexpert.h>
+#include <console/serial_protos.h>
+#include <IOKit/IOPlatformExpert.h>
+
+static lck_grp_t *gPRNGGrp;
+static lck_attr_t *gPRNGAttr;
+static lck_grp_attr_t *gPRNGGrpAttr;
+static lck_mtx_t *gPRNGMutex = NULL;
+
+typedef struct prngContext {
+ struct ccdrbg_info *infop;
+ struct ccdrbg_state *statep;
+ uint64_t bytes_generated;
+ uint64_t bytes_reseeded;
+} *prngContextp;
+
+ccdrbg_factory_t prng_ccdrbg_factory = NULL;
+
+entropy_data_t EntropyData = { .index_ptr = EntropyData.buffer };
+
+boolean_t erandom_seed_set = FALSE;
+char erandom_seed[EARLY_RANDOM_SEED_SIZE];
+typedef struct ccdrbg_state ccdrbg_state_t;
+uint8_t master_erandom_state[EARLY_RANDOM_STATE_STATIC_SIZE];
+ccdrbg_state_t *erandom_state[MAX_CPUS];
+struct ccdrbg_info erandom_info;
+decl_simple_lock_data(,entropy_lock);
+
+struct ccdrbg_nisthmac_custom erandom_custom = {
+ .di = &ccsha1_eay_di,
+ .strictFIPS = 0,
+};
+
+static void read_erandom(void *buffer, u_int numBytes); /* Forward */
+
+void
+entropy_buffer_read(char *buffer,
+ unsigned int *count)
+{
+ boolean_t current_state;
+ unsigned int i, j;
+
+ if (!erandom_seed_set) {
+ panic("early_random was never invoked");
+ }
+
+ if ((*count) > (ENTROPY_BUFFER_SIZE * sizeof(unsigned int)))
+ *count = ENTROPY_BUFFER_SIZE * sizeof(unsigned int);
+
+ current_state = ml_set_interrupts_enabled(FALSE);
+#if defined (__x86_64__)
+ simple_lock(&entropy_lock);
+#endif
+
+ memcpy((char *) buffer, (char *) EntropyData.buffer, *count);
+
+ for (i = 0, j = (ENTROPY_BUFFER_SIZE - 1); i < ENTROPY_BUFFER_SIZE; j = i, i++)
+ EntropyData.buffer[i] = EntropyData.buffer[i] ^ EntropyData.buffer[j];
+
+#if defined (__x86_64__)
+ simple_unlock(&entropy_lock);
+#endif
+ (void) ml_set_interrupts_enabled(current_state);
+
+#if DEVELOPMENT || DEBUG
+ uint32_t *word = (uint32_t *) (void *) buffer;
+ /* Good for both 32-bit and 64-bit kernels. */
+ for (i = 0; i < ENTROPY_BUFFER_SIZE; i += 4)
+ /*
+ * We use "EARLY" here so that we can grab early entropy on
+ * ARM, where tracing is not started until after PRNG is
+ * initialized.
+ */
+ KERNEL_DEBUG_EARLY(ENTROPY_READ(i/4),
+ word[i+0], word[i+1], word[i+2], word[i+3]);
+#endif
+}
+
+/*
+ * Return a uniformly distributed 64-bit random number.
+ *
+ * This interface should have minimal dependencies on kernel
+ * services, and thus be available very early in the life
+ * of the kernel.
+ * This provides cryptographically secure randomness.
+ * Each processor has its own generator instance.
+ * It is seeded (lazily) with entropy provided by the Booter.
+*
+ * For <rdar://problem/17292592> the algorithm switched from LCG to
+ * NIST HMAC DBRG as follows:
+ * - When first called (on OSX this is very early while page tables are being
+ * built) early_random() calls ccdrbg_factory_hmac() to set-up a ccdbrg info
+ * structure.
+ * - The boot processor's ccdrbg state structure is a statically allocated area
+ * which is then initialized by calling the ccdbrg_init method.
+ * The initial entropy is 16 bytes of boot entropy.
+ * The nonce is the first 8 bytes of entropy xor'ed with a timestamp
+ * from ml_get_timebase().
+ * The personalization data provided is null.
+ * - The first 64-bit random value is returned on the boot processor from
+ * an invocation of the ccdbrg_generate method.
+ * - Non-boot processor's DRBG state structures are allocated dynamically
+ * from prng_init(). Each is initialized with the same 16 bytes of entropy
+ * but with a different timestamped nonce and cpu number as personalization.
+ * - Subsequent calls to early_random() pass to read_erandom() to generate
+ * an 8-byte random value. read_erandom() ensures that pre-emption is
+ * disabled and selects the DBRG state from the current processor.
+ * The ccdbrg_generate method is called for the required random output.
+ * If this method returns CCDRBG_STATUS_NEED_RESEED, the erandom_seed buffer
+ * is re-filled with kernel-harvested entropy and the ccdbrg_reseed method is
+ * called with this new entropy. The kernel panics if a reseed fails.
+ */
+uint64_t
+early_random(void)
+{
+ uint32_t cnt = 0;
+ uint64_t result;
+ uint64_t nonce;
+ int rc;
+ ccdrbg_state_t *state;
+
+ if (!erandom_seed_set) {
+ simple_lock_init(&entropy_lock,0);
+ erandom_seed_set = TRUE;
+ cnt = PE_get_random_seed((unsigned char *) EntropyData.buffer,
+ sizeof(EntropyData.buffer));
+
+ if (cnt < sizeof(EntropyData.buffer)) {
+ /*
+ * Insufficient entropy is fatal. We must fill the
+ * entire entropy buffer during initializaton.
+ */
+ panic("EntropyData needed %lu bytes, but got %u.\n",
+ sizeof(EntropyData.buffer), cnt);
+ }
+
+ /*
+ * Use some of the supplied entropy as a basis for early_random;
+ * reuse is ugly, but simplifies things. Ideally, we would guard
+ * early random values well enough that it isn't safe to attack
+ * them, but this cannot be guaranteed; thus, initial entropy
+ * can be considered 8 bytes weaker for a given boot if any
+ * early random values are conclusively determined.
+ *
+ * early_random_seed could be larger than EntopyData.buffer...
+ * but it won't be.
+ */
+ bcopy(EntropyData.buffer, &erandom_seed, sizeof(erandom_seed));
+
+ /* Init DRBG for NIST HMAC */
+ ccdrbg_factory_nisthmac(&erandom_info, &erandom_custom);
+ assert(erandom_info.size <= sizeof(master_erandom_state));
+ state = (ccdrbg_state_t *) master_erandom_state;
+ erandom_state[0] = state;
+
+ /*
+ * Init our DBRG from the boot entropy and a nonce composed of
+ * a timestamp swizzled with the first 8 bytes of this entropy.
+ */
+ assert(sizeof(erandom_seed) > sizeof(nonce));
+ bcopy(erandom_seed, &nonce, sizeof(nonce));
+ nonce ^= ml_get_timebase();
+ rc = ccdrbg_init(&erandom_info, state,
+ sizeof(erandom_seed), erandom_seed,
+ sizeof(nonce), &nonce,
+ 0, NULL);
+ assert(rc == CCDRBG_STATUS_OK);
+
+ /* Generate output */
+ rc = ccdrbg_generate(&erandom_info, state,
+ sizeof(result), &result,
+ 0, NULL);
+ assert(rc == CCDRBG_STATUS_OK);
+
+ return result;
+ };
+
+ read_erandom(&result, sizeof(result));
+
+ return result;
+}
+
+void
+read_erandom(void *buffer, u_int numBytes)
+{
+ int cpu;
+ int rc;
+ uint32_t cnt;
+ ccdrbg_state_t *state;
+
+ mp_disable_preemption();
+ cpu = cpu_number();
+ state = erandom_state[cpu];
+ assert(state);
+ while (TRUE) {
+ /* Generate output */
+ rc = ccdrbg_generate(&erandom_info, state,
+ numBytes, buffer,
+ 0, NULL);
+ if (rc == CCDRBG_STATUS_OK)
+ break;
+ if (rc == CCDRBG_STATUS_NEED_RESEED) {
+ /* It's time to reseed. Get more entropy */
+ cnt = sizeof(erandom_seed);
+ entropy_buffer_read(erandom_seed, &cnt);
+ assert(cnt == sizeof(erandom_seed));
+ rc = ccdrbg_reseed(&erandom_info, state,
+ sizeof(erandom_seed), erandom_seed,
+ 0, NULL);
+ if (rc == CCDRBG_STATUS_OK)
+ continue;
+ panic("read_erandom reseed error %d\n", rc);
+ }
+ panic("read_erandom ccdrbg error %d\n", rc);
+ }
+ mp_enable_preemption();
+}
+
+void
+read_frandom(void *buffer, u_int numBytes)
+{
+ char *cp = (char *) buffer;
+ int nbytes;
+
+ /*
+ * Split up into requests for blocks smaller than
+ * than the DBRG request limit. iThis limit is private but
+ * for NISTHMAC it's known to be greater then 4096.
+ */
+ while (numBytes) {
+ nbytes = MIN(numBytes, PAGE_SIZE);
+ read_erandom(cp, nbytes);
+ cp += nbytes;
+ numBytes -= nbytes;
+ }
+}
+
+/*
+ * Register a DRBG factory routine to e used in constructing the kernel PRNG.
+ * XXX to be called from the corecrypto kext.
+ */
+void
+prng_factory_register(ccdrbg_factory_t factory)
+{
+ prng_ccdrbg_factory = factory;
+ thread_wakeup((event_t) &prng_ccdrbg_factory);
+}
+
+void
+prng_cpu_init(int cpu)
+{
+ uint64_t nonce;
+ int rc;
+ ccdrbg_state_t *state;
+ prngContextp pp;
+
+ /*
+ * Allocate state and initialize DBRG state for early_random()
+ * for this processor, if necessary.
+ */
+ if (erandom_state[cpu] == NULL) {
+
+ state = kalloc(erandom_info.size);
+ if (state == NULL) {
+ panic("prng_init kalloc failed\n");
+ }
+ erandom_state[cpu] = state;
+
+ /*
+ * Init our DBRG from boot entropy, nonce as timestamp xor'ed
+ * with the first 8 bytes of entropy, and use the cpu number
+ * as the personalization parameter.
+ */
+ bcopy(erandom_seed, &nonce, sizeof(nonce));
+ nonce ^= ml_get_timebase();
+ rc = ccdrbg_init(&erandom_info, state,
+ sizeof(erandom_seed), erandom_seed,
+ sizeof(nonce), &nonce,
+ sizeof(cpu), &cpu);
+ assert(rc == CCDRBG_STATUS_OK);
+ }
+
+ /* Non-boot cpus use the master cpu's global context */
+ if (cpu != master_cpu) {
+ cpu_datap(cpu)->cpu_prng = master_prng_context();
+ return;
+ }
+
+ assert(gPRNGMutex == NULL); /* Once only, please */
+
+ /* make a mutex to control access */
+ gPRNGGrpAttr = lck_grp_attr_alloc_init();
+ gPRNGGrp = lck_grp_alloc_init("random", gPRNGGrpAttr);
+ gPRNGAttr = lck_attr_alloc_init();
+ gPRNGMutex = lck_mtx_alloc_init(gPRNGGrp, gPRNGAttr);
+
+ pp = kalloc(sizeof(*pp));
+ if (pp == NULL)
+ panic("Unable to allocate prng context");
+ pp->bytes_generated = 0;
+ pp->bytes_reseeded = 0;
+ pp->infop = NULL;
+
+ /* XXX Temporary registration */
+ prng_factory_register(ccdrbg_factory_yarrow);
+
+ master_prng_context() = pp;
+}
+
+static ccdrbg_info_t *
+prng_infop(prngContextp pp)
+{
+ lck_mtx_assert(gPRNGMutex, LCK_MTX_ASSERT_OWNED);
+
+ /* Usual case: the info is all set */
+ if (pp->infop)
+ return pp->infop;
+
+ /*
+ * Possibly wait for the CCDRBG factory routune to be registered
+ * by corecypto. But panic after waiting for more than 10 seconds.
+ */
+ while (prng_ccdrbg_factory == NULL ) {
+ wait_result_t wait_result;
+ assert_wait_timeout((event_t) &prng_ccdrbg_factory, TRUE,
+ 10, NSEC_PER_USEC);
+ lck_mtx_unlock(gPRNGMutex);
+ wait_result = thread_block(THREAD_CONTINUE_NULL);
+ if (wait_result == THREAD_TIMED_OUT)
+ panic("prng_ccdrbg_factory registration timeout");
+ lck_mtx_lock(gPRNGMutex);
+ }
+ /* Check we didn't lose the set-up race */
+ if (pp->infop)
+ return pp->infop;
+
+ pp->infop = (ccdrbg_info_t *) kalloc(sizeof(ccdrbg_info_t));
+ if (pp->infop == NULL)
+ panic("Unable to allocate prng info");
+
+ prng_ccdrbg_factory(pp->infop, NULL);
+
+ pp->statep = kalloc(pp->infop->size);
+ if (pp->statep == NULL)
+ panic("Unable to allocate prng state");
+
+ char rdBuffer[ENTROPY_BUFFER_BYTE_SIZE];
+ unsigned int bytesToInput = sizeof(rdBuffer);
+
+ entropy_buffer_read(rdBuffer, &bytesToInput);
+
+ (void) ccdrbg_init(pp->infop, pp->statep,
+ bytesToInput, rdBuffer,
+ 0, NULL,
+ 0, NULL);
+ return pp->infop;
+}
+
+static void
+Reseed(prngContextp pp)
+{
+ char rdBuffer[ENTROPY_BUFFER_BYTE_SIZE];
+ unsigned int bytesToInput = sizeof(rdBuffer);
+
+ entropy_buffer_read(rdBuffer, &bytesToInput);
+
+ PRNG_CCDRBG((void) ccdrbg_reseed(pp->infop, pp->statep,
+ bytesToInput, rdBuffer,
+ 0, NULL));
+
+ pp->bytes_reseeded = pp->bytes_generated;
+}
+
+
+/* export good random numbers to the rest of the kernel */
+void
+read_random(void* buffer, u_int numbytes)
+{
+ prngContextp pp;
+ ccdrbg_info_t *infop;
+ int ccdrbg_err;
+
+ lck_mtx_lock(gPRNGMutex);
+
+ pp = current_prng_context();
+ infop = prng_infop(pp);
+
+ /*
+ * Call DRBG, reseeding and retrying if requested.
+ */
+ while (TRUE) {
+ PRNG_CCDRBG(
+ ccdrbg_err = ccdrbg_generate(infop, pp->statep,
+ numbytes, buffer,
+ 0, NULL));
+ if (ccdrbg_err == CCDRBG_STATUS_OK)
+ break;
+ if (ccdrbg_err == CCDRBG_STATUS_NEED_RESEED) {
+ Reseed(pp);
+ continue;
+ }
+ panic("read_random ccdrbg error %d\n", ccdrbg_err);
+ }
+
+ pp->bytes_generated += numbytes;
+ lck_mtx_unlock(gPRNGMutex);
+}
+
+int
+write_random(void* buffer, u_int numbytes)
+{
+#if 0
+ int retval = 0;
+ prngContextp pp;
+
+ lck_mtx_lock(gPRNGMutex);
+
+ pp = current_prng_context();
+
+ if (ccdrbg_reseed(prng_infop(pp), pp->statep,
+ bytesToInput, rdBuffer, 0, NULL) != 0)
+ retval = EIO;
+
+ lck_mtx_unlock(gPRNGMutex);
+ return retval;
+#else
+#pragma unused(buffer, numbytes)
+ return 0;
+#endif
+}