范围内的恒定时间随机数

时间:2017-03-17 17:29:56

标签: c arm cortex-m thumb

我有一个随机数字生成器,可以在恒定时间内运行。

此功能的原型如下:

uint8_t rand();

我想要做的是创建一个随机返回uint8_t的函数,使得输出介于0和max之间,其中max是要返回的最大数。这种功能的原型是:

uint8_t randi(uint8_t max);

有在线算法可以执行此操作以及堆栈溢出。例如,https://stackoverflow.com/a/6852396/1444313。但我的问题是我想在不断的时间内实现这一目标。我找不到任何方法可以在不变的时间内完成这项工作。

此外,我在没有硬件分区的ARM Cortex-m0上运行,因此无法使用%运算符。

有没有人对如何在恒定时间内实现这一目标有任何建议或指示?

由于

5 个答案:

答案 0 :(得分:4)

从最严格的意义上讲,这些要求是不可能满足的。只要您的RNG以二进制形式生成均匀分布,就无法在不丢弃某些结果或分布不完美的情况下将其映射到非2的幂数范围。

您可以使瑕疵小很多,也可以使丢弃的情况很少。由于罕见的丢弃表示发生时出现了意外的时序异常(并且通过使其变得更稀有,您只会使其变得更加令人惊讶),请考虑this solution,它是恒定时间,可以适应您的特定rand()函数方式:

int n = max + 1;
int r = n / 2;
for (int i = 0; i < 6; ++i) {
    r = (rand() * n + r) >> 8;
}
return r;

这将抽取48个随机位,取0到1之间的一个分数,并将其乘以n,仅保留整数部分,该整数部分的值将不大于max。偏爱某些价值相对于其他价值的偏好小于万亿分之一。如果您对此不满意,则可以将6更改为更大的值。

这种偏倚意味着,如果您每秒绘制1000个随机数,则要花30多年的时间才能看到一个结果出现的时间比预期的多,而统计噪声仍然会淹没那个时间。

通过将rand()替换为可以在单个调用中产生更多随机位的东西,并且仅迭代一次或两次,可以使此过程更快。

答案 1 :(得分:0)

为了帮助澄清OP的困境,下面是两个不符合OP目标的简单解决方案。

的作用是什么:

非均匀分布,除非max +1是2的幂。

uint8_t randi_fail1(uint8_t max) {
  return rand()%(max + 1);
}

分布均匀,但时间不统一。

uint8_t randi_fail2(uint8_t max) {
  unsigned n = (256/(max+1))*(max+1);
  unsigned r;
  do {
    r = rand();  // 0 - 255
  } while (r >= n);
  return r/(max+1);
}

答案 2 :(得分:0)

这是一个符合您要求的解决方案,但可能不喜欢!

制作256个256或65536个随机值的数组,称之为uint8_t randval[255][256];

现在每个randval[N][M] <= N;换句话说,子数组专门用于randi max参数。

uint8_t randi(uint8_t max)
{
    return (max == 0u) ? 0u : randval[max - 1][rand()];
}

请注意,randval可以在编译时构建,也可以在@Realtime Rik建议的后台更新。当然,如果在编译时构造,它可以是const,并存储在flash中。

答案 3 :(得分:0)

输入和输出都是8位字节,那么为什么不得到你的随机值,乘以你的'&#39; max&#39;,然后右移8除以256?我通过获取0-25值(8位随机,乘以26,取顶部字节)来选择随机字母。

答案 4 :(得分:0)

此处基于表的答案可确保在理想的硬件上持续执行,但在现实生活中,它因内存访问定时是可定时的以及与访问数据相关的(randval[..][rand()]而异,这会有所不同由于CPU级别的缓存而无法及时更新):https://stackoverflow.com/a/42867281

这里的统一分配建议是可行的方法:https://stackoverflow.com/a/42865274 就安全性而言,通常您想要的东西是constant time时,您只是想要您的代码中没有data-dependent个代码路径-内存访问和指令分支-而不是时间实际上恒定

要保护边界不被暴露,您可以提供一个上限(例如64位),并使用与boringssl的FIPS模块中此处使用的constant_time_select_int类似的技术(不知道为什么在其中使用常规加密代码boringssl不使用恒定时间实现)来获取所需的样本-但是请注意,在大多数情况下,仅敏感的是随机数生成器返回的值而不是范围:{{3} }

有关更多启发,请参阅boringssl中的随机数生成器/包装器函数: https://github.com/google/boringssl/blob/master/crypto/fipsmodule/bn/cmp.c#L77和OCaml库eqaf(请注意,后者专门处理单个字节,因此比较功能compare(a,b){ return (a - b) }并非旨在处理{{1 }}):https://github.com/google/boringssl/blob/master/crypto/fipsmodule/bn/random.c