如何使用STM32 MCU生成REAL随机数?

时间:2013-02-10 09:39:39

标签: c++ random embedded microcontroller stm32

我正在使用keil microvision IDE中的STM32F103E arm cortex-m3 MCU开展项目。
我需要为某些目的生成随机数,但我不想使用标准c ++库生成的伪随机数,所以我需要一种使用硬件功能生成REAL随机数的方法,但我不知道如何我能做到。
任何的想法? (我是一名软件工程师而非电子专业人员,所以请简单描述一下:P)

4 个答案:

答案 0 :(得分:10)

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

“我需要随机数生成RSA密钥。”

这意味着PRNG例程(经常被错误地称为RNG,我的一个宠儿)是不可接受的,并且不会提供所需的安全性。

外部真正的RNG是可以接受的,但最优雅的答案是切换到STM32F2xx或STM32F4xx微控制器,该微控制器具有内置的TRUE随机数发生器,正好适用于此类应用。为了开发,我想你可以使用thr F1和任何PRNG,但是在使用真正的RNG之前会有“它可以工作,让我们发货”的诱惑,在正确的组件(当然是ST F4,我认为是STF4)时运送有缺陷的产品自问这个问题之前,F2筹码一直存在。

由于非技术原因(芯片已经指定,OP没有输入所需的功能),这个答案可能是不可接受的,但选择芯片的人应该根据所需的片上外设和功能选择它申请。

答案 1 :(得分:4)

F1系列似乎没有RNG(硬件随机数生成器),因此您唯一的选择是使用伪随机数或询问外部输入(一些考虑例如人手移动随机)。你经常使用一些加密库而不是标准的C ++库来获得更好的伪随机数。

答案 2 :(得分:4)

正如所指出的,该芯片没有硬件RNG。

但你可以自己动手。通常的方法是测量INDEPENDENT时钟之间的抖动。独立意味着两个时钟由不同的christal或RC振荡器支持,而不是来自相同的。

我会用:

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

在kHz范围的RC振荡器上设置一个计数器,每秒几次给你一个中断。在中断处理程序中,您读取SysTick计数器的当前值。无论SysTick是否用于其他目的(调度),低5位都是不可预测的。

要获取随机数,请使用普通的伪RNG。使用上面收集的熵不可预测地改变伪RNG的内部状态。对于密钥生成,不要一次读取所有位,但允许发生一些突变。

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

答案 3 :(得分:2)

我找到并测试了另一种方法,效果很好。它可以生成真正的随机32位数字,我从来没有检查它的速度,每个数字可能需要几毫秒。这是怎么回事:

  • 以尽可能最快的速度读取内部温度噪声,以产生最大的ADC噪声
  • 通过大多数(全部?)STM32芯片上提供的硬件CRC生成器运行值

重复几次,我发现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);

}