Pomocí instrukcí AVX2 jsem naprogramoval funkci pro šifrování HMAC, SHA1. Funkci SHA1 mi funguje pro vstupní text "Ahoj1234", který je menší než 64 bytů. Po doplnění funkce HMAC mi ale výpočet s testovacími daty nefunguje správně a nemohu přijít na to kde mám chybu.
Testovací data pro HMAC, SHA1 jsou:
key = "Jefe"
message = "what do ya want for nothing?"
viz Test Cases for HMAC-MD5 and HMAC-SHA-1
Výsledek by měl být:
effcdf6ae5eb2fa2d27416d5f184df9c259a7c79
spočítá ale:
974020ac606d38554f91a4786d23aec3cf4d77de
viz HMAC generator zde
SHA1 generator tady
Vlastní kód (Visual Studio 2022, C++ a AVX2)
32 bitová data ukládá Visual Studio 2022 ve tvaru little endian tj. string "Ahoj1234" je uložen jako "johA" a "4321" (lsb na první místo v paměti).
Specifikace HMAC požaduje uložení počtu bitů šifrované zprávy jako 64bitové big-endian číslo, pracuji s ním ale jako s 32 bitovým little-endian číslem (posledních 32 bitů v druhé AVX2 části 128 bitů a vyzkoušel jsem, že pro test SHA1 "Ahoj1234" to funguje správně.
#include <iostream>
#include <immintrin.h>
#include <chrono>
#include <stdint.h>
#include <stdio.h>
#include <intrin.h> // Byte swap, unsigned long _byteswap_ulong(unsigned long value);
#ifndef __cplusplus
#include <stdalign.h> // C11 defines _Alignas(). This header defines alignas()
#endif
using namespace std;
using namespace std::chrono;
uint32_t result;
__m256i sha1res; // výsledek SHA1
__m256i key;
__m256i indata[4]; //2x 512 bitů pro vstup HMAC
// HMAC block size block size of the SHA1 hash function is 64 bytes, output size pro SHA1 is 20 bytes
// https://en.m.wikipedia.org/wiki/HMAC
// SHA1
// https://en.wikipedia.org/wiki/SHA-1
void p256_hex_u32(__m256i in) {
alignas(32) uint32_t v[8];
_mm256_maskstore_epi32((int*)v, _mm256_setr_epi32(-1, -1, -1, -1, -1, 0, 0, 0), in);
printf("v8_u32: %x %x %x %x %x\n", v[0], v[1], v[2], v[3], v[4]);
}
inline uint32_t rotl32_30(uint32_t value) {
return value << 30 | value >> 2;
}
inline uint32_t rotl32_5(uint32_t value) {
return value << 5 | value >> 27;
}
inline uint32_t rotl32_1(uint32_t value) {
return value << 1 | value >> 31;
}
uint32_t rotl32(uint32_t value, unsigned int count) {
return value << count | value >> (32 - count);
}
void SHA1(__m256i* indata) {
uint32_t pole[80];
__m256i data;
uint32_t h0 = 0x67452301, a = h0;
uint32_t h1 = 0xEFCDAB89, b = h1;
uint32_t h2 = 0x98BADCFE, c = h2;
uint32_t h3 = 0x10325476, d = h3;
uint32_t h4 = 0xC3D2E1F0, e = h4;
for (int k = 0; k < 2; k++) {
data = _mm256_load_si256(indata + k * 2);
_mm256_store_si256((__m256i*)pole, data);
data = _mm256_load_si256(indata + k * 2 + 1);
_mm256_store_si256((__m256i*)pole + 1, data );
uint32_t temp;
for (int i = 0; i < 80; i++) {
if (i > 15) {
pole[i] = rotl32_1((pole[i - 3] ^ pole[i - 8] ^ pole[i - 14] ^ pole[i - 16]));
}
temp = rotl32_5(a) + e + pole[i];
if (i < 20) {
temp += ((b & c) | ((~b) & d)) + 0x5A827999;
}
else if (i < 40) {
temp += (b ^ c ^ d) + 0x6ED9EBA1;
}
else if (i < 60) {
temp += ((b & c) | (b & d) | (c & d)) + 0x8F1BBCDC;
}
else {
temp += (b ^ c ^ d) + 0xCA62C1D6;
}
e = d;
d = c;
c = rotl32_30(b);
b = a;
a = temp;
}
h0 += a;
h1 += b;
h2 += c;
h3 += d;
h4 += e;
}
sha1res = _mm256_setr_epi32(h0, h1, h2, h3, h4, 0, 0, 0);
}
int main() {
__m256i tmp;
auto start = high_resolution_clock::now();
//Pro test funkce SHA1 lze odkomentovat (a komentovat přiřazení pro Jefe..., nutno také změnit parametr pro načítání k, místo k < 2 na k < 1 a dát stopku za sha1res = ...
//sha1res = 8162b79671480fc441e2d54ee85dea77f43b5bc2
//viz odkaz na generátor SHA1 v úvodu
//key = _mm256_setr_epi32(0x41686f6a, 0x31323334, 0x80000000, 0, 0, 0, 0, 0); // "Ahoj1234"
//indata[0] = key;
//indata[1] = _mm256_setr_epi32(0, 0, 0, 0, 0, 0, 0, 0x40);
//key = _mm256_setr_epi32(_byteswap_ulong(0x4A656665), 0, 0, 0, 0, 0, 0, 0); // Jefe
key = _mm256_setr_epi32(0x4A656665, 0, 0, 0, 0, 0, 0, 0); // Jefe
indata[0] = _mm256_xor_si256(key, _mm256_set1_epi8(0x36));
indata[1] = _mm256_set1_epi8(0x36); // 0 XOR 0x36 = 0x36
//tmp = _mm256_setr_epi32(_byteswap_ulong(0x77686174), _byteswap_ulong(0x20646F20), _byteswap_ulong(0x79612077), _byteswap_ulong(0x616E7420), _byteswap_ulong(0x666F7220), _byteswap_ulong(0x6E6F7468), _byteswap_ulong(0x696E673F), 0); // "what do ya want for nothing?" 28 znaků
tmp = _mm256_setr_epi32(0x77686174, 0x20646F20, 0x79612077, 0x616E7420, 0x666F7220, 0x6E6F7468, 0x696E673F, 0); // "what do ya want for nothing?" 28 znaků
indata[2] = _mm256_or_si256(tmp, _mm256_setr_epi32(0, 0, 0, 0, 0, 0, 0, 0x80000000));
indata[3] = _mm256_setr_epi32(0, 0, 0, 0, 0, 0, 0, 736); //počet bitů pri ipad hashování = (64 + 28) * 8
SHA1(indata);
indata[0] = _mm256_xor_si256(key, _mm256_set1_epi8(0x5c));
indata[1] = _mm256_set1_epi8(0x5c); // 0 XOR 0x5c = 0x5c
indata[2] = _mm256_or_si256(sha1res, _mm256_setr_epi32(0, 0, 0, 0, 0, 0x80000000, 0, 0));
indata[3] = _mm256_setr_epi32(0, 0, 0, 0, 0, 0, 0, 672); //počet bitů pro opad hashování = (64 + 20) * 8
SHA1(indata);
auto stop = high_resolution_clock::now();
auto duration = duration_cast<microseconds>(stop - start);
printf("Time = %lli (us DEC)\n", duration.count());
p256_hex_u32(sha1res);
}