16ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org/*
26ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org * prng.c
36ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org *
46ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org * pseudorandom source
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
466ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org#include "prng.h"
476ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
486ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org/* single, global prng structure */
496ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
506ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.orgx917_prng_t x917_prng;
516ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
526ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.orgerr_status_t
536ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.orgx917_prng_init(rand_source_func_t random_source) {
546ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  uint8_t tmp_key[16];
556ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  err_status_t status;
566ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
576ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  /* initialize output count to zero */
586ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  x917_prng.octet_count = 0;
596ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
606ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  /* set random source */
616ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  x917_prng.rand = random_source;
626ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
636ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  /* initialize secret key from random source */
646ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  status = random_source(tmp_key, 16);
656ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  if (status)
666ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    return status;
676ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
686ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  /* expand aes key */
696ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  aes_expand_encryption_key(tmp_key, 16, &x917_prng.key);
706ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
716ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  /* initialize prng state from random source */
726ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  status = x917_prng.rand((uint8_t *)&x917_prng.state, 16);
736ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  if (status)
746ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    return status;
756ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
766ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  return err_status_ok;
776ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org}
786ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
796ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.orgerr_status_t
806ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.orgx917_prng_get_octet_string(uint8_t *dest, uint32_t len) {
816ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  uint32_t t;
826ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  v128_t buffer;
836ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  uint32_t i, tail_len;
846ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  err_status_t status;
856ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
866ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  /*
876ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org   * if we need to re-initialize the prng, do so now
886ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org   *
896ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org   * avoid overflows by subtracting instead of adding
906ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org   */
916ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  if (x917_prng.octet_count > MAX_PRNG_OUT_LEN - len) {
926ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    status = x917_prng_init(x917_prng.rand);
936ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    if (status)
946ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org      return status;
956ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  }
966ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  x917_prng.octet_count += len;
976ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
986ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  /* find out the time */
996ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  t = (uint32_t)time(NULL);
1006ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
1016ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  /* loop until we have output enough data */
1026ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  for (i=0; i < len/16; i++) {
1036ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
1046ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    /* exor time into state */
1056ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    x917_prng.state.v32[0] ^= t;
1066ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
1076ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    /* copy state into buffer */
1086ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    v128_copy(&buffer, &x917_prng.state);
1096ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
1106ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    /* apply aes to buffer */
1116ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    aes_encrypt(&buffer, &x917_prng.key);
1126ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
1136ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    /* write data to output */
1146ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    *dest++ = buffer.v8[0];
1156ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    *dest++ = buffer.v8[1];
1166ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    *dest++ = buffer.v8[2];
1176ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    *dest++ = buffer.v8[3];
1186ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    *dest++ = buffer.v8[4];
1196ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    *dest++ = buffer.v8[5];
1206ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    *dest++ = buffer.v8[6];
1216ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    *dest++ = buffer.v8[7];
1226ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    *dest++ = buffer.v8[8];
1236ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    *dest++ = buffer.v8[9];
1246ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    *dest++ = buffer.v8[10];
1256ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    *dest++ = buffer.v8[11];
1266ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    *dest++ = buffer.v8[12];
1276ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    *dest++ = buffer.v8[13];
1286ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    *dest++ = buffer.v8[14];
1296ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    *dest++ = buffer.v8[15];
1306ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
1316ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    /* exor time into buffer */
1326ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    buffer.v32[0] ^= t;
1336ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
1346ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    /* encrypt buffer */
1356ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    aes_encrypt(&buffer, &x917_prng.key);
1366ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
1376ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    /* copy buffer into state */
1386ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    v128_copy(&x917_prng.state, &buffer);
1396ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
1406ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  }
1416ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
1426ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  /* if we need to output any more octets, we'll do so now */
1436ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  tail_len = len % 16;
1446ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  if (tail_len) {
1456ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
1466ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    /* exor time into state */
1476ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    x917_prng.state.v32[0] ^= t;
1486ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
1496ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    /* copy value into buffer */
1506ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    v128_copy(&buffer, &x917_prng.state);
1516ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
1526ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    /* apply aes to buffer */
1536ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    aes_encrypt(&buffer, &x917_prng.key);
1546ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
1556ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    /* write data to output */
1566ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    for (i=0; i < tail_len; i++) {
1576ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org      *dest++ = buffer.v8[i];
1586ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    }
1596ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
1606ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    /* now update the state one more time */
1616ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
1626ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    /* exor time into buffer */
1636ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    buffer.v32[0] ^= t;
1646ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
1656ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    /* encrypt buffer */
1666ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    aes_encrypt(&buffer, &x917_prng.key);
1676ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
1686ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    /* copy buffer into state */
1696ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org    v128_copy(&x917_prng.state, &buffer);
1706ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
1716ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  }
1726ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
1736ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  return err_status_ok;
1746ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org}
1756ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
1766ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.orgerr_status_t
1776ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.orgx917_prng_deinit(void) {
1786ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org
1796ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org  return err_status_ok;
1806ed0ee98e1c3d29a0ef79996f7d1abf174f39besergeyu@chromium.org}
181