/* * 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; static UART_HandleTypeDef *huart; static __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 */ static void get_entropy32(uint32_t num_bytes, uint32_t buf_idx); /* Various support functions */ static inline uint32_t get_one_bit(void) __attribute__((__always_inline__)); static volatile uint32_t *restart_DMA(void); static inline volatile uint32_t *get_DMA_read_buf(void); static inline uint32_t safe_get_counter(volatile uint32_t *dmabuf, const uint32_t dmabuf_idx); static void check_uart_rx(UART_HandleTypeDef *this); static void cc_reseed(struct cc20_state *cc); void Error_Handler(void); 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 */ static 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 */ static 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. */ static 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. */ static 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. */ static 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. */ static 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. */ static 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. */ static 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) { ; } }