From 8048b5e47cf6d375903685ce945bc9764d531cb3 Mon Sep 17 00:00:00 2001 From: Floris Bos Date: Sat, 23 May 2020 17:50:59 +0200 Subject: [PATCH] Use accelerated hashing for verification Modern CPUs have special instructions to accelerate computing SHA hashes. The Qt QCryptographicHash code is standard C, so not taking advantage of those though. Outsource the hashing to OpenSSL that does. Shaves off some seconds during verification stage. --- CMakeLists.txt | 9 ++++---- acceleratedcryptographichash.cpp | 37 ++++++++++++++++++++++++++++++++ acceleratedcryptographichash.h | 25 +++++++++++++++++++++ downloadextractthread.h | 2 +- downloadthread.cpp | 3 +++ downloadthread.h | 4 ++-- 6 files changed, 73 insertions(+), 7 deletions(-) create mode 100644 acceleratedcryptographichash.cpp create mode 100644 acceleratedcryptographichash.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 4453e15..25b338e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,7 @@ set(CMAKE_AUTORCC ON) # Adding headers explicity so they are displayed in Qt Creator set(HEADERS config.h imagewriter.h networkaccessmanagerfactory.h nan.h drivelistitem.h drivelistmodel.h driveformatthread.h powersaveblocker.h - downloadthread.h downloadextractthread.h dependencies/mountutils/src/mountutils.hpp) + downloadthread.h downloadextractthread.h acceleratedcryptographichash.h dependencies/mountutils/src/mountutils.hpp) # Add 3rd-party dependencies if (APPLE) @@ -60,8 +60,9 @@ endif() set(SOURCES "main.cpp" "imagewriter.cpp" "networkaccessmanagerfactory.cpp" "drivelistitem.cpp" "drivelistmodel.cpp" "downloadthread.cpp" "downloadextractthread.cpp" - "driveformatthread.cpp" "powersaveblocker.cpp" "qml.qrc") + "driveformatthread.cpp" "powersaveblocker.cpp" "acceleratedcryptographichash.cpp" "qml.qrc") +find_package(OpenSSL REQUIRED) find_package(Qt5 COMPONENTS Core Quick Widgets LinguistTools REQUIRED) #qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} i18n/rpi-imager_nl.ts i18n/rpi-imager_zh_cn.ts) @@ -233,5 +234,5 @@ else() install(FILES linux/rpi-imager.desktop DESTINATION share/applications) endif() -include_directories(${CURL_INCLUDE_DIR} ${LibArchive_INCLUDE_DIR}) -target_link_libraries(${PROJECT_NAME} Qt5::Core Qt5::Quick Qt5::Widgets ${CURL_LIBRARIES} ${LibArchive_LIBRARIES} ${ATOMIC_LIBRARY} ${EXTRALIBS}) +include_directories(${CURL_INCLUDE_DIR} ${LibArchive_INCLUDE_DIR} ${OPENSSL_INCLUDE_DIR}) +target_link_libraries(${PROJECT_NAME} Qt5::Core Qt5::Quick Qt5::Widgets ${CURL_LIBRARIES} ${LibArchive_LIBRARIES} ${OPENSSL_LIBRARIES} ${ATOMIC_LIBRARY} ${EXTRALIBS}) diff --git a/acceleratedcryptographichash.cpp b/acceleratedcryptographichash.cpp new file mode 100644 index 0000000..cade939 --- /dev/null +++ b/acceleratedcryptographichash.cpp @@ -0,0 +1,37 @@ +/* + * Use OpenSSL for hashing as their code is more optimized than Qt's + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright (C) 2020 Raspberry Pi (Trading) Limited + */ + +#include "acceleratedcryptographichash.h" + +AcceleratedCryptographicHash::AcceleratedCryptographicHash(QCryptographicHash::Algorithm method) +{ + if (method != QCryptographicHash::Sha256) + throw std::runtime_error("Only sha256 implemented"); + + SHA256_Init(&_sha256); +} + +AcceleratedCryptographicHash::~AcceleratedCryptographicHash() +{ +} + +void AcceleratedCryptographicHash::addData(const char *data, int length) +{ + SHA256_Update(&_sha256, data, length); +} + +void AcceleratedCryptographicHash::addData(const QByteArray &data) +{ + addData(data.constData(), data.size()); +} + +QByteArray AcceleratedCryptographicHash::result() +{ + unsigned char binhash[SHA256_DIGEST_LENGTH]; + SHA256_Final(binhash, &_sha256); + return QByteArray((char *) binhash, sizeof binhash); +} diff --git a/acceleratedcryptographichash.h b/acceleratedcryptographichash.h new file mode 100644 index 0000000..4e87227 --- /dev/null +++ b/acceleratedcryptographichash.h @@ -0,0 +1,25 @@ +#ifndef ACCELERATEDCRYPTOGRAPHICHASH_H +#define ACCELERATEDCRYPTOGRAPHICHASH_H + +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright (C) 2020 Raspberry Pi (Trading) Limited + */ + +#include +#include "openssl/sha.h" + +class AcceleratedCryptographicHash +{ +public: + explicit AcceleratedCryptographicHash(QCryptographicHash::Algorithm method); + virtual ~AcceleratedCryptographicHash(); + void addData(const char *data, int length); + void addData(const QByteArray &data); + QByteArray result(); + +protected: + SHA256_CTX _sha256; +}; + +#endif // ACCELERATEDCRYPTOGRAPHICHASH_H diff --git a/downloadextractthread.h b/downloadextractthread.h index 0fc7a07..1d7e6bf 100644 --- a/downloadextractthread.h +++ b/downloadextractthread.h @@ -41,7 +41,7 @@ protected: std::mutex _queueMutex; std::condition_variable _cv; bool _ethreadStarted, _isImage; - QCryptographicHash _inputHash; + AcceleratedCryptographicHash _inputHash; int _activeBuf; bool _writeThreadStarted; QFuture _writeFuture; diff --git a/downloadthread.cpp b/downloadthread.cpp index 2ed035b..8a62e4e 100644 --- a/downloadthread.cpp +++ b/downloadthread.cpp @@ -600,6 +600,8 @@ bool DownloadThread::_verify() char *verifyBuf = (char *) qMallocAligned(IMAGEWRITER_VERIFY_BLOCKSIZE, 4096); _lastVerifyNow = 0; _verifyTotal = _file.pos(); + QElapsedTimer t1; + t1.start(); if (!_firstBlock) { @@ -627,6 +629,7 @@ bool DownloadThread::_verify() } qFreeAligned(verifyBuf); + qDebug() << "Verify done in" << t1.elapsed() / 1000.0 << "seconds"; qDebug() << "Verify hash:" << _verifyhash.result().toHex(); if (_verifyhash.result() == _writehash.result() || !_verifyEnabled || _cancelled) diff --git a/downloadthread.h b/downloadthread.h index 98b3931..896ecf6 100644 --- a/downloadthread.h +++ b/downloadthread.h @@ -9,12 +9,12 @@ #include #include #include -#include #include #include #include #include #include +#include "acceleratedcryptographichash.h" #ifdef Q_OS_WIN #include "windows/winfile.h" @@ -172,7 +172,7 @@ protected: #endif QFile _cachefile; - QCryptographicHash _writehash, _verifyhash; + AcceleratedCryptographicHash _writehash, _verifyhash; }; #endif // DOWNLOADTHREAD_H