aboutsummaryrefslogtreecommitdiff
path: root/src/cc20rng/main.c
diff options
context:
space:
mode:
authorFredrik Thulin <fredrik@thulin.net>2016-11-21 11:45:58 +0100
committerFredrik Thulin <fredrik@thulin.net>2016-11-21 14:14:09 +0100
commite7f5fdebef90d0be01edeabd4ea260138ed1fbd0 (patch)
tree2396c0328e3bbee3f8286fe3c0b443bef4c76d58 /src/cc20rng/main.c
parentc4678339908e413cbc6751cf863267807acafc85 (diff)
add RNG using ChaCha20 as CSPRNGHEADmaster
Diffstat (limited to 'src/cc20rng/main.c')
-rw-r--r--src/cc20rng/main.c358
1 files changed, 358 insertions, 0 deletions
diff --git a/src/cc20rng/main.c b/src/cc20rng/main.c
new file mode 100644
index 0000000..2c2650c
--- /dev/null
+++ b/src/cc20rng/main.c
@@ -0,0 +1,358 @@
+/*
+ * Copyright (c) 2014, 2015, 2016 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"
+#include "cc20_prng.h"
+
+#define UART_RANDOM_BYTES_PER_CHUNK 8
+#define RESEED_BLOCKS CHACHA20_MAX_BLOCK_COUNTER
+
+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);
+/* 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);
+void Error_Handler(void);
+void cc_reseed(struct cc20_state *cc);
+
+
+int
+main()
+{
+ uint32_t i, timeout, block_counter = 0;
+ struct cc20_state cc, out;
+ HAL_StatusTypeDef res;
+
+ /* 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);
+
+ if (! chacha20_prng_self_test()) {
+ Error_Handler();
+ }
+
+ /* 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);
+ }
+ }
+
+ /* Generate initial block of random data directly into buf */
+ cc_reseed(&cc);
+ block_counter = RESEED_BLOCKS;
+ chacha20_prng_block(&cc, block_counter--, (struct cc20_state *) buf.rnd32);
+
+ /*
+ * Main loop
+ */
+ while (1) {
+ if (! (block_counter % 1000)) {
+ HAL_GPIO_TogglePin(LED_PORT, LED_YELLOW);
+ }
+
+ if (! block_counter) {
+ cc_reseed(&cc);
+ block_counter = RESEED_BLOCKS;
+ }
+
+ /* Send buf on UART (non blocking interrupt driven send). */
+ UartReady = RESET;
+ res = HAL_UART_Transmit_IT(huart, &buf.rnd[0], CHACHA20_BLOCK_SIZE);
+
+ /* Generate next block while this block is being transmitted */
+ chacha20_prng_block(&cc, block_counter--, &out);
+ /* Copying using a loop is faster than memcpy on STM32 */
+ for (i = 0; i < CHACHA20_NUM_WORDS; i++) {
+ buf.rnd32[i] = out.i[i];
+ }
+
+ if (res == HAL_OK) {
+ 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);
+ }
+}
+
+
+/**
+ * @brief Reseed chacha20 state with hardware generated entropy.
+ * @param cc: ChaCha20 state
+ * @retval None
+ */
+void
+cc_reseed(struct cc20_state *cc)
+{
+ HAL_GPIO_WritePin(LED_PORT, LED_BLUE, GPIO_PIN_SET);
+
+ get_entropy32(CHACHA20_BLOCK_SIZE / 4, 0);
+ restart_DMA();
+ chacha20_prng_reseed(cc, (uint32_t *) &buf);
+
+ HAL_GPIO_WritePin(LED_PORT, LED_BLUE, GPIO_PIN_RESET);
+}
+
+/**
+ * @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
+ */
+
+void Error_Handler(void)
+{
+ /* Turn on RED LED and then loop indefinitely */
+ HAL_GPIO_WritePin(LED_PORT, LED_RED, GPIO_PIN_SET);
+ while(1) { ; }
+}