Какая самая быстрая реализация SHA1?

#c #boost #openssl #crypto

#c #повышение #openssl #крипто

Вопрос:

Я ищу самую быструю реализацию SHA1, поскольку мне приходится вычислять ее миллионы раз. Я попробовал boost::uuids::detail::sha1 и OpenSSL SHA1 и обнаружил, что OpenSSL в 2,5 раза быстрее boost. Я также проверил Crypto , который намного медленнее, чем два других. Вот что я делаю, чтобы проверить их производительность:

OpenSSL SHA1:

 #include "openssl/sha.h"

void sha1_ossl (const unsigned char* data) {
    unsigned char  hash[20];
    for (long i=0; i<100000000;   i) {
      SHA1(data, 64, hash);
    
      if ((unsigned int)hash[0]==0 amp;amp; (unsigned int)hash[1]==0 amp;amp; (unsigned int)hash[2]==0 amp;amp; (unsigned int)hash[3]==0)
          break;
    }
}
  

Boost::SHA1:

 #include <boost/uuid/detail/sha1.hpp>

void sha1_boost (const unsigned char* data) {
    boost::uuids::detail::sha1 sha1;
    unsigned hash[5];
    for (long i=0; i<100000000;   i) {
        sha1.process_bytes(data, 64);
        sha1.get_digest(hash);
        sha1.reset();
        if (hash[0]==0) break;
    }
}
  

CryptoPP::SHA1:

 #include <cryptopp/sha.h>
#include <cryptopp/hex.h>

void sha1_cryptoPP (const unsigned char* data) {
    std::string data_s (reinterpret_cast<char const*>(data));
    std::string hash_hex;
    CryptoPP::SHA1 sha1;
    for (long i=0; i<100000000;   i) {
        CryptoPP::StringSource ss(data_s, true, new CryptoPP::HashFilter(sha1, new CryptoPP::HexEncoder(new CryptoPP::StringSink(hash_hex))));
        if (hash_hex.starts_with("00000000")) break;
    }
}
  

Затем я тестирую эти функции со случайными данными:

 int main() {
  const unsigned char data[65] = "tJQVfvcjGMNIvJfowXBjmSRcKtSjCcyQvaAdakfEJtgSNZHnOHCjkzGFwngiLFPm";
  sha1_boost (data);
  sha1_ossl (data);
  sha1_cryptoPP (data);
}
  

Результаты производительности

Я скомпилировал все коды с g -O3 -std=c 2a , чтобы получить следующие результаты. Я обнаружил, что OpenSSL быстрее других реализаций, а Crypto — самый медленный:

Производительность различных алгоритмов SHA1

Вопросы

  • Какая самая быстрая реализация SHA1?
  • Как я могу улучшить свою функцию Crypto ?

Приветствуется любая обратная связь для повышения производительности.

Комментарии:

1. Эта ссылка выглядит интересно. Кажется, что OpenSSL настолько хорош, насколько это возможно.

2. @PaulSanders Благодарит Пола. Ссылка очень полезна. Кажется, что OpenSSL — самая быстрая реализация.

3. Некоторые библиотеки защищены от атак по побочным каналам (по времени), а некоторые нет?

4. У вас есть 2 вопроса. Один запрашивает рекомендации для библиотеки, другой лучше подходит для code-review

5. Мои последние эксперименты подтвердили, что openssl был самым быстрым среди нескольких (включая Crypto и некоторые реализации lose на C с одним исходным кодом, которые я забыл, какие именно)

Ответ №1:

Мои последние эксперименты подтвердили, что openssl был самым быстрым среди нескольких (включая Crypto и некоторые реализации lose на C с одним исходным кодом, которые я забыл, какие именно)

Re: части вопроса, связанные с типом проверки кода:

  • «реализация» boost взята из пространства имен detail и на нее не следует полагаться.

  • Криптопроцессор может извлечь выгоду из использования процедурного интерфейса вместо того, чтобы каждый раз динамически составлять конвейер. В частности, вам не следует преобразовывать в строку для проверки первых n байтов дайджеста. Вероятно, это доминирующая часть среды выполнения из-за повторяющихся распределений.

    Соблюдение процедурного интерфейса также может позволить вам использовать элемент reset / clear (цитирование из памяти)


     if ((unsigned int)hash[0] == 0 amp;amp; (unsigned int)hash[1] == 0 amp;amp;
        (unsigned int)hash[2] == 0 amp;amp; (unsigned int)hash[3] == 0)
        break;
  

Должен был быть простой

     if (!(hash[0] || hash[1] || hash[2] || hash[3]))
        break;
  

Или даже

     if (!std::any_of(hash 0, hash 4, std::identity{}))
        break;
  

Улучшенный тестовый код

Включает в себя некоторые из вышеперечисленных и некоторые другие (в основном в хорошем стиле, избегая ошибок указателя, показывая эффективные итерации и позволяя dump проверять дайджесты на точность):

В режиме реального времени в Compiler Explorer

 #include "openssl/sha.h"
#include <boost/uuid/detail/sha1.hpp>
#include <algorithm>
#include <iostream>
#include <iomanip>
using byte = unsigned char;

#ifndef ONLINE_DEMO
auto constexpr iterations = 100'000'000;
#else
auto constexpr iterations = 10000;
#endif

static void dump(byte const (amp;a)[20]) {
    for (unsigned b : a) {
        std::cout << std::setw(2) << std::setfill('0') << std::hex << b;
    }
    std::cout << std::dec << std::endl;
}

static void dump(uint32_t const (amp;a)[5]) {
    for (auto b : a) {
        std::cout << std::setw(8) << std::setfill('0') << std::hex << b;
    }
    std::cout << std::dec << std::endl;
}

long sha1_ossl(std::string_view data) {
    byte hash[20];
    for (long i = 0; i < iterations;   i) {
        SHA1(reinterpret_cast<byte const*>(data.data()), data.size(), hash);

        //dump(hash);
        if (!std::any_of(hash 0, hash 4, std::identity{}))
            return i;
    }
    return iterations;
}

long sha1_boost(std::string_view data) {
    boost::uuids::detail::sha1 sha1;
    uint32_t hash[5];
    for (long i = 0; i < iterations;   i) {
        sha1.process_bytes(reinterpret_cast<byte const*>(data.data()), data.size());
        sha1.get_digest(hash);
        sha1.reset();
        //dump(hash);
        if (hash[0] == 0)
            return i;
    }
    return iterations;
}

#ifndef ONLINE_DEMO
#include <cryptopp/hex.h>
#include <cryptopp/sha.h>
long sha1_cryptoPP(std::string_view data) {
    byte digest[20];

    CryptoPP::SHA1 sha1;
    for (long i = 0; i < iterations;   i) {
        sha1.Restart();
        sha1.Update(reinterpret_cast<byte const*>(data.data()), data.size());
        sha1.Final(digest);

        //dump(digest);
        if (!std::any_of(digest 0, digest 4, std::identity{}))
            return i;
    }
    return iterations;
}
#endif

#include <chrono>
using namespace std::chrono_literals;

int main() {
    static auto now = std::chrono::high_resolution_clock::now;
    constexpr static std::string_view data =
        "tJQVfvcjGMNIvJfowXBjmSRcKtSjCcyQvaAdakfEJtgSNZHnOHCjkzGFwngiLFPm";

    auto timed = [](auto caption, auto f) {
        auto const start = now();
        auto n = f(data);
        std::cout << caption << ": " << n << " in " << (now()-start)/1.0ms << "msn";
    };

    timed("sha1_boost", sha1_boost);
    timed("sha1_ossl", sha1_ossl);
#ifndef ONLINE_DEMO
    timed("sha1_cryptoPP", sha1_cryptoPP);
#endif
}
  

С принтами:

 sha1_boost: 100000000 in 85660.5ms
sha1_ossl: 100000000 in 24652.6ms
sha1_cryptoPP: 100000000 in 34921.3ms
  

Или онлайн (там нет крипто ):

 sha1_boost: 10000 in 8.71938ms
sha1_ossl: 10000 in 2.32025ms
  

Это значительное улучшение, но победитель тот же.

Комментарии:

1. У меня была небольшая ошибка в timed , возможно, вы поняли, что тайминги были кумулятивными : (Исправлено сейчас, чтобы это было менее запутанным!

2. Вы утверждаете, что OpenSSL «был самым быстрым среди»… но из проводника компилятора я вижу: sha1_boost: 10000 за 9.2606 мс, sha1_ossl: 10000 за 15.9871 мс. Я пытаюсь выяснить, какая из этих библиотек предлагает лучшую реализацию SHA-256 с точки зрения скорости и использования процессора :/

3. @NorbertBoros скорость зависит от компиляторов, флагов, архитектур и т.д. Итак, да, всегда проводите свое собственное профилирование. Удивительно, что результаты в Compiler Explorer изменились. Я думаю, это показывает, что вам нужно повторять тесты при изменении версий. Вы можете увидеть результаты от 2020/08/20 в моем посте, которые были явно > в 3 раза быстрее для boost.

4. @NorbertBoros Я только что повторил тесты на моем процессоре i7-3770K с частотой 3,50 ГГц и неизменно получал одинаковые тайминги: (100 ‘000’000) sha1_boost: 78.8104 с, sha1_ossl: 21.8883 с, sha1_cryptoPP: 30.4004 с. Это libssl1.1: amd64, boost 1.75, GCC 10, c 17 и libcrypto 6 (= 5.6.4-8), все на Ubuntu 18.04 (я знаю, довольно старая, но не должна иметь большого значения)

5. Да, я думаю, это действительно, я только что провел свой собственный тест на AMD Ryzen, и OpenSSL потрясающий по сравнению с crypto с SHA256. Я предполагаю, что победителем является OpenSSL во что бы то ни стало, потому что он просто кажется лучше оптимизированным.