语境
OpenSSL 中的功能 BN_consttime_swap
很漂亮。在此代码段中,condition
已计算为0
or (BN_ULONG)-1
:
#define BN_CONSTTIME_SWAP(ind) \
do { \
t = (a->d[ind] ^ b->d[ind]) & condition; \
a->d[ind] ^= t; \
b->d[ind] ^= t; \
} while (0)
…
BN_CONSTTIME_SWAP(9);
…
BN_CONSTTIME_SWAP(8);
…
BN_CONSTTIME_SWAP(7);
目的是为了确保更高级别的 bignum 操作花费恒定的时间,这个函数要么交换两个 bignum,要么在恒定的时间内将它们留在原处。当它把它们留在原地时,它实际上读取每个 bignum 的每个单词,计算一个与旧单词相同的新单词,并将结果写回原始位置。
目的是这将花费与 bignums 被有效交换的时间相同的时间。
在这个问题中,我假设一个现代的、广泛使用的架构,例如 Agner Fog 在他的优化手册中描述的架构。还假定将 C 代码直接转换为汇编(没有 C 编译器取消程序员的努力)。
问题
我试图理解上面的构造是否被描述为“尽力而为”的恒定时间执行,还是完美的恒定时间执行。
特别是,我担心调用a
函数时 bignum 已经在 L1 数据缓存中BN_consttime_swap
,而函数返回后的代码立即开始处理 bignum的情况a
。在现代处理器上,可以同时运行足够多的指令,以便在使用 bignum 时复制不会在技术上完成a
。允许调用后的指令BN_consttime_swap
工作的机制a
是内存依赖推测。为了论证,让我们假设幼稚的内存依赖推测。
这个问题似乎归结为:
当处理器最终检测到BN_consttime_swap
从内存中读取的代码(与推测相反,已写入函数内部)时,它是在检测到地址已被写入后立即取消推测执行,还是允许自己当它检测到已写入的值与已经存在的值相同时保留它?
在第一种情况下,BN_consttime_swap
看起来它实现了完美的恒定时间。在第二种情况下,它只是尽力而为的常数时间:如果没有交换 bignums,则调用之后的代码的执行BN_consttime_swap
速度将比交换它们时快得多。
即使在第二种情况下,这看起来也可以在可预见的未来(只要处理器保持足够幼稚)通过为两个 bignums 中的每一个的每个单词写入一个与两个可能的 final 不同的值来修复值之前再次写入旧值或新值。在某些时候可能需要使用volatile
类型限定符以防止普通编译器过度优化序列,但听起来仍然可行。
注意:我知道商店转发,但商店转发只是一个捷径。它不会阻止在它应该之后的写入之前执行读取。在某些情况下它会失败,尽管在这种情况下人们不会期望它会失败。