initial commit

master
Arne Schroeder 2019-09-07 10:04:50 +02:00
commit 084a150cb6
8 changed files with 568 additions and 0 deletions

6
.clang-format Normal file
View File

@ -0,0 +1,6 @@
---
BasedOnStyle: LLVM
ColumnLimit: '100'
MaxEmptyLinesToKeep: '2'
...

54
CMakeLists.txt Normal file
View File

@ -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
)

33
inc/integretychecker.h Normal file
View File

@ -0,0 +1,33 @@
#pragma once
#include "../cryptopp/rsa.h"
#include <string>
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;
};

26
inc/mainwindow.h Normal file
View File

@ -0,0 +1,26 @@
#pragma once
#include "../inc/integretychecker.h"
#include <QMainWindow>
#include <memory>
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::MainWindow> ui;
const QString checkPublicKey(const QString &path);
IntegretyCheck integretyCheck;
};

160
src/integretychecker.cpp Normal file
View File

@ -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 <filesystem>
#include <fstream>
#include <iostream>
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<char>(in)),
std::istreambuf_iterator<char>());
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;
}

80
src/main.cpp Normal file
View File

@ -0,0 +1,80 @@
#include <iostream>
#include <iomanip>
#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<const unsigned char*>( 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<CryptoPP::byte>(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<const unsigned char*>( 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;
}

59
src/mainwindow.cpp Normal file
View File

@ -0,0 +1,59 @@
#include "inc/mainwindow.h"
#include "../ui/ui_mainwindow.h"
#include <QFile>
#include <QFileDialog>
#include <QFileInfo>
#include <QStandardPaths>
#include <QDebug>
#include <iostream>
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;
}

150
ui/mainwindow.ui Normal file
View File

@ -0,0 +1,150 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>375</height>
</rect>
</property>
<property name="windowTitle">
<string>Software Application Integrety Check</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QFormLayout" name="formLayout">
<item row="1" column="0">
<widget class="QLabel" name="lblPublicKey">
<property name="text">
<string>PublicKey:</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="lblFolderPath">
<property name="text">
<string>Application Folder:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="hLayoutKeyInput">
<item>
<widget class="QLineEdit" name="txtPublicKey"/>
</item>
<item>
<widget class="QToolButton" name="btnSelectPublicKey">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="hLayoutAppInput">
<item>
<widget class="QLineEdit" name="txtFolderPath"/>
</item>
<item>
<widget class="QToolButton" name="btnFolderSelect">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="hLayoutButtons">
<item>
<spacer name="hSpacerLeft">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btnSign">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Sign</string>
</property>
</widget>
</item>
<item>
<spacer name="hSpacerMiddle">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btnVerify">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Verify</string>
</property>
</widget>
</item>
<item>
<spacer name="hSpacerRight">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QListWidget" name="listWidget"/>
</item>
</layout>
</widget>
</widget>
<tabstops>
<tabstop>txtPublicKey</tabstop>
<tabstop>btnSelectPublicKey</tabstop>
<tabstop>txtFolderPath</tabstop>
<tabstop>btnFolderSelect</tabstop>
<tabstop>btnSign</tabstop>
<tabstop>btnVerify</tabstop>
<tabstop>listWidget</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>