【问题标题】:SHA-1 in C on little-endian environment小端环境中 C 中的 SHA-1
【发布时间】:2019-04-25 17:05:57
【问题描述】:

我遇到的大多数 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] = '\0';

    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]='\0';

    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]='\0';

    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

我的字节序转换是否正确或在正确的位置?

【问题讨论】:

  • 我的输出不正确。对我来说,似乎一切都是正确的,但显然某处存在错误。
  • 我已经在最后澄清了这个问题。但我想缩小范围:“我的字节顺序转换正确吗?”
  • 好的,希望现在已经把主体清理干净了。
  • 我见过的大多数实现都是针对大端和小端的。试试这个链接,它适用于小端 github.com/B-Con/crypto-algorithms/blob/master
  • 顺便说一下,字节序在重新解释内存时发挥作用,而不仅仅是一般算术(包括按位运算)。这段代码没有重新解释,所以应该没关系。

标签: c endianness sha


【解决方案1】:

我的字节序转换是否正确或在正确的位置?

unsigned 不是 32 位时,字节序转换 endian_reverse(() 不正确。

字节顺序转换未在正确位置使用。不需要字节序转换。

存在其他问题。


代码假设 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 的倍数

str = malloc(size) 中的size 应该是 64 的倍数

保持在 64 的倍数内

str[current_length+i]='\0'; 可以写外分配。

备用尺寸存储

适用于 所有 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;

【讨论】:

  • 你是在大端机器上编译吗?
  • @xornoz 如我所见-机器字节序在此代码中没有任何区别。各种类型的错误大小会弄乱代码。您认为哪些代码行会因为机器字节序而有所不同?
  • @xornoz 你如何看待h0 的字节序在这里很重要?无论机器字节序如何,常量都需要具有0x67452301 的值。
  • 那已经转换为大端。标准常量如下:H0 = 67452301 H1 = EFCDAB89 H2 = 98BADCFE H3 = 10325476 H4 = C3D2E1F0. 参见此处:tools.ietf.org/html/rfc3174#page-6 (§6.1.1)
  • 其实我错了。您可以使用&gt;&gt; 运算符将h0, h1... 写入缓冲区。这不需要字节序检查。一些实现使用memcpy, fwrite 等将&amp;h0 中的内存写入缓冲区,这需要翻转little-endian 的字节顺序。后一种方法复杂得没有意义,而且在很多地方也有使用。
猜你喜欢
  • 2013-03-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-14
  • 2017-02-26
  • 2016-02-03
  • 2012-06-14
  • 1970-01-01
相关资源
最近更新 更多