最近,仅就此而已,我一直在尝试实现Keccak(SHA-3背后的加密原语)。但是,我遇到了一些问题,特别是在计算排列的“ Iota”步骤中使用的舍入常数。
只是为了解决问题:是的。我知道他们是圆形的常数。我知道我可以将它们硬编码为常量。但是那有什么乐趣呢?
我特别提到了SHA-3上的FIPS 202 specification document以及Keccak团队自己的Keccak reference。但是,尽管我付出了很多努力,但最终似乎无法获得正确的常数。我以前从未处理过位操作,因此,如果我以完全错误的方式进行操作,请随时告诉我。
rc是Keccak的FIPS 202标准中定义的函数,它是线性多项式移位寄存器,其反馈多项式为x^8 + x^6 + x^5 + x^4 + 1
。
t
的值(特定于SHA-3)定义为包含j + 7 * i_r
的整数集,其中i_r = {0,1,...,22,23}和j = {0,1,...,4,5}。
预期输出(舍入常数)定义如下:0x0000000000000001,0x0000000000008082,0x800000000000808a, 0x8000000080008000、0x000000000000808b,0x0000000080000001, 0x8000000080008081、0x8000000000008009、0x000000000000008a, 0x0000000000000088、0x0000000080008009、0x000000008000000a, 0x000000008000808b,0x800000000000008b,0x8000000000008089, 0x8000000000008003、0x8000000000008002、0x8000000000000080, 0x000000000000800a,0x800000008000000a,0x8000000080008081, 0x8000000000008080、0x0000000080000001和0x8000000080008008。
rc函数实现
uint64_t rc(int t)
{
if(t % 255 == 0)
{
return 0x1;
}
uint64_t R = 0x1;
for(int i = 1; i <= t % 255; i++)
{
R = R << 0x1;
R |= (((R >> 0x0) & 0x1) ^ ((R >> 0x8) & 0x1)) << 0x0;
R |= (((R >> 0x4) & 0x1) ^ ((R >> 0x8) & 0x1)) << 0x4;
R |= (((R >> 0x5) & 0x1) ^ ((R >> 0x8) & 0x1)) << 0x5;
R |= (((R >> 0x6) & 0x1) ^ ((R >> 0x8) & 0x1)) << 0x6;
R &= 0xFF;
}
return R & 0x1;
}
rc函数调用
for(int i_r = 0; i_r < 24; i_r++)
{
uint64_t RC = 0x0;
// TODO: Fix so the limit is not constant
for(int j = 0; j < 6; j++)
{
RC ^= (rc(j + 7 * i_r) << ((int) pow(2, j) - 1));
}
printf("%llu\n", RC);
}
在此问题上的任何帮助都将受到赞赏。
答案 0 :(得分:2)
我对代码进行了一些随机更改,现在可以使用了。这些是亮点:
j
循环需要从0到6进行计数。这是因为2 ^ 6-1 =63。因此,如果j
从不为6,则输出将永远不会具有MSB。设置,即不可能输出0x8...。
对于这种类型的应用程序,使用pow
函数通常是一个坏主意。 double
值有一个讨厌的习惯,就是比期望值要低一些,例如4实际上是3.99999999999,当您将其转换为int
时,它会被截断为3。在这种情况下,这种情况令人怀疑,但为什么要冒险呢,因为每次通过循环时,很容易将变量shift
乘以2。
t
的最大值为7 * 23 + 6 = 167,因此% 255
不执行任何操作(至少使用i
和{{1}的值})。另外,也不需要将t
视为特例。当t == 0
为0时,循环将不会运行,因此默认情况下结果为0x1。
在C中实现线性反馈移位寄存器非常简单。多项式中的每个项都对应于一个位。 t
只是2 ^ 8,即x^8
,而0x100
是x^6 + x^5 + x^4 + 1
。因此,只要设置了0x71
位,就可以对0x100
的结果进行XOR。
这是更新的代码:
0x71