aboutsummaryrefslogblamecommitdiff
path: root/src/entropy/main.c
blob: f7663f52c91879a5f4aecfab7f46a8089a397bea (plain) (tree)











































                                                                        
                          




































                                                                                             
                                             






                                      
                                                                                













                                                                     

                  










































                                                                

                                                                                                  









                                                                                  





                                                          




                                                           



                                       






















































































































































































                                                                                             

                                                              




                                                                     













                                                                            













                                                                 
/*
 * Copyright (c) 2014, 2015 NORDUnet A/S
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following
 * conditions are met:
 *
 *    1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *    2. Redistributions in binary form must reproduce the above
 *       copyright notice, this list of conditions and the following
 *       disclaimer in the documentation and/or other materials provided
 *       with the distribution.
 *    3. Neither the name of the NORDUnet nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
#include <string.h>

#include "main.h"
#include "stm_init.h"

#define MODE_DELTAS			1
#define MODE_ENTROPY			2

#define UART_RANDOM_BYTES_PER_CHUNK	8
#define UART_DELTA_WORDS_PER_CHUNK	32

extern DMA_HandleTypeDef hdma_tim;

UART_HandleTypeDef *huart;
__IO ITStatus UartReady = RESET;

static union {
  uint8_t rnd[257];		/* 256 bytes + 1 for use in the POST */
  uint32_t rnd32[64];
} buf;

/* First DMA value (DMA_counters[0]) is unreliable, leftover in DMA FIFO perhaps? */
#define FIRST_DMA_IDX_USED  3

/*
 * Number of counters used to produce 8 bits of entropy is:
 *   8 * 4 - four flanks are used to produce two (hopefully) uncorrelated bits (a and b)
 *     * 2 - von Neumann will on average discard 1/2 of the bits 'a' and 'b'
 */
#define DMA_COUNTERS_NUM ((UART_RANDOM_BYTES_PER_CHUNK * 8 * 4 * 2) + FIRST_DMA_IDX_USED + 1)
struct DMA_params {
  volatile uint32_t buf0[DMA_COUNTERS_NUM];
  volatile uint32_t buf1[DMA_COUNTERS_NUM];
  volatile uint32_t write_buf;
};

static struct DMA_params DMA = {
  {},
  {},
  0,
};


/* The main work horse functions */
void get_entropy32(uint32_t num_bytes, uint32_t buf_idx);
void perform_delta32(uint32_t count, const uint32_t start);
/* Various support functions */
inline uint32_t get_one_bit(void) __attribute__((__always_inline__));
volatile uint32_t *restart_DMA(void);
inline volatile uint32_t *get_DMA_read_buf(void);
inline uint32_t safe_get_counter(volatile uint32_t *dmabuf, const uint32_t dmabuf_idx);
void check_uart_rx(UART_HandleTypeDef *this);
/* static void Error_Handler(void); */



int
main()
{
  uint32_t count = 0, send_bytes, idx = 255, send_sync_bytes_in = 0, i, timeout;
  uint32_t mode = MODE_ENTROPY;

  /* Initialize buffers */
  memset(buf.rnd, 0, sizeof(buf.rnd));
  for (i = 0; i < DMA_COUNTERS_NUM; i++) {
    DMA.buf0[i] = 0xffff0000 + i;
    DMA.buf1[i] = 0xffff0100 + i;
  }

  stm_init((uint32_t *) &DMA.buf0, DMA_COUNTERS_NUM);
  /* Ensure there is actual Timer IC counters in both DMA buffers. */
  restart_DMA();
  restart_DMA();

  huart = &huart1;

  /* Toggle GREEN LED to show we've initialized */
  {
    for (i = 0; i < 10; i++) {
      HAL_GPIO_TogglePin(LED_PORT, LED_GREEN);
      HAL_Delay(125);
    }
  }

  if (mode == MODE_ENTROPY) {
    /* Initialize buffer with the first round of entropy */
    idx = 0;
    send_bytes = UART_RANDOM_BYTES_PER_CHUNK;
    get_entropy32(send_bytes / 4, idx);
  }

  /*
   * Main loop
   */
  while (1) {
    if (! (count % 1000)) {
      HAL_GPIO_TogglePin(LED_PORT, LED_YELLOW);
    }

    switch (mode)
      {
      case MODE_DELTAS:
	if (! send_sync_bytes_in) {
	  /* Send 128 bits of sync-bytes every 1024 bytes */
	  send_sync_bytes_in = 1024;
	  memset(buf.rnd, 0xf0, sizeof(buf.rnd));
	  send_bytes = 16;
	} else {
	  perform_delta32(UART_DELTA_WORDS_PER_CHUNK, 0);
	  send_bytes = UART_DELTA_WORDS_PER_CHUNK * 4;
	  send_sync_bytes_in -= send_bytes;
	}
	idx = 0;
	break;;
      case MODE_ENTROPY:
	break;;
      }

    /* Send buf on UART (non blocking interrupt driven send). */
    UartReady = RESET;
    if (HAL_UART_Transmit_IT(huart, (uint8_t *) buf.rnd + idx, (uint16_t) send_bytes) == HAL_OK) {

      if (mode == MODE_ENTROPY) {
	/* Flip-flop idx between the value 0 and the value BYTES_PER_CHUNK.
	 * This enables collecting of BYTES_PER_CHUNK bytes of entropy at the same
	 * time as the USART sends the previous BYTES_PER_CHUNK using interrupts.
	 */
	idx = idx ? 0 : UART_RANDOM_BYTES_PER_CHUNK;
	get_entropy32(send_bytes / 4, idx / 4);
      }

      timeout = 0xffff;
      while (UartReady != SET && timeout) { timeout--; }
    }

    if (UartReady != SET) {
      /* Failed to send, turn on RED LED for one second */
      HAL_GPIO_WritePin(LED_PORT, LED_RED, GPIO_PIN_SET);
      HAL_Delay(1000);
      HAL_GPIO_WritePin(LED_PORT, LED_RED, GPIO_PIN_RESET);
    }

    /* Check for UART change request */
    check_uart_rx(&huart1);
    check_uart_rx(&huart2);

    count++;
  }

}

/**
 * @brief  Fill a buffer (buf.rnd32) with Timer IC counter values.
 *         Each value is 16 bits, but this function packs two counters
 *         into a 32 bit word so (2 * count) timer values are collected.
 * @param  count: Number of 32 bit words to collect.
 * @param  start: Start index value into buf.rnd32.
 * @retval None
 */
void perform_delta32(uint32_t count, const uint32_t start)
{
  /* Start at end of buffer so restart_DMA() is called. */
  static uint32_t dmabuf_idx = DMA_COUNTERS_NUM - 1;
  volatile uint32_t *dmabuf;
  uint32_t i, buf_idx;

  dmabuf = get_DMA_read_buf();
  buf_idx = start;

  do {
    if (dmabuf_idx > DMA_COUNTERS_NUM - 1 - 2) {
      /* If there are less than two counters available in the dmabuf,
       * we need to get a fresh DMA buffer first.
       */
      dmabuf = restart_DMA();
      dmabuf_idx = FIRST_DMA_IDX_USED;
    }

    i  = safe_get_counter(dmabuf, dmabuf_idx++) << 16;
    i |= safe_get_counter(dmabuf, dmabuf_idx++);

    /* Store the 32 bits in output buffer */
    buf.rnd32[buf_idx++] = i;
  } while (--count);
}


/**
 * @brief  Collect `count' times 32 bits of entropy.
 * @param  count: Number of 32 bit words to collect.
 * @param  start: Start index value into buf.rnd32.
 * @retval None
 */
inline void get_entropy32(uint32_t count, const uint32_t start)
{
  uint32_t i, bits, buf_idx;

  buf_idx = start;

  do {
    bits = 0;
    /* Get 32 bits of entropy.
     */
    for (i = 32; i; i--) {
      bits <<= 1;
      bits += get_one_bit();
    }

    /* Store the 32 bits in output buffer */
    buf.rnd32[buf_idx++] = bits;
  } while (--count);
}

/**
 * @brief  Return one bit of entropy.
 * @param  None
 * @retval One bit, in the LSB of an uint32_t since this is a 32 bit MCU.
 */
inline uint32_t get_one_bit()
{
  register uint32_t a, b, temp;
  /* Start at end of buffer so restart_DMA() is called. */
  static uint32_t dmabuf_idx = DMA_COUNTERS_NUM - 1;
  volatile uint32_t *dmabuf;

  dmabuf = get_DMA_read_buf();

  do {
    if (dmabuf_idx > DMA_COUNTERS_NUM - 1 - 4) {
      /* If there are less than four counters available in the dmabuf,
       * we need to get a fresh DMA buffer first.
       */
      dmabuf = restart_DMA();
      dmabuf_idx = FIRST_DMA_IDX_USED;
    }

    /* Get one bit from two subsequent counter values */
    a = safe_get_counter(dmabuf, dmabuf_idx++) & 1;
    temp = safe_get_counter(dmabuf, dmabuf_idx++) & 1;
    a ^= temp;

    /* Get another bit from two other counter values. Getting
     * two bits from two unrelated [1] pairs of counters is
     * supposed to help against phase correlations between the
     * frequency of the noise and the MCU sampling rate.
     *
     * [1] This is how it is done in the ARRGH board, although
     *     since this is a faster MCU and DMA is used the two
     *     pairs are more likely to still be related since they
     *     are more likely to be directly subsequent diode
     *     breakdowns. Have to evaluate if this is enough.
     */
    b = safe_get_counter(dmabuf, dmabuf_idx++) & 1;
    temp = safe_get_counter(dmabuf, dmabuf_idx++) & 1;
    b ^= temp;

    /* Do von Neumann extraction of a and b to eliminate bias
     * (only eliminates bias if a and b are uncorrelated)
     */
  } while (a == b);

  return a;
}

/**
 * @brief  Return a pointer to the DMA.buf NOT currently being written to
 * @param  None
 * @retval Pointer to buffer currently being read from.
 */
inline volatile uint32_t *get_DMA_read_buf(void)
{
  return DMA.write_buf ? DMA.buf0 : DMA.buf1;
}

/**
 * @brief  Return a pointer to the DMA.buf currently being written to
 * @param  None
 * @retval Pointer to buffer currently being written to.
 */
inline volatile uint32_t *get_DMA_write_buf(void)
{
  return DMA.write_buf ? DMA.buf1 : DMA.buf0;
}

/**
 * @brief  Initiate DMA collection of another buffer of Timer IC values.
 * @param  None
 * @retval Pointer to buffer full of Timer IC values ready to be consumed.
 */
volatile uint32_t *restart_DMA(void)
{
  /* Wait for transfer complete flag to become SET. Trying to change the
   * M0AR register while the DMA is running is a no-no.
   */
  while(__HAL_DMA_GET_FLAG(&hdma_tim, __HAL_DMA_GET_TC_FLAG_INDEX(&hdma_tim)) == RESET) { ; }

  /* Switch buffer being written to */
  DMA.write_buf ^= 1;
  hdma_tim.Instance->M0AR = (uint32_t) get_DMA_write_buf();

  /* Start at 0 to help manual inspection */
  TIM2->CNT = 0;

  /* Clear the transfer complete flag before re-enabling DMA */
  __HAL_DMA_CLEAR_FLAG(&hdma_tim, __HAL_DMA_GET_TC_FLAG_INDEX(&hdma_tim));
  __HAL_DMA_ENABLE(&hdma_tim);

  return get_DMA_read_buf();
}

/**
 * @brief  Get one counter value, guaranteed to not have been used before.
 * @param  dmabuf:     Pointer to the current DMA read buffer.
 * @param  dmabuf_idx: Word index into `dmabuf'.
 * @retval One Timer IC counter value.
 */
inline uint32_t safe_get_counter(volatile uint32_t *dmabuf, const uint32_t dmabuf_idx) {
  register uint32_t a;
  /* Prevent re-use of values. DMA stored values are <= 0xffff. */
  do {
    a = dmabuf[dmabuf_idx];
  } while (a > 0xffff);
  dmabuf[dmabuf_idx] = 0xffff0000;
  return a;
}

/* UART transmit complete callback */
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *UH)
{
  if ((UH->Instance == USART1 && huart->Instance == USART1) ||
      (UH->Instance == USART2 && huart->Instance == USART2)) {
    /* Signal UART transmit complete to the code in the main loop. */
    UartReady = SET;
  }
}

/*
 * If a newline is received on UART1 or UART2, redirect output to that UART.
 */
void check_uart_rx(UART_HandleTypeDef *this) {
  uint8_t rx = 0;
  if (HAL_UART_Receive(this, &rx, 1, 0) == HAL_OK) {
    if (rx == '\n') {
      huart = this;
      /* Signal UART transmit complete to the code in the main loop. */
      UartReady = SET;
    }
  }
}

/**
 * @brief  This function is executed in case of error occurrence.
 * @param  None
 * @retval None
 */
/* Currently unused function */
#if 0
static void Error_Handler(void)
{
  /* Turn on RED LED and then loop indefinitely */
  HAL_GPIO_WritePin(LED_PORT, LED_RED, GPIO_PIN_SET);
  while(1) { ; }
}
#endif