为什么没有转换为 Big Endian 的 Arduino(Little Endian)上的 Sha1 工作?
Why does Sha1 on Arduino (Little Endian) without conversion to Big Endian work?
我很幸运地说,我的简单 SHA1 哈希生成器代码似乎运行良好。不幸的是,我知道这个 Arduino 程序以 Little Endianness 运行,并且关于生成哈希的方法的描述要求将原始消息长度附加为 Big Endian 整数。
这意味着对于消息 char m[] = "Applecake"
我将有 9*8 位,表示为 64 位无符号整数,即 0x0000 0000 0000 0048
。这意味着,使用 Little Endian 存储,内存将如下所示:0x0048 0000 0000 0000
.
如 Section 4 of RFC 3174 步骤 c) 中所述,我必须
Obtain the 2-word representation of l, the number of bits in the original message. If l < 2^32 then the first word is all zeroes. Append these two words to the padded message.
所以根据我上面描述的内存,我必须先将它转换为 Big Endian,然后将低 32 位附加到填充的消息中。
问题是,如果我确实转换了长度的字节顺序,我知道它是 Little Endian,我得到了错误的填充,因此得到了错误的散列。
为什么我的代码在没有转换 Endianness 的情况下工作?
我的代码在不同 Arduino、微控制器和编译器之间的兼容性方面有哪些限制?
// initialize variables
h0 = 0x67452301;
h1 = 0xEFCDAB89;
h2 = 0x98BADCFE;
h3 = 0x10325476;
h4 = 0xC3D2E1F0;
// calculate the number of required cycles and create a blocks array
uint32_t numCycles = ((ml+65)/512)+1;
uint32_t blocks[numCycles*16] = {};
// copy message
uint32_t messageBytes = ml/8 + (ml%8!=0 ? 1 : 0);
for (uint32_t i = 0; i < messageBytes; i++) {
blocks[i/4] |= ((uint32_t) message[i]) << (8*(3-(i%4)));
}
// append the 1 bit
blocks[ml/32] |= ((uint32_t) 0b1) << (31-(ml%32));
// append the 64-bit big endian ml at the end
if (ml < 0x80000000)
blocks[(numCycles*16)-1] = (uint32_t) ml;
else {
blocks[(numCycles*16)-2] = (uint32_t) ml;
blocks[(numCycles*16)-1] = (uint32_t) (ml >> 32);
}
for (uint32_t iCycle = 0; iCycle < numCycles; iCycle++) {
// initalize locals
uint32_t w[80] = {};
uint32_t a = h0, b = h1, c = h2, d = h3, e = h4;
for (uint8_t i = 0; i < 80; i++) {
// convert words to big-endian and copy to 80-elem array
if (i < 16)
w[i] = blocks[(iCycle*16)+i];
else
w[i] = rotL((w[i-3]^w[i-8]^w[i-14]^w[i-16]), 1);
// run defined formulas
uint32_t f, k, temp;
if (i < 20) {
f = (b & c) | ((~b) & d);
k = 0x5A827999;
}
else if (i < 40) {
f = b ^ c ^ d;
k = 0x6ED9EBA1;
}
else if (i < 60) {
f = (b & c) | (b & d) | (c & d);
k = 0x8F1BBCDC;
}
else {
f = b ^ c ^ d;
k = 0xCA62C1D6;
}
temp = rotL(a, 5) + f + e + k + w[i];
e = d; d = c; c = rotL(b, 30); b = a; a = temp;
}
// write back the results
h0 += a; h1 += b; h2 += c; h3 += d; h4 += e;
}
// append the 64-bit big endian ml at the end
if (ml < 0x80000000)
blocks[(numCycles*16)-1] = (uint32_t) ml;
else {
blocks[(numCycles*16)-2] = (uint32_t) ml;
blocks[(numCycles*16)-1] = (uint32_t) (ml >> 32);
}
这会将 most-significant 32 位值放在第一位,然后将 least-significant 32 位值放在第二位。这是您的代码工作的一半原因。
另一半是,虽然 32 位值采用 little-endian 形式,但您正在 little-endian 平台上读取它们的 值 。这将始终为您提供正确的价值。您永远不会尝试访问 32 位值的各个字节,因此哪些字节去哪里没有区别。
我很幸运地说,我的简单 SHA1 哈希生成器代码似乎运行良好。不幸的是,我知道这个 Arduino 程序以 Little Endianness 运行,并且关于生成哈希的方法的描述要求将原始消息长度附加为 Big Endian 整数。
这意味着对于消息
char m[] = "Applecake"
我将有 9*8 位,表示为 64 位无符号整数,即0x0000 0000 0000 0048
。这意味着,使用 Little Endian 存储,内存将如下所示:0x0048 0000 0000 0000
.如 Section 4 of RFC 3174 步骤 c) 中所述,我必须
Obtain the 2-word representation of l, the number of bits in the original message. If l < 2^32 then the first word is all zeroes. Append these two words to the padded message.
所以根据我上面描述的内存,我必须先将它转换为 Big Endian,然后将低 32 位附加到填充的消息中。
问题是,如果我确实转换了长度的字节顺序,我知道它是 Little Endian,我得到了错误的填充,因此得到了错误的散列。
为什么我的代码在没有转换 Endianness 的情况下工作?
我的代码在不同 Arduino、微控制器和编译器之间的兼容性方面有哪些限制?
// initialize variables
h0 = 0x67452301;
h1 = 0xEFCDAB89;
h2 = 0x98BADCFE;
h3 = 0x10325476;
h4 = 0xC3D2E1F0;
// calculate the number of required cycles and create a blocks array
uint32_t numCycles = ((ml+65)/512)+1;
uint32_t blocks[numCycles*16] = {};
// copy message
uint32_t messageBytes = ml/8 + (ml%8!=0 ? 1 : 0);
for (uint32_t i = 0; i < messageBytes; i++) {
blocks[i/4] |= ((uint32_t) message[i]) << (8*(3-(i%4)));
}
// append the 1 bit
blocks[ml/32] |= ((uint32_t) 0b1) << (31-(ml%32));
// append the 64-bit big endian ml at the end
if (ml < 0x80000000)
blocks[(numCycles*16)-1] = (uint32_t) ml;
else {
blocks[(numCycles*16)-2] = (uint32_t) ml;
blocks[(numCycles*16)-1] = (uint32_t) (ml >> 32);
}
for (uint32_t iCycle = 0; iCycle < numCycles; iCycle++) {
// initalize locals
uint32_t w[80] = {};
uint32_t a = h0, b = h1, c = h2, d = h3, e = h4;
for (uint8_t i = 0; i < 80; i++) {
// convert words to big-endian and copy to 80-elem array
if (i < 16)
w[i] = blocks[(iCycle*16)+i];
else
w[i] = rotL((w[i-3]^w[i-8]^w[i-14]^w[i-16]), 1);
// run defined formulas
uint32_t f, k, temp;
if (i < 20) {
f = (b & c) | ((~b) & d);
k = 0x5A827999;
}
else if (i < 40) {
f = b ^ c ^ d;
k = 0x6ED9EBA1;
}
else if (i < 60) {
f = (b & c) | (b & d) | (c & d);
k = 0x8F1BBCDC;
}
else {
f = b ^ c ^ d;
k = 0xCA62C1D6;
}
temp = rotL(a, 5) + f + e + k + w[i];
e = d; d = c; c = rotL(b, 30); b = a; a = temp;
}
// write back the results
h0 += a; h1 += b; h2 += c; h3 += d; h4 += e;
}
// append the 64-bit big endian ml at the end
if (ml < 0x80000000)
blocks[(numCycles*16)-1] = (uint32_t) ml;
else {
blocks[(numCycles*16)-2] = (uint32_t) ml;
blocks[(numCycles*16)-1] = (uint32_t) (ml >> 32);
}
这会将 most-significant 32 位值放在第一位,然后将 least-significant 32 位值放在第二位。这是您的代码工作的一半原因。
另一半是,虽然 32 位值采用 little-endian 形式,但您正在 little-endian 平台上读取它们的 值 。这将始终为您提供正确的价值。您永远不会尝试访问 32 位值的各个字节,因此哪些字节去哪里没有区别。