16ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org/*
26ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org * rand_source.c
36ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org *
46ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org * implements a random source based on /dev/random
56ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org *
66ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org * David A. McGrew
76ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org * Cisco Systems, Inc.
86ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org */
96ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org/*
106ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org *
116ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org * Copyright(c) 2001-2006 Cisco Systems, Inc.
126ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org * All rights reserved.
136ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org *
146ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org * Redistribution and use in source and binary forms, with or without
156ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org * modification, are permitted provided that the following conditions
166ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org * are met:
176ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org *
186ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org *   Redistributions of source code must retain the above copyright
196ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org *   notice, this list of conditions and the following disclaimer.
206ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org *
216ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org *   Redistributions in binary form must reproduce the above
226ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org *   copyright notice, this list of conditions and the following
236ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org *   disclaimer in the documentation and/or other materials provided
246ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org *   with the distribution.
256ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org *
266ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org *   Neither the name of the Cisco Systems, Inc. nor the names of its
276ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org *   contributors may be used to endorse or promote products derived
286ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org *   from this software without specific prior written permission.
296ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org *
306ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
316ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
326ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
336ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
346ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
356ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
366ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
376ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
386ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
396ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
406ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
416ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org * OF THE POSSIBILITY OF SUCH DAMAGE.
426ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org *
436ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org */
446ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
456ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org#include "config.h"
466ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
476ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org#ifdef DEV_URANDOM
486ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org# include <fcntl.h>          /* for open()  */
496ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org# include <unistd.h>         /* for close() */
506ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org#elif defined(HAVE_RAND_S)
516ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org# define _CRT_RAND_S
526ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org# include <stdlib.h>
536ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org#else
546ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org# include <stdio.h>
556ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org#endif
566ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
576ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org#include "rand_source.h"
586ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
596ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
606ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org/*
616ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org * global dev_rand_fdes is file descriptor for /dev/random
626ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org *
636ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org * This variable is also used to indicate that the random source has
646ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org * been initialized.  When this variable is set to the value of the
656ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org * #define RAND_SOURCE_NOT_READY, it indicates that the random source
666ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org * is not ready to be used.  The value of the #define
676ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org * RAND_SOURCE_READY is for use whenever that variable is used as an
686ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org * indicator of the state of the random source, but not as a file
696ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org * descriptor.
706ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org */
716ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
726ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org#define RAND_SOURCE_NOT_READY (-1)
736ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org#define RAND_SOURCE_READY     (17)
746ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
756ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.orgstatic int dev_random_fdes = RAND_SOURCE_NOT_READY;
766ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
776ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
786ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.orgerr_status_t
796ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.orgrand_source_init(void) {
806ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  if (dev_random_fdes >= 0) {
816ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    /* already open */
826ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    return err_status_ok;
836ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  }
846ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org#ifdef DEV_URANDOM
856ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  /* open random source for reading */
866ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  dev_random_fdes = open(DEV_URANDOM, O_RDONLY);
876ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  if (dev_random_fdes < 0)
886ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    return err_status_init_fail;
896ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org#elif defined(HAVE_RAND_S)
906ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  dev_random_fdes = RAND_SOURCE_READY;
916ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org#else
926ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  /* no random source available; let the user know */
936ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  fprintf(stderr, "WARNING: no real random source present!\n");
946ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  dev_random_fdes = RAND_SOURCE_READY;
956ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org#endif
966ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  return err_status_ok;
976ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org}
986ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
996ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.orgerr_status_t
1006ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.orgrand_source_get_octet_string(void *dest, uint32_t len) {
1016ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
1026ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  /*
1036ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org   * read len octets from /dev/random to dest, and
1046ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org   * check return value to make sure enough octets were
1056ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org   * written
1066ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org   */
1076ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org#ifdef DEV_URANDOM
1086ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  uint8_t *dst = (uint8_t *)dest;
1096ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  while (len)
1106ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  {
1116ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    ssize_t num_read = read(dev_random_fdes, dst, len);
1126ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    if (num_read <= 0 || num_read > len)
1136ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org      return err_status_fail;
1146ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    len -= num_read;
1156ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    dst += num_read;
1166ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  }
1176ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org#elif defined(HAVE_RAND_S)
1186ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  uint8_t *dst = (uint8_t *)dest;
1196ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  while (len)
1206ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  {
1216ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    unsigned int val;
1226ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    errno_t err = rand_s(&val);
1236ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
1246ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    if (err != 0)
1256ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org      return err_status_fail;
1266ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
1276ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    *dst++ = val & 0xff;
1286ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    len--;
1296ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  }
1306ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org#else
1316ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  /* Generic C-library (rand()) version */
1326ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  /* This is a random source of last resort */
1336ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  uint8_t *dst = (uint8_t *)dest;
1346ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  while (len)
1356ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  {
1366ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org	  int val = rand();
1376ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org	  /* rand() returns 0-32767 (ugh) */
1386ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org	  /* Is this a good enough way to get random bytes?
1396ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org	     It is if it passes FIPS-140... */
1406ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org	  *dst++ = val & 0xff;
1416ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org	  len--;
1426ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  }
1436ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org#endif
1446ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  return err_status_ok;
1456ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org}
1466ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
1476ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.orgerr_status_t
1486ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.orgrand_source_deinit(void) {
1496ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  if (dev_random_fdes < 0)
1506ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    return err_status_dealloc_fail;  /* well, we haven't really failed, *
1516ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org				      * but there is something wrong    */
1526ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org#ifdef DEV_URANDOM
1536ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  close(dev_random_fdes);
1546ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org#endif
1556ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  dev_random_fdes = RAND_SOURCE_NOT_READY;
1566ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
1576ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  return err_status_ok;
1586ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org}
159