Почему Sha1 на Arduino (Little Endian) без преобразования в Big Endian работает? - PullRequest
0 голосов
/ 17 июня 2019

Мне повезло сказать, что мой код для простого генератора хеша SHA1, кажется, работает хорошо. К сожалению, я знаю, что эта программа Arduino работает с Little Endianness, и описание метода для генерации хэша требует, чтобы исходная длина сообщения добавлялась как Big Endian integer.

  1. Это означает, что для сообщения char m[] = "Applecake" у меня будет 9 * 8 битов, выраженных как 64-разрядное целое число без знака, равное 0x0000 0000 0000 0048. Это означает, что при хранении в Little Endian память будет выглядеть так: 0x0048 0000 0000 0000.

  2. Как описано в Разделе 4 RFC 3174 Шаг c) Я должен

    Получить 2-х словное представление l, количество бит в исходном сообщении. Если l <2 ^ 32, тогда первое слово - все нули. Добавьте эти два слова к дополненному сообщению. </p>

    Итак, с моей памятью, как описано выше, мне нужно сначала преобразовать ее в Big Endian, а затем добавить младшие 32 бита к дополненному сообщению.

Проблема в том, что если я преобразую Endianness длины, которую я знаю как Little Endian, я получу неправильное заполнение и, следовательно, неправильный хеш.

Почему мой код работает без преобразования Endianness?
Какие ограничения есть в моем коде относительно совместимости между различными Arduinos, микроконтроллерами и компиляторами?


// 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;
}

1 Ответ

1 голос
/ 17 июня 2019
// 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);
}

Это ставит старшее 32-битное значение первым, а младшее 32-битное значение - вторым. Это половина причины, по которой ваш код работает.

Другая половина состоит в том, что, хотя 32-разрядные значения представлены в формате с прямым порядком байтов, вы читаете их значения на платформе с прямым порядком байтов. Это всегда даст вам правильное значение. Вы никогда не пытаетесь получить доступ к отдельным байтам 32-битных значений, поэтому, какие байты идут туда, где нет разницы.

...