Generating True Random Numbers | STM32L4 | RNG | CMSIS
True Random Number Generator
Randomness is necessary for cryptography operations to be unpredictable and to fend off attacks. We need randomization for everything from key generation to data encryption. We use True Random Number Generators, which create random numbers from an entropy source, to get around this predictability. The STM32L4's RNG peripheral is a true RNG that produces high-quality random numbers. The application is guaranteed to receive all entropy from its 32-bit samples via its built-in conditioning component and analog entropy generator. Read this blog on Random Number generators and their limitations for more information.
The RNG runs on two different clocks: the AHB bus clock and a dedicated RNG clock. The AHB clock is used to clock the AHB banked registers and conditioning components. The RNG clock is used for noise source sampling
RNG Registers
The RNG peripheral on the STM32L4S5xxx has few registers compared to that of STM32L4P5xxx and STM32L4Q5xxx microcontrollers. They are:
- RNG Control Register (RNG_CR)
- RNG Status Register (RNG_SR)
- RNG Data Register(RNG_DR)
Configuration of the RNG
First, we will initialize the RNG peripheral by setting the bits in the RNG_CR register. Then we will wait for the RNG_DR register to contain valid data. Once the DRDY bit is set in the RNG_SR register, we will read the RNG_DR register. The RNG_DR register will have a 32-bit random number. For common data sizes, such as 128-bit, 192-bit, 256-bit, and 512-bit, we have to read the RNG_DR register in multiples of 4 bytes. For that scenario, we can have a generic function that takes the required size as an argument.
Walkthrough
Header File
We have three functions used in this driver. RNG_Init() is used to initialize the RNG Peripheral. RNG_GetRandom() generates a 32-bit random value using the True RNG peripheral. RNG_GenerateRandom() is used to generate random values of user-specific sizes (in bytes).
Configuring Clock source for RNG Peripheral
If you refer to the Clock Tree of the STM32L4S5 Microcontroller, you can see that the RNG Peripheral requires an independent clock rather than the System Clock. Check out the Explanation of the Clock Tree of the STM32L4S5xx to learn more about it. For the RNG Peripheral, we need to have a clock source of 48 MHz. It can be sourced from the MSI, HSI48, PLL48M1CLK or PLL48M2CLK. In the RCC_CCIPR register, we can select the 48 MHz Clock Source required for operating the RNG peripheral by setting the CLK48SEL bits. For our use, since the System Clock is at 4MHz from the MSI (Reset Value), we can choose the 48 MHz Internal RC Oscillator, that is, HSI48. Then, we have to enable the HSI48. For that, we have to set the HSI48ON bit in the RCC_CRRCR register. Then wait for the HSI48RDY bit to be set in that same register.
Initializing the RNG Peripheral
In the Initialization function, initially, we enable the clock to the RNG Peripheral by setting the RNGEN bit in the RCC_AHB2ENR register. If you look at the block diagram of the STM32L4S5xxx Block Diagram in the Reference Manual RM0432, you can notice that the RNG peripheral is connected to the AHB2 Bus. Then we have to wait for the RNGEN bit to be set, as a precaution. Then, we have to enable the Clock Error Detection bit. Note that writing 1 to this bit disables the Clock Error Detection. So, we negate the value. Then, finally enable the RNG peripheral by setting the RNGEN bit in the RNG_CR register.
Generating 32-bit Random Value
Before reading the Data Register for fetching the 32-bit Random value, we should check for Seed Error Current status and Clock Error Current status. If an error has occurred, print that error to the serial terminal. If all goes well, wait for the DRDY bit to be set in the RNG_SR register which indicates that the RNG_DR register contains valid random value. Before reading the RNG_DR register, check if the RNG_DR is 'ZERO'. There might be a possibility for a Seed Error to occur while waiting for the DRDY bit to be set. So, if RNG_DR is not equal to zero, return the value in RNG_DR. If RNG_DR is zero, print an error message.
Generating Random values of specific sizes
To generate Random values of different sizes, we use this function. The user can specify the size as an argument to this function. Once inside the function, we can load the said buffer with random values in blocks of 4 bytes or 32 bits. Note that this function takes the size in bytes and not in bits.
Application of RNG Driver
In main() function, call RNG_Init(). Then, create a character buffer of size 512 bytes. I have defined the size as a Macro RANDOM_NUMBER_LENGTH for ease of changing the size. Then, call the RNG_GenerateRandom() function with the buffer and the size as arguments. Finally, print the contents of the character buffer onto the Serial Terminal. That's it.