From 084a150cb6393faa3c1346f65f8a13e9a4d9cf74 Mon Sep 17 00:00:00 2001 From: Arne Schroeder Date: Sat, 7 Sep 2019 10:04:50 +0200 Subject: [PATCH] initial commit --- .clang-format | 6 ++ CMakeLists.txt | 54 +++++++++++++ inc/integretychecker.h | 33 ++++++++ inc/mainwindow.h | 26 +++++++ src/integretychecker.cpp | 160 +++++++++++++++++++++++++++++++++++++++ src/main.cpp | 80 ++++++++++++++++++++ src/mainwindow.cpp | 59 +++++++++++++++ ui/mainwindow.ui | 150 ++++++++++++++++++++++++++++++++++++ 8 files changed, 568 insertions(+) create mode 100644 .clang-format create mode 100644 CMakeLists.txt create mode 100644 inc/integretychecker.h create mode 100644 inc/mainwindow.h create mode 100644 src/integretychecker.cpp create mode 100644 src/main.cpp create mode 100644 src/mainwindow.cpp create mode 100644 ui/mainwindow.ui diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..caca0eb --- /dev/null +++ b/.clang-format @@ -0,0 +1,6 @@ +--- +BasedOnStyle: LLVM +ColumnLimit: '100' +MaxEmptyLinesToKeep: '2' + +... diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..aeaaa9e --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,54 @@ +cmake_minimum_required(VERSION 3.0.0) + +option(TESTS "Build all tests" OFF) + +project(IntegrityCheck) + +include(CPack) + +set(CPACK_PACKAGE_NAME "IntegrityCheck") +set(CPACK_PACKAGE_VENDOR "Arne Schröder") +set(CPACK_PACKAGE_VERSION_MAJOR 1) +set(CPACK_PACKAGE_VERSION_MINOR 0) +set(CPACK_PACKAGE_VERSION_PATCH 0) +set(CPACK_GENERATOR "TGZ") + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +set(CMAKE_CXX_STANDARD 17) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +set(CMAKE_AUTOUIC ON) + +find_package(Qt5 COMPONENTS Core Gui Widgets REQUIRED) + +set(SOURCES + src/main.cpp + src/mainwindow.cpp + src/integretychecker.cpp +) + +set(HEADERS + inc/mainwindow.h + src/integretychecker.cpp +) + +set(FORMS + ui/mainwindow.ui +) + +add_executable(${PROJECT_NAME} + ${SOURCES} + ${HEADERS} + ${FORMS} +) +target_include_directories(${PROJECT_NAME} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ./cryptopp/ +) +target_link_libraries(${PROJECT_NAME} + ${CMAKE_SOURCE_DIR}/cryptopp/libcryptopp.a + Qt5::Core + Qt5::Widgets +) diff --git a/inc/integretychecker.h b/inc/integretychecker.h new file mode 100644 index 0000000..0227068 --- /dev/null +++ b/inc/integretychecker.h @@ -0,0 +1,33 @@ +#pragma once + +#include "../cryptopp/rsa.h" +#include + +class IntegretyCheck { +public: + IntegretyCheck(); + IntegretyCheck(const std::string &appPath, const std::string &keyFile, + const std::string &hashList); + + bool loadKeyFile(const std::string &app); + bool saveKeyFile(const std::string &app); + bool generateKeyPair(); + + std::string loadHashList(const std::string &hashList); + std::string generateHashList(const std::string &app); + std::string generateFileHash(const std::string &filepath); + + void signHashList(std::string &hashList); + bool verifyHashList(const std::string &signedHashList); + + static const int KEY_SIZE; + static const std::string HASH_FILE; + static const std::string KEY_FILE; + static const std::string SIGNATURE_MARKER; + +private: + CryptoPP::RSA::PublicKey m_publicKey; + CryptoPP::RSA::PrivateKey m_privateKey; + std::string tmp; + CryptoPP::SecByteBlock tmpSec; +}; diff --git a/inc/mainwindow.h b/inc/mainwindow.h new file mode 100644 index 0000000..a3cf7c3 --- /dev/null +++ b/inc/mainwindow.h @@ -0,0 +1,26 @@ +#pragma once + +#include "../inc/integretychecker.h" +#include +#include + +namespace Ui { +class MainWindow; +} + +class AppWindow : public QMainWindow { + Q_OBJECT + +public: + explicit AppWindow(); + +public slots: + void onAppFolderSelect_click(); + void onPublicKeySelect_click(); + +private: + std::shared_ptr ui; + + const QString checkPublicKey(const QString &path); + IntegretyCheck integretyCheck; +}; diff --git a/src/integretychecker.cpp b/src/integretychecker.cpp new file mode 100644 index 0000000..c2d6c6a --- /dev/null +++ b/src/integretychecker.cpp @@ -0,0 +1,160 @@ +#include "../inc/integretychecker.h" + +#include "../cryptopp/base64.h" +#include "../cryptopp/files.h" +#include "../cryptopp/osrng.h" +#include "../cryptopp/rsa.h" + +#include +#include +#include +namespace fs = std::filesystem; + +const int IntegretyCheck::KEY_SIZE = 2048; +const std::string IntegretyCheck::HASH_FILE = "hashlist.sha256"; +const std::string IntegretyCheck::KEY_FILE = "appKey.pub"; +const std::string IntegretyCheck::SIGNATURE_MARKER = "### signature ###\n"; + +std::string string_to_hex(const std::string &input) { + static const char *const lut = "0123456789ABCDEF"; + size_t len = input.length(); + + std::string output; + output.reserve(2 * len); + for (size_t i = 0; i < len; ++i) { + const unsigned char c = input[i]; + output.push_back(lut[c >> 4]); + output.push_back(lut[c & 15]); + } + return output; +} + +IntegretyCheck::IntegretyCheck() {} + +IntegretyCheck::IntegretyCheck(const std::string &appPath, + const std::string &keyFile, + const std::string &hashList) { + generateKeyPair(); + saveKeyFile(appPath); + std::string hashlist = generateHashList(appPath); + signHashList(hashlist); + // loadHashList(hashList); +} + +bool IntegretyCheck::loadKeyFile(const std::string &app) { + fs::path appPath(app); + CryptoPP::FileSource input((appPath / KEY_FILE).c_str(), true); + m_publicKey.BERDecode(input); + return true; +} + +bool IntegretyCheck::saveKeyFile(const std::string &app) { + ///@todo https://github.com/noloader/cryptopp-pem + fs::path appPath(app); + std::cout << (appPath / KEY_FILE).string() << std::endl; + CryptoPP::FileSink output((appPath / KEY_FILE).c_str()); + m_publicKey.DEREncode(output); + return true; +} + +bool IntegretyCheck::generateKeyPair() { + CryptoPP::AutoSeededRandomPool rng; + + CryptoPP::InvertibleRSAFunction params; + params.GenerateRandomWithKeySize(rng, KEY_SIZE); + + m_privateKey = CryptoPP::RSA::PrivateKey(params); + m_publicKey = CryptoPP::RSA::PublicKey(params); + return true; +} + +std::string IntegretyCheck::generateHashList(const std::string &app) { + std::stringstream hashlist; + fs::path appPath(app); + std::string p = appPath.string(); + + for (auto f : fs::recursive_directory_iterator(appPath)) { + if (f.is_directory()) { + // skip directory, iterator is recursive + } else { + // strip appPath + std::string filepath = f.path().string(); + filepath.erase(0, p.size()); + // write hash line to file + hashlist << generateFileHash(f.path().string()) << " *" << filepath + << std::endl; + } + } + return hashlist.str(); +} + +std::string IntegretyCheck::generateFileHash(const std::string &filepath) { + std::ifstream in(filepath); + std::string contents((std::istreambuf_iterator(in)), + std::istreambuf_iterator()); + CryptoPP::byte const *pbData = (CryptoPP::byte *)contents.c_str(); + unsigned long nDataLen = contents.size(); + CryptoPP::byte abDigest[CryptoPP::SHA256::DIGESTSIZE]; + + CryptoPP::SHA256().CalculateDigest(abDigest, pbData, nDataLen); + + return string_to_hex( + std::string((char *)abDigest, CryptoPP::SHA256::DIGESTSIZE)); +} + +void IntegretyCheck::signHashList(std::string &hashList) { + CryptoPP::AutoSeededRandomPool rng; + tmp = hashList; + + CryptoPP::RSASSA_PKCS1v15_SHA_Signer signer(m_privateKey); + size_t signature_length = signer.MaxSignatureLength(); + CryptoPP::SecByteBlock signature(signature_length); + signature_length = + signer.SignMessage(rng, (const CryptoPP::byte *)hashList.c_str(), + hashList.length(), signature); + std::cerr << signature_length << std::endl; + signature.resize(signature_length); + tmpSec = signature; + + // transfrom binary signature to base64 encoded string + std::string strHexSignature; + CryptoPP::Base64Encoder base64enc(new CryptoPP::StringSink(strHexSignature)); + base64enc.Put(signature, signature_length); + base64enc.MessageEnd(); + + // add divider and base64 encoded signature to hash list + hashList.append(SIGNATURE_MARKER); + hashList.append(strHexSignature); +} + +bool IntegretyCheck::verifyHashList(const std::string &signedHashList) { + size_t startSig = signedHashList.find(SIGNATURE_MARKER); + if (startSig == std::string::npos) { + return false; + } + + // extract hash list + std::string hashList = signedHashList.substr(0, startSig); + + // extract base64 encoded signature + std::string base64Signature = + signedHashList.substr(startSig + SIGNATURE_MARKER.length()); + + // decode base64 encoded signature + std::string decodedSignature; + CryptoPP::StringSource decoder( + base64Signature, true, + new CryptoPP::Base64Decoder(new CryptoPP::StringSink(decodedSignature))); + + // transform std::string into SecByteBlock + CryptoPP::SecByteBlock signature( + (const CryptoPP::byte *)decodedSignature.c_str(), + decodedSignature.length()); + + CryptoPP::RSASSA_PKCS1v15_SHA_Verifier verifier(m_publicKey); + bool result = + verifier.VerifyMessage((const CryptoPP::byte *)hashList.c_str(), + hashList.length(), signature, signature.size()); + + return result; +} diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..a0bdbe7 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,80 @@ +#include +#include + +#include "modes.h" +#include "aes.h" +#include "filters.h" + +#include "QApplication" +#include "inc/mainwindow.h" + + +int main(int argc, char **argv) { + //Key and IV setup + //AES encryption uses a secret key of a variable length (128-bit, 196-bit or 256- + //bit). This key is secretly exchanged between two parties before communication + //begins. DEFAULT_KEYLENGTH= 16 bytes + CryptoPP::byte key[ CryptoPP::AES::DEFAULT_KEYLENGTH ], iv[ CryptoPP::AES::BLOCKSIZE ]; + memset( key, 0x00, CryptoPP::AES::DEFAULT_KEYLENGTH ); + memset( iv, 0x00, CryptoPP::AES::BLOCKSIZE ); + + // + // String and Sink setup + // + std::string plaintext = "Now is the time for all good men to come to the aide..."; + std::string ciphertext; + std::string decryptedtext; + + // + // Dump Plain Text + // + std::cout << "Plain Text (" << plaintext.size() << " bytes)" << std::endl; + std::cout << plaintext; + std::cout << std::endl << std::endl; + + // + // Create Cipher Text + // + CryptoPP::AES::Encryption aesEncryption(key, CryptoPP::AES::DEFAULT_KEYLENGTH); + CryptoPP::CBC_Mode_ExternalCipher::Encryption cbcEncryption( aesEncryption, iv ); + + CryptoPP::StreamTransformationFilter stfEncryptor(cbcEncryption, new CryptoPP::StringSink( ciphertext ) ); + stfEncryptor.Put( reinterpret_cast( plaintext.c_str() ), plaintext.length() ); + stfEncryptor.MessageEnd(); + + // + // Dump Cipher Text + // + std::cout << "Cipher Text (" << ciphertext.size() << " bytes)" << std::endl; + + for( uint i = 0; i < ciphertext.size(); i++ ) { + + std::cout << "0x" << std::hex << (0xFF & static_cast(ciphertext[i])) << " "; + } + + std::cout << std::endl << std::endl; + + // + // Decrypt + // + CryptoPP::AES::Decryption aesDecryption(key, CryptoPP::AES::DEFAULT_KEYLENGTH); + CryptoPP::CBC_Mode_ExternalCipher::Decryption cbcDecryption( aesDecryption, iv ); + + CryptoPP::StreamTransformationFilter stfDecryptor(cbcDecryption, new CryptoPP::StringSink( decryptedtext ) ); + stfDecryptor.Put( reinterpret_cast( ciphertext.c_str() ), ciphertext.size() ); + stfDecryptor.MessageEnd(); + + // + // Dump Decrypted Text + // + std::cout << "Decrypted Text: " << std::endl; + std::cout << decryptedtext; + std::cout << std::endl << std::endl; + + QApplication app(argc, argv); + AppWindow mw; + mw.show(); + app.exec(); + + return 0; +} diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp new file mode 100644 index 0000000..871d4ba --- /dev/null +++ b/src/mainwindow.cpp @@ -0,0 +1,59 @@ + +#include "inc/mainwindow.h" +#include "../ui/ui_mainwindow.h" + +#include +#include +#include +#include + +#include +#include + +AppWindow::AppWindow() : QMainWindow(nullptr), ui(new Ui::MainWindow()) { + ui->setupUi(this); + connect(ui->btnFolderSelect, &QToolButton::clicked, this, + &AppWindow::onAppFolderSelect_click); + connect(ui->btnSelectPublicKey, &QToolButton::clicked, this, + &AppWindow::onPublicKeySelect_click); +} + +void AppWindow::onAppFolderSelect_click() { + auto basePath = + QStandardPaths::standardLocations(QStandardPaths::HomeLocation).first(); + QString dir = QFileDialog::getExistingDirectory( + this, tr("Open Directory"), basePath, + QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); + ui->txtFolderPath->setText(dir); + QString pubKey = checkPublicKey(dir); + if (!pubKey.isEmpty()) { + ui->txtPublicKey->setText(pubKey); + } + + integretyCheck.generateKeyPair(); + integretyCheck.saveKeyFile(dir.toStdString()); + std::string hList = integretyCheck.generateHashList(dir.toStdString()); + integretyCheck.signHashList(hList); + std::cout << hList << std::endl; + + integretyCheck.verifyHashList(hList); +} + +void AppWindow::onPublicKeySelect_click() { + auto basePath = ui->txtFolderPath->text(); + if (basePath.isEmpty()) { + QStandardPaths::standardLocations(QStandardPaths::HomeLocation); + } + QString fileName = QFileDialog::getOpenFileName( + this, tr("Open Public Key File"), basePath, tr("KeyFiles (*.pub)")); + ui->txtPublicKey->setText(fileName); +} + +const QString AppWindow::checkPublicKey(const QString &path) { + QString pubKeyFile = path + QDir::separator() + + QString::fromStdString(IntegretyCheck::KEY_FILE); + if (!QFileInfo::exists(pubKeyFile)) { + pubKeyFile = ""; + } + return pubKeyFile; +} diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui new file mode 100644 index 0000000..57b0e01 --- /dev/null +++ b/ui/mainwindow.ui @@ -0,0 +1,150 @@ + + + MainWindow + + + + 0 + 0 + 800 + 375 + + + + Software Application Integrety Check + + + + + + + + + PublicKey: + + + + + + + Application Folder: + + + + + + + + + + + + ... + + + + + + + + + + + + + + ... + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 1 + 0 + + + + Sign + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 1 + 0 + + + + Verify + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + txtPublicKey + btnSelectPublicKey + txtFolderPath + btnFolderSelect + btnSign + btnVerify + listWidget + + + +