【问题标题】:A memory-efficient SHA1 implementation一种节省内存的 SHA1 实现
【发布时间】:2011-08-03 14:03:12
【问题描述】:

我正在使用一个非常严格的嵌入式处理器,它只有 128 字节的内存。我想在它上面实现 SHA1。 RFC3174 在“方法 2”中描述了一种实现 SHA1 的方法,它不需要分配 80 个 32 位字的数组(这在 320 字节处显然是不切实际的),并且似乎应该是可在我的处理器上使用。但是,我无法找到“方法 2”的任何实现,并且 RFC 中的示例代码仅实现了默认方法。

有人知道 C 或 C++ 中 SHA1 的内存高效实现吗?

【问题讨论】:

  • 方法 2 建议您需要 16 个 4 字节(32 位)字,即 64 字节。当然,SHA1 算法需要更多的存储空间。所以你只剩下不到 64 字节的 RAM 来保存被加密的消息;您可能还必须发送加密结果。你有什么,适合这么少的存储空间,需要 SHA1 加密?
  • “128 字节 RAM”?!我的烤面包机有更多!
  • @Greg:不,它不是重复的。您的链接想要计算文件的哈希值,而不必一次将其全部加载到内存中。这是微不足道的——所有实现都支持它。但是这个 OP 想要实现 SHA-1 算法,而不必从算法规范中构建内部 320 字节的 W 数组。这是可以做到的,但并非易事。
  • @Nick:哇,现在是 HMAC-SHA-1。那是一个不同的故事。如果你能在 128 字节内做到这一点,那么你就是一个比我更好的程序员。

标签: c++ c hash cryptography sha1


【解决方案1】:

你应该可以快速将方法1源适配到方法2。要改变的函数是方法1中的Sha1ProcessMessageBlock()。从消息中初始化w[0:15],然后循环0到79,这里你只做w[] 迭代 16 后的操作,临时计算取决于 ts 值(0-19 使用一个,20-39 使用另一个,等等)。重要的是要记住在寻址 w[] 数组时使用 index%16index & 0x0f

快速修改将是这样的(仔细检查对w 的所有访问,以确保我没有错过t & 0x0f):

void SHA1ProcessMessageBlock(SHA1Context *context)
{
    const uint32_t K[] =    {       /* Constants defined in SHA-1   */
                            0x5A827999,
                            0x6ED9EBA1,
                            0x8F1BBCDC,
                            0xCA62C1D6
                            };
    int           t;                 /* Loop counter                */
    uint32_t      temp;              /* Temporary word value        */
    uint32_t      W[16];             /* Word sequence               */
    uint32_t      A, B, C, D, E;     /* Word buffers                */

    /*
     *  Initialize the first 16 words in the array W. You can move this to your
     *  context.
     */
    for(t = 0; t < 16; t++)
    {
        W[t] = context->Message_Block[t * 4] << 24;
        W[t] |= context->Message_Block[t * 4 + 1] << 16;
        W[t] |= context->Message_Block[t * 4 + 2] << 8;
        W[t] |= context->Message_Block[t * 4 + 3];
    }


    A = context->Intermediate_Hash[0];
    B = context->Intermediate_Hash[1];
    C = context->Intermediate_Hash[2];
    D = context->Intermediate_Hash[3];
    E = context->Intermediate_Hash[4];

    for(t = 0; t < 80; t++) {
        if (t >= 16) {
            W[t&0xf] = SHA1CircularShift(1,W[(t-3)&0xf] ^ W[(t-8)&0xf] ^ W[(t-14)&0xf] ^ W[t&0xf]);

        }

        if (t<20) {
            temp =  SHA1CircularShift(5,A) +
                    ((B & C) | ((~B) & D)) + E + W[t&0xf] + K[0];
        }
        else if (t<40) {
            temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t&0xf] + K[1];
        }
        else if (t < 60) {
            temp = SHA1CircularShift(5,A) +
                   ((B & C) | (B & D) | (C & D)) + E + W[t&0xf] + K[2];
        }
        else {
            temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t&0xf] + K[3];
        }
        E = D;
        D = C;
        C = SHA1CircularShift(30,B);
        B = A;
        A = temp;
    }

    context->Intermediate_Hash[0] += A;
    context->Intermediate_Hash[1] += B;
    context->Intermediate_Hash[2] += C;
    context->Intermediate_Hash[3] += D;
    context->Intermediate_Hash[4] += E;

    context->Message_Block_Index = 0;
}

仍有待节省:摆脱堆栈上的 W[] 数组,并将其放入使用您获得的数据预先初始化的上下文中。

此外,在调用此函数之前,您需要进行大量预处理。例如,如果您的所有消息都小于 55 字节,您可以将其放入 W 数组中,添加填充,并立即处理。如果没有,您将不得不调用 process 两次:首先使用部分填充的输入,然后再使用填充的其余部分,等等。这种事情将是非常特定于应用程序的,我怀疑您是否能够找到为您执行此操作的代码。

顺便说一句,上面的代码直接改编自您链接中的类型 1 源代码。如果您尝试进一步优化它,您可能会从中挤出更多。

我想不出任何节省中间哈希的方法,因此您总共需要 108 个字节(如果计数器也在 RAM 中,则为 109 个字节),其中 24 个是此函数的本地字节,并且可以在其他地方重复使用——只要它们也是临时的。所以做自己想做的事是非常困难的。


编辑:如果您的所有消息都小于 55 个字节,您可以通过摆脱 intermediate_hash[] 存储在您的上下文中再节省 20 个字节。只需从常量初始化 A-E,然后在末尾添加常量。最后,不要将它们存储在单独的变量中,而是在此函数结束时覆盖您的输入。

【讨论】:

  • 谢谢!我已经在平面上独立实现了初始优化,使用来自 RFC 的基本源(例如,使用许多小循环而不是您在上面使用的一个大循环)。要记住的另一件事是,我使用的处理器具有相当数量的寄存器(32 个 8 字节寄存器),因此希望编译器可以将一些工作转移到寄存器中,从而释放堆栈。
  • @Nick 对于许多小循环方法,需要注意的一件事是 W 值是临时的。这就是为什么一切都陷入同一个循环的原因。您计算 W[t],并立即将其用于临时变量。如果您指的是大循环,但是为了摆脱大的 if 链而数到 0-20、20-40 等,那么从速度上讲这可能是一个好主意,但它可能会浪费一些 ROM空间。
  • 我可以看到它会导致生成更多指令,但我不确定这与 W 的临时性有何关系。您能详细说明一下吗?
  • 如果按照我建议的方式进行操作(如 0-20、20-40 等),您可能不需要警告。一个容易掉入的陷阱是试图在不同的循环中分离出 W[t] 计算和 AE 计算(实际上,试图从方法 1 实现中保持相同的循环),这显然是行不通的,因为我们一直在覆盖循环中的 W 数组中的元素。对我来说这可能是不必要的评论。
【解决方案2】:

我已经为几个内存受限的环境实施了 SHA-1。你可以通过

DWORD W[16] ;        // instead of H[80]
DWORD H[5] ;         // Intermediate hash value
DWORD BitCount[2] ;  // Probably a single DWORD is enough here

加上几个字节的内务处理。 W 作为循环缓冲区即时更新,而不是在每一轮开始时生成。

【讨论】:

  • 对 - 这几乎就是方法 2 中描述的内容。是否有现有的实现可供我使用,还是我必须自己实现?
  • @Nick:如果你不告诉我们你使用的是什么处理器,我们怎么知道?
  • @TonyK 我要求在 C 中实现;处理器考虑不应该是一个巨大的组成部分。尽管如此 - 这是一个 Atmel ATtiny24。
  • @Nick:很抱歉不得不把这个告诉你,但你必须用汇编语言来编程。
  • @TonyK 最终可能会是这样,但我不认为这是已成定局。
【解决方案3】:

工作示例:

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string>

using namespace std;

unsigned CircularShift(int bits, unsigned word)
{
    return ((word << bits) & 0xFFFFFFFF) | ((word & 0xFFFFFFFF) >> (32-bits));
}

int main(void)
{
    string mess;
    cin >> mess;
    unsigned int lm = mess.length();
    unsigned int lmb = lm*8;
    unsigned char *messc;
    messc=(unsigned char*)malloc((sizeof(unsigned char))*64);

    for (unsigned short int i =0;i<64;i++)
    {
        messc[i]=char(0x00);
    }
    for(int i=0;i<mess.length();i++)
    {
        messc[i]=mess[i];
    }
    messc[lm]=(unsigned char)128;
    messc[56] = (lmb >> 24) & 0xFF;
    messc[57] = (lmb >> 16) & 0xFF;
    messc[58] = (lmb >> 8) & 0xFF;
    // messc[59] = (lmb) & 0xFF;
    messc[60] = (lmb >> 24) & 0xFF;
    messc[61] = (lmb >> 16) & 0xFF;
    messc[62] = (lmb >> 8) & 0xFF;
    messc[63] = (lmb) & 0xFF;
    for(int i =0 ;i<64;i++)
    {
        cout<< hex << (int)messc[i] << " ";
    }
    unsigned *H;
    H=(unsigned*)malloc(5*sizeof(unsigned));
    H[0]        = 0x67452301;
    H[1]        = 0xEFCDAB89;
    H[2]        = 0x98BADCFE;
    H[3]        = 0x10325476;
    H[4]        = 0xC3D2E1F0;
    const unsigned K[]={0x5A827999,0x6ED9EBA1,0x8F1BBCDC,0xCA62C1D6};
    int         t;
    unsigned    temp;
    unsigned    *W;
    unsigned    A, B, C, D, E;
    W=(unsigned*)malloc(80*sizeof(unsigned));
    unsigned char *messh;
    messh=(unsigned char*)malloc(64*sizeof(unsigned char));
    int k;
    for(t = 0; t < 16; t++)
    {
        W[t] = ((unsigned) messc[t * 4])<< 24; ;
        W[t] |= ((unsigned) messc[t * 4 + 1])<< 16;
        W[t] |= ((unsigned) messc[t * 4 + 2]) << 8;
        W[t] |= ((unsigned) messc[t * 4 + 3]);
    }
    for(t = 16; t < 80; t++)
    {
        W[t] = CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
    }

    A = H[0];
    B = H[1];
    C = H[2];
    D = H[3];
    E = H[4];

    for(t = 0; t < 20; t++)
    {
        temp = CircularShift(5,A) + ((B & C) | ((~B) & D)) + E + W[t] + K[0];
        temp &= 0xFFFFFFFF;
        E = D;
        D = C;
        C = CircularShift(30,B);
        B = A;
        A = temp;
    }

    for(t = 20; t < 40; t++)
    {
        temp = CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
        temp &= 0xFFFFFFFF;
        E = D;
        D = C;
        C = CircularShift(30,B);
        B = A;
        A = temp;
    }

    for(t = 40; t < 60; t++)
    {
        temp = CircularShift(5,A) +
                ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
        temp &= 0xFFFFFFFF;
        E = D;
        D = C;
        C = CircularShift(30,B);
        B = A;
        A = temp;
    }

    for(t = 60; t < 80; t++)
    {
        temp = CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
        temp &= 0xFFFFFFFF;
        E = D;
        D = C;
        C = CircularShift(30,B);
        B = A;
        A = temp;
    }

    H[0] = (H[0] + A) & 0xFFFFFFFF;
    H[1] = (H[1] + B) & 0xFFFFFFFF;
    H[2] = (H[2] + C) & 0xFFFFFFFF;
    H[3] = (H[3] + D) & 0xFFFFFFFF;
    H[4] = (H[4] + E) & 0xFFFFFFFF;

    cout <<"\nTHIS IS SHHHHHAAAAAAAAAAA\n";
    for(int i=0;i<5;i++)
    {
        cout << hex << H[i] << " ";
    }

    //Message_Block_Index = 0;


}

【讨论】:

    【解决方案4】:

    考虑到所有因素,看看您的要求,我认为您将不得不更改您的规格。要么更大的芯片,要么更简单的算法。即使实现 SHA-1(没有 HMAC)也是一个挑战,但它应该是可行的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-22
      • 2013-05-12
      相关资源
      最近更新 更多