小端环境下 C 中的 SHA-1
SHA-1 in C on little-endian environment
我遇到的大多数 SHA-1 实现(甚至在维基百科上)都是为大端运行时编码的。所以我正在尝试为我的机器(小端)实现我自己的版本。
我遵循了维基百科的伪代码形式,得到了以下代码。我找到了一个转换字节顺序的函数,但仍然没有得到正确的输出。
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#define rotateleft(x,n) ((x<<n) | (x>>(32-n)))
unsigned int endian_reverse(unsigned int n)
{
unsigned int m = 0;
m |= n << 24;
m |= ((n >> 8) << 24) >> 8;
m |= ((n << 8) >> 24) << 8;
m |= n >> 24;
return m;
}
void SHA1(unsigned char * str1)
{
unsigned long int h0,h1,h2,h3,h4,a,b,c,d,e,f,k,temp;
h0 = 0x67452301;
h1 = 0xEFCDAB89;
h2 = 0x98BADCFE;
h3 = 0x10325476;
h4 = 0xC3D2E1F0;
unsigned char * str;
str = (unsigned char *)malloc(strlen((const char *)str1)+100);
strcpy((char *)str,(const char *)str1);
int current_length = strlen((const char *)str);
int original_length = current_length;
str[current_length] = 0x80;
str[current_length + 1] = '[=10=]';
char ic = str[current_length];
current_length++;
int ib = current_length % 64;
int i, j;
if(ib<56)
ib = 56-ib;
else
ib = 120 - ib;
for(i=0;i < ib;i++)
{
str[current_length]=0x00;
current_length++;
}
str[current_length + 1]='[=10=]';
for(i=0;i<6;i++)
{
str[current_length]=0x0;
current_length++;
}
str[current_length] = (original_length * 8) / 0x100 ;
current_length++;
str[current_length] = (original_length * 8) % 0x100;
current_length++;
str[current_length+i]='[=10=]';
int number_of_chunks = current_length/64;
unsigned long int word[80];
for(i=0;i<number_of_chunks;i++)
{
for(j=0;j<16;j++)
{
word[j] =
str[i*64 + j*4 + 0] * 0x1000000 + str[i*64 + j*4 + 1] * 0x10000 +
str[i*64 + j*4 + 2] * 0x100 + str[i*64 + j*4 + 3];
}
for(j=16;j<80;j++)
{
word[j] = rotateleft((word[j-3] ^ word[j-8] ^ word[j-14] ^ word[j-16]),1);
}
a = h0;
b = h1;
c = h2;
d = h3;
e = h4;
for(int m=0;m<80;m++)
{
if(m<=19)
{
f = (b & c) | ((~b) & d);
k = 0x5A827999;
}
else if(m<=39)
{
f = b ^ c ^ d;
k = 0x6ED9EBA1;
}
else if(m<=59)
{
f = (b & c) | (b & d) | (c & d);
k = 0x8F1BBCDC;
}
else
{
f = b ^ c ^ d;
k = 0xCA62C1D6;
}
temp = (rotateleft(a,5) + f + e + k + word[m]) & 0xFFFFFFFF;
e = d;
d = c;
c = rotateleft(b,30);
b = a;
a = temp;
}
h0 = h0 + a;
h1 = h1 + b;
h2 = h2 + c;
h3 = h3 + d;
h4 = h4 + e;
}
h0 = endian_reverse(h0);
h1 = endian_reverse(h1);
h2 = endian_reverse(h2);
h3 = endian_reverse(h3);
h4 = endian_reverse(h4);
printf("\n\n");
printf("Hash: %x %x %x %x %x",h0, h1, h2, h3, h4);
printf("\n\n");
}
int main()
{
SHA1((unsigned char *)"abc");
return 0;
}
SHA-1 ("abc"):
结果
Hash: f7370736 e388302f 15815610 1ccacd49 e7649bb6
正确(实际)
Hash: a9993e36 4706816a ba3e2571 7850c26c 9cd0d89d
我是否正确或在正确的位置进行字节序转换?
Am I doing my endianness conversion correctly or in the correct spot?
当 unsigned
不是 32 位时,字节顺序转换 endian_reverse(()
不正确。
未在正确位置使用字节顺序转换。不需要 Endian 转换。
存在其他问题。
代码是 假设 unsigned long int
是 32 位。当 unsigned long int
是 64 位时,我可以得到与 OP 相同的答案。
节省时间:这里没有理由使用松散类型。使用 uint32_t, uint8_t
。对于数组大小和字符串长度,请使用 size_t
。避免带符号的类型和常量。
通过更改 unsigned long
--> uint32_t
并删除 h0 = endian_reverse(h0); ... h7 = endian_reverse(h7);
我想出了
Hash: a9993e36 4706816a ba3e2571 7850c26c 9cd0d89d
其他建议。
64的倍数
size
in str = malloc(size)
应该是 64
的倍数
保持在 64 的倍数范围内
str[current_length+i]='[=23=]';
可以写外分配
备用尺寸存储
适用于 所有 size_t
值最多为 264-3 - 1.
size_t current_length = ...
// append length in bits
uint64_t current_length_bits = current_length;
current_length_bits *= 8;
for (i = 8; i > 0; ) {
i--;
str[current_length + i] = (unsigned char) current_length_bits;
current_length_bits >>= 8;
}
current_length += 8;
我遇到的大多数 SHA-1 实现(甚至在维基百科上)都是为大端运行时编码的。所以我正在尝试为我的机器(小端)实现我自己的版本。
我遵循了维基百科的伪代码形式,得到了以下代码。我找到了一个转换字节顺序的函数,但仍然没有得到正确的输出。
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#define rotateleft(x,n) ((x<<n) | (x>>(32-n)))
unsigned int endian_reverse(unsigned int n)
{
unsigned int m = 0;
m |= n << 24;
m |= ((n >> 8) << 24) >> 8;
m |= ((n << 8) >> 24) << 8;
m |= n >> 24;
return m;
}
void SHA1(unsigned char * str1)
{
unsigned long int h0,h1,h2,h3,h4,a,b,c,d,e,f,k,temp;
h0 = 0x67452301;
h1 = 0xEFCDAB89;
h2 = 0x98BADCFE;
h3 = 0x10325476;
h4 = 0xC3D2E1F0;
unsigned char * str;
str = (unsigned char *)malloc(strlen((const char *)str1)+100);
strcpy((char *)str,(const char *)str1);
int current_length = strlen((const char *)str);
int original_length = current_length;
str[current_length] = 0x80;
str[current_length + 1] = '[=10=]';
char ic = str[current_length];
current_length++;
int ib = current_length % 64;
int i, j;
if(ib<56)
ib = 56-ib;
else
ib = 120 - ib;
for(i=0;i < ib;i++)
{
str[current_length]=0x00;
current_length++;
}
str[current_length + 1]='[=10=]';
for(i=0;i<6;i++)
{
str[current_length]=0x0;
current_length++;
}
str[current_length] = (original_length * 8) / 0x100 ;
current_length++;
str[current_length] = (original_length * 8) % 0x100;
current_length++;
str[current_length+i]='[=10=]';
int number_of_chunks = current_length/64;
unsigned long int word[80];
for(i=0;i<number_of_chunks;i++)
{
for(j=0;j<16;j++)
{
word[j] =
str[i*64 + j*4 + 0] * 0x1000000 + str[i*64 + j*4 + 1] * 0x10000 +
str[i*64 + j*4 + 2] * 0x100 + str[i*64 + j*4 + 3];
}
for(j=16;j<80;j++)
{
word[j] = rotateleft((word[j-3] ^ word[j-8] ^ word[j-14] ^ word[j-16]),1);
}
a = h0;
b = h1;
c = h2;
d = h3;
e = h4;
for(int m=0;m<80;m++)
{
if(m<=19)
{
f = (b & c) | ((~b) & d);
k = 0x5A827999;
}
else if(m<=39)
{
f = b ^ c ^ d;
k = 0x6ED9EBA1;
}
else if(m<=59)
{
f = (b & c) | (b & d) | (c & d);
k = 0x8F1BBCDC;
}
else
{
f = b ^ c ^ d;
k = 0xCA62C1D6;
}
temp = (rotateleft(a,5) + f + e + k + word[m]) & 0xFFFFFFFF;
e = d;
d = c;
c = rotateleft(b,30);
b = a;
a = temp;
}
h0 = h0 + a;
h1 = h1 + b;
h2 = h2 + c;
h3 = h3 + d;
h4 = h4 + e;
}
h0 = endian_reverse(h0);
h1 = endian_reverse(h1);
h2 = endian_reverse(h2);
h3 = endian_reverse(h3);
h4 = endian_reverse(h4);
printf("\n\n");
printf("Hash: %x %x %x %x %x",h0, h1, h2, h3, h4);
printf("\n\n");
}
int main()
{
SHA1((unsigned char *)"abc");
return 0;
}
SHA-1 ("abc"):
结果
Hash: f7370736 e388302f 15815610 1ccacd49 e7649bb6
正确(实际)
Hash: a9993e36 4706816a ba3e2571 7850c26c 9cd0d89d
我是否正确或在正确的位置进行字节序转换?
Am I doing my endianness conversion correctly or in the correct spot?
当 unsigned
不是 32 位时,字节顺序转换 endian_reverse(()
不正确。
未在正确位置使用字节顺序转换。不需要 Endian 转换。
存在其他问题。
代码是 假设 unsigned long int
是 32 位。当 unsigned long int
是 64 位时,我可以得到与 OP 相同的答案。
节省时间:这里没有理由使用松散类型。使用 uint32_t, uint8_t
。对于数组大小和字符串长度,请使用 size_t
。避免带符号的类型和常量。
通过更改 unsigned long
--> uint32_t
并删除 h0 = endian_reverse(h0); ... h7 = endian_reverse(h7);
我想出了
Hash: a9993e36 4706816a ba3e2571 7850c26c 9cd0d89d
其他建议。
64的倍数
size
in str = malloc(size)
应该是 64
保持在 64 的倍数范围内
str[current_length+i]='[=23=]';
可以写外分配
备用尺寸存储
适用于 所有 size_t
值最多为 264-3 - 1.
size_t current_length = ...
// append length in bits
uint64_t current_length_bits = current_length;
current_length_bits *= 8;
for (i = 8; i > 0; ) {
i--;
str[current_length + i] = (unsigned char) current_length_bits;
current_length_bits >>= 8;
}
current_length += 8;