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