initial commit
commit
084a150cb6
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
BasedOnStyle: LLVM
|
||||
ColumnLimit: '100'
|
||||
MaxEmptyLinesToKeep: '2'
|
||||
|
||||
...
|
||||
|
|
@ -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
|
||||
)
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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>
|
||||
Loading…
Reference in New Issue