From e7f5fdebef90d0be01edeabd4ea260138ed1fbd0 Mon Sep 17 00:00:00 2001 From: Fredrik Thulin Date: Mon, 21 Nov 2016 11:45:58 +0100 Subject: add RNG using ChaCha20 as CSPRNG --- src/cc20rng/main.c | 358 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 358 insertions(+) create mode 100644 src/cc20rng/main.c (limited to 'src/cc20rng/main.c') 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 + +#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) { ; } +} -- cgit v1.2.3