Pomocí instrukcí AVX2 jsem naprogramoval funkci pro šifrování
HMAC,
SHA-1. 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-1Výsledek by měl být:
effcdf6ae5eb2fa2d27416d5f184df9c259a7c79
spočítá ale:
974020ac606d38554f91a4786d23aec3cf4d77de
viz
HMAC generator zdeSHA1 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);
}