我有一个随机数字生成器,可以在恒定时间内运行。
此功能的原型如下:
uint8_t rand();
我想要做的是创建一个随机返回uint8_t的函数,使得输出介于0和max之间,其中max是要返回的最大数。这种功能的原型是:
uint8_t randi(uint8_t max);
有在线算法可以执行此操作以及堆栈溢出。例如,https://stackoverflow.com/a/6852396/1444313。但我的问题是我想在不断的时间内实现这一目标。我找不到任何方法可以在不变的时间内完成这项工作。
此外,我在没有硬件分区的ARM Cortex-m0上运行,因此无法使用%运算符。
有没有人对如何在恒定时间内实现这一目标有任何建议或指示?
由于
答案 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