On Thu, Jul 02, 2015 at 08:21:31PM +0100, Mark R V Murray wrote:
> > I.e., if the box is configured to boot in FIPS mode, it should use NIST
> > SP800-90 HMAC-DRBG adaptor. Otherwise, it uses the default FreeBSD
> > adaptor (Fortuna I guess).
> 
> No problem!
> 
> Could you please let me know your implementation???s API? If I have that,
> or at least an approximation, I can make a framework in which you can
> insert your code.

Sure, Here is the shim between HMAC_DRBG and struct random_adaptor (that
used to plug-in before removal on 2015/6/30).

/*
 * $Id: $
 *
 * Copyright (c) 2014, Juniper Networks, Inc.
 * All rights reserved.
 */

#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/fcntl.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/mutex.h>
#include <sys/poll.h>
#include <sys/random.h>
#include <sys/sysctl.h>
#include <sys/selinfo.h>

#include <opencrypto/cryptodev.h>
#include <opencrypto/xform.h>
#include <dev/random/randomdev.h>
#include <dev/random/randomdev_soft.h>
#include <dev/random/random_harvestq.h>
#include <dev/random/random_adaptors.h>

#include "hmac.h"
#include "hmac_shim.h"
#include "hmac_drbg.h"
#include "hmac_drbg_adaptor.h"
#include "health_tests.h"
#include "debug.h"

/*
 * The context mutex protects the consistency of the hmac_drbg state
 */
struct mtx drbg_mtx;

static int hmac_drbg_security_level = 256;

/*
 * hmac_drbg context
 */
static struct random_hmac_drbg_s drbg_ctx;

static void random_hmac_drbg_init(void);
static void random_hmac_drbg_deinit(void);
static int random_hmac_drbg_block(int);
static int random_hmac_drbg_read(void *, int);
static void random_hmac_drbg_write(void *, int);
static int random_hmac_drbg_poll(int, struct thread *);
static void random_hmac_drbg_reseed(void);
void random_hmac_drbg_unblock(void);

struct random_adaptor random_hmac_drbg = {
        .ident = "Software, HMAC DRBG, NIST 800-90A",
        .init = random_hmac_drbg_init,
        .deinit = random_hmac_drbg_deinit,
        .block = random_hmac_drbg_block,
        .read = random_hmac_drbg_read,
        .poll = random_hmac_drbg_poll,
        .reseed = random_hmac_drbg_reseed,
        .seeded = 0,
};

/* entropy bit counter */
uint64_t random_hmac_drbg_ecount;

void
random_hmac_drbg_unblock(void)
{

        if (!random_hmac_drbg.seeded) {
                random_hmac_drbg.seeded = 1;
                selwakeuppri(&random_hmac_drbg.rsel, PUSER);
                wakeup(&random_hmac_drbg);
        }
}

static void
hmac_drbg_process_event(struct harvest *event)
{

        /* If entropy health test fails, discard the entropy */
        if (entropy_health_test(event) != 0) {
                return;
        }

        /*
         * Feed noise in to our DRBG.
         * Performance optimization: even though not all fields in event are
         * entropic, it's much faster to call random_hmac_drbg_write() on the
         * whole struct, vs calling random_hmac_drbg_write() separately for
         * event->somecounter and event->entropy.
         */
        random_hmac_drbg_write(event, sizeof(*event));

        random_hmac_drbg_ecount += event->bits;

        if (random_hmac_drbg_ecount >= hmac_drbg_security_level)
                random_hmac_drbg_unblock(); /* Unblock random(4) */
}

void
random_hmac_drbg_init(void)
{
        int error;

        mtx_init(&drbg_mtx, "hmac_drbg context mutex", NULL, MTX_DEF);
        error = hmac_drbg_init(&drbg_ctx, NULL);
        KASSERT(error == 0, ("hmac_drbg_init() failure: %d\n", error));

        random_harvestq_init(hmac_drbg_process_event);

        /* Register the randomness harvesting routine */
        randomdev_init_harvester(random_harvestq_internal,
            random_hmac_drbg_read);
}

static void
random_hmac_drbg_deinit(void)
{

        mtx_destroy(&drbg_mtx);
}

static int
random_hmac_drbg_block(int flag)
{
        int error = 0;

        mtx_lock(&drbg_mtx);

        while (!random_hmac_drbg.seeded && !error) {
                if (flag & O_NONBLOCK)
                        error = EWOULDBLOCK;
                else {
                        printf("Entropy device is blocking.\n");
                        error = msleep(&random_hmac_drbg,
                            &drbg_mtx, PUSER | PCATCH, "block", 0);
                }
        }

        mtx_unlock(&drbg_mtx);

        return (error);
}

static int
random_hmac_drbg_read(void *buf, int count)
{
        int error;

        mtx_lock(&drbg_mtx);

        error = hmac_drbg_get_bytes(&drbg_ctx, buf, count);
        KASSERT(error != -1, ("hmac_drbg_get_bytes() failure: %d\n",
            error));

        mtx_unlock(&drbg_mtx);

        return(error != -1 ? count : 0);
}

static void
random_hmac_drbg_write(void *buf, int count)
{
        int error;

        mtx_lock(&drbg_mtx);

        error = hmac_drbg_update(&drbg_ctx, buf, count);
        KASSERT(error == 0, ("hmac_drbg_update() failure: %d\n", error));

        mtx_unlock(&drbg_mtx);
}

static int
random_hmac_drbg_poll(int events, struct thread *td)
{
        int revents = 0;
        mtx_lock(&drbg_mtx);

        if (random_hmac_drbg.seeded)
                revents = events & (POLLIN | POLLRDNORM);
        else
                selrecord(td, &random_hmac_drbg.rsel);

        mtx_unlock(&drbg_mtx);

        return (revents);
}

static void
random_hmac_drbg_reseed(void)
{

        /* Command a entropy queue flush and wait for it to finish */
        random_kthread_control = 1;
        while (random_kthread_control)
                pause("-", hz / 10);

}

static int
random_hmac_drbg_modevent(module_t mod, int type, void *unused)
{
        switch(type) {
        case MOD_LOAD:
                /* Initialize health tests */
                health_tests_init();

                random_adaptor_register("hmac-drbg", &random_hmac_drbg);
                /*
                 * For statically built kernels that contain both device
                 * random and options PADLOCK_RNG/RDRAND_RNG/etc..,
                 * this event handler will do nothing, since the random
                 * driver-specific handlers are loaded after these HW
                 * consumers, and hence hasn't yet registered for this event.
                 *
                 * In case where both the random driver and RNG's are built
                 * as seperate modules, random.ko is loaded prior to *_rng.ko's
                 * (by dependency). This event handler is there to delay
                 * creation of /dev/{u,}random and attachment of this *_rng.ko.
                 */
                EVENTHANDLER_INVOKE(random_adaptor_attach, &random_hmac_drbg);

                return (0);
        }

        return (EINVAL);
}

RANDOM_ADAPTOR_MODULE(random_hmac_drbg, random_hmac_drbg_modevent, 1);

-- 
Arthur Mesh <am...@juniper.net>
Juniper Networks
+1 408 936-4968

Attachment: pgp_tB5_CRwr2.pgp
Description: PGP signature

Reply via email to