如何使用STM32 MCU生成真正的随机数?

7

我正在使用Keil Microvision IDE开发STM32F103E Arm Cortex-M3 MCU的项目。
我需要生成一些随机数,但我不想使用标准C++库生成的伪随机数,因此我需要一种使用硬件特性生成真正随机数的方法,但我不知道如何实现。
有什么建议吗?(我是一名软件工程师,不是电子专业人员,请简单描述)


1
你的芯片/板子有硬件随机数生成器吗? - Mat
你需要随机数做什么? - starblue
我需要用于RSA密钥生成的随机数。正如Jari所说,似乎F1系列没有RNG,但我想知道是否有任何方法可以使用其他硬件特性(例如RTC)模拟硬件RNG? - Ehsan Khodarahmi
我想你可以购买一颗TRNG芯片,然后通过STM32上的接口进行通信。 - old_timer
5个回答

14

正如指出的那样,芯片没有硬件随机数生成器。

但你可以自己制作。通常的方法是测量独立时钟之间的抖动。独立意味着两个时钟由不同的晶体或RC振荡器支持,并且不是从相同的时钟导出的。

我会使用:

  • 从系统时钟派生的SysTick计时器/计数器(MHz范围)
  • kHz范围的RC振荡器之一

在kHz范围的RC振荡器上设置一个计数器,使其每秒产生几次中断。在中断处理程序中,读取SysTick计数器的当前值。无论是否将SysTick用于其他目的(调度),都可以肯定地说,最低的5位或更少位是不可预测的。

为了从中获取随机数,请使用普通伪随机数生成器。使用上面收集的熵来不可预测地突变伪随机数生成器的内部状态。对于密钥生成,请不要一次读取所有位,而是允许进行几次突变。

对此的攻击显而易见:如果攻击者能够测量或控制kHz范围的RC振荡器,其精度达到MHz,则随机性消失。如果你担心这个问题,请使用智能卡或其他安全协处理器。


14

我刚发现这是一个老问题,但我想回答,因为我不认为其他答案令人满意。

"我需要用于RSA密钥生成的随机数。"

这意味着PRNG例程(我常常错误地称之为RNG,这是我的一个怪癖)是不可接受的,并且无法提供所需的安全性。

外部真正的随机数发生器是可以接受的,但最优雅的答案是改用STM32F2xx或STM32F4xx微控制器,它具有内置的真正随机数发生器,专门用于此类应用程序。为了开发,我想您可以使用F1和任何PRNG,但那里的诱惑是“它能工作,让我们将其上市”而没有使用真正的随机数发生器,在可用正确组件(肯定是ST F4,我认为F2芯片在此问题被提出之前已经存在)之前发布有故障的产品。

由于非技术原因,此答案可能不可接受(芯片已指定,OP对所需功能没有输入),但选择芯片的人应该根据应用程序需要的芯片内外设和功能来选择。


4

我发现并测试了另一种方法,效果非常好。它可以生成真正的随机32位数字,我从未检查过它的速度有多快,可能每个数字需要几毫秒。以下是具体步骤:

  • 以最快速度读取嘈杂的内部温度,以产生最大的ADC噪声
  • 通过硬件CRC生成器(在大多数(全部?)STM32芯片上都可用)运行这些值

重复几次,我发现重复8次可以得到相当不错的随机性。我通过将输出值按升序排序并在Excel中绘制它们来检查随机性,好的随机数会生成一条直线,而不良的随机性或某些数字的“聚集”立即可见。

以下是适用于STM32F03的代码:

uint32_t getTrueRandomNumber(void) {

ADC_InitTypeDef ADC_InitStructure;

//enable ADC1 clock
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

// Initialize ADC 14MHz RC
RCC_ADCCLKConfig(RCC_ADCCLK_HSI14);
RCC_HSI14Cmd(ENABLE);
while (!RCC_GetFlagStatus(RCC_FLAG_HSI14RDY))
    ;

ADC_DeInit(ADC1);
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStructure.ADC_ScanDirection = ADC_ScanDirection_Backward;
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_TRGO; //default
ADC_Init(ADC1, &ADC_InitStructure);

//enable internal channel
ADC_TempSensorCmd(ENABLE);

// Enable ADCperipheral
ADC_Cmd(ADC1, ENABLE);
while (ADC_GetFlagStatus(ADC1, ADC_FLAG_ADEN) == RESET)
    ;

ADC1->CHSELR = 0; //no channel selected
//Convert the ADC1 temperature sensor, user shortest sample time to generate most noise
ADC_ChannelConfig(ADC1, ADC_Channel_TempSensor, ADC_SampleTime_1_5Cycles);

// Enable CRC clock
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_CRC, ENABLE);

uint8_t i;
for (i = 0; i < 8; i++) {
    //Start ADC1 Software Conversion
    ADC_StartOfConversion(ADC1);
    //wait for conversion complete
    while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)) {
    }

    CRC_CalcCRC(ADC_GetConversionValue(ADC1));
    //clear EOC flag
    ADC_ClearFlag(ADC1, ADC_FLAG_EOC);
}

//disable ADC1 to save power
ADC_Cmd(ADC1, DISABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, DISABLE);

return CRC_CalcCRC(0xBADA55E5);

}


5
你的方法有缺陷。通过观察白化(CRC)后的输出结果来确定熵源(ADC)的质量是不可行的。此外,简单的输出值直方图只能检测到 RNG 中最简单的缺陷。你需要使用8个轮次才能生成看起来甚至有点随机的东西,这表明你从每个 ADC 样本中获得了非常少的熵(如果有的话)。因此,在进行进一步的分析之前,你不能对 RNG 的熵做出任何假设,因此也不能将其用于加密密钥生成。 - JimmyB

4

F1系列似乎没有RNG(硬件随机数生成器),所以您的唯一选择是使用伪随机数或请求外部输入(例如,某些人认为人手运动是随机的)。使用某些加密库而不是标准的C++库通常可以获得更好的伪随机数。


1
异步时钟方法效果不佳,我已经尝试过。问题在于RC时钟的稳定性在短时间内相当好,因此它们与另一个RC或晶体时钟同步时会产生相当稳定的模式(低熵)。
温度传感器方法效果略好;与上述方法不同,您可以使用ADC配置为最大噪声(最大分辨率,无平均值,最小采样时间)读取温度传感器N次,并使用每个读数的低位作为随机位,将其移位到位置n以生成n位随机数。这种方法效果略好,但也不是非常随机。
到目前为止,我发现最好的策略是利用RAM在上电时固有的随机性作为熵源。在初始化RAM之前,在重置原因为POR时计算RAM的哈希值(CRC,SHA,任何一种),并将该值保存在启动代码不清除的RAM区域或不跨重置清除的寄存器中。将该值用作LFSR的初始种子,用作PRNG。每次使用PRNG时,更新存储的种子并使用它来种子下一个PRNG调用。这样,即使跨重置和电源周期,您也会得到随机序列。
显然仍不如TRNG好,但至今为止我发现的PRNG解决方案的最佳熵源。如果有人发现更好的方法,请发布!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接