Облегченный алгоритм хэш-функции 8 байт

Мне нужно извлечь 8-байтовый дайджест из строки переменной длины, поэтому я ищу такой алгоритм, который я буду реализовывать на c/C++. Это будет частью процедуры цифровой подписи на микроконтроллере, поэтому она должна быть:

  • записывается в нескольких строках кода, так как прошивка должна храниться как можно меньше;
  • низкое потребление ресурсов, особенно ОЗУ (желательно менее 100 байт);
  • достаточно сильный, чтобы изменить один символ в любой точке строки изменит общий дайджест.

Я посмотрел на существующие алгоритмы, такие как crc64, но они кажутся слишком тяжелыми для моей платформы.

4 ответов


Как сказал AndrewTomazos-Fathomling, невозможно сделать безопасный хэш в 64 битах, поэтому, если это ваше намерение, то мой совет остановка, возьмите книгу и прочитайте о криптографически безопасном хешировании.

Если вы не планируете использовать это как безопасный хэш, и вас не волнуют столкновения или атаки, то ответ, который он дал Вам, работает отлично, и вы можете настроить простые числа P1 и P2 по мере необходимости. Я дам вам другую альтернативу, которая позволит вам сделать помеченные хэширование и смешивает вещи больше.

// Disclaimer: I make no claims about the quality of this particular hash - it's 
// certainly not a cryptographically secure hash, nor should it *ever* be 
// construed as such. 

unsigned long long quickhash64(const char *str, unsigned long long mix = 0)
{ // set 'mix' to some value other than zero if you want a tagged hash          
    const unsigned long long mulp = 2654435789;

    mix ^= 104395301;

    while(*str)
        mix += (*str++ * mulp) ^ (mix >> 23);

    return mix ^ (mix << 37);
}

нет возможности сделать безопасный хэш в 64 битах. Даже SHA-1 на 160 бит считается теоретически сломанным. Вы должны использовать SHA2-256, если вы действительно заботитесь о надежности цифровой подписи. Если вы не заботитесь о безопасности и просто хотите, чтобы хэш-функция избегала неконфликтных столкновений, просто используйте следующее:

constexpr uint64 P1 = 7;
constexpr uint64 P2 = 31;

uint64 hash = P1;
for (const char* p = s; *p != 0; p++) {
    hash = hash * P2 + *p;
}

вот измененная версия 32-битной версии, которую я нашел в моих старых исходных файлах

static unsigned long long llhash(const char *str)
{
    unsigned long long hash = 5381;
    int c;

    while (c = *str++)
        hash = ((hash << 5) + hash) + c;

    return hash;
}

но хэширование всегда приводит к столкновениям. Конечно, некоторые алгоритмы лучше других.

Edit: Я нашел источник 32-битной версии:http://www.cse.yorku.ca / ~oz/hash.html


у меня было точно такое же требование, и я устроился на FNV-1A, после отклонения хэша SIP (реализовано bloomberg здесь).

Я нашел реализацию FNV здесь:

https://github.com/foonathan/string_id/blob/master/hash.hpp

что:

constexpr uint64_t fnv_basis = 14695981039346656037ull;
constexpr uint64_t fnv_prime = 1099511628211ull;

// FNV-1a 64 bit hash of null terminated buffer
uint64_t fnv1a_hash(const char* str, uint64_t hash = fnv_basis)
{
    return *str ? fnv1a_hash(str + 1, (hash ^ *str) * fnv_prime) : hash;
}

похоже, он зацикливается, используя хвостовую рекурсию. И условие остановки -