aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/gpg/GpgContext.h2
-rw-r--r--include/gpg/GpgGenKeyInfo.h2
-rwxr-xr-xinclude/ui/KeyMgmt.h4
-rw-r--r--include/ui/Wizard.h2
-rw-r--r--include/ui/keygen/KeygenDialog.h (renamed from include/ui/KeygenDialog.h)1
-rw-r--r--include/ui/keygen/KeygenThread.h (renamed from include/ui/KeygenThread.h)10
-rw-r--r--include/ui/keygen/SubkeyGenerateDialog.h79
-rw-r--r--include/ui/keygen/SubkeyGenerateThread.h33
-rw-r--r--include/ui/keypair_details/KeyPairSubkeyTab.h21
-rw-r--r--include/ui/widgets/KeyList.h5
-rw-r--r--src/gpg/GpgContext.cpp71
-rw-r--r--src/gpg/GpgGenKeyInfo.cpp27
-rw-r--r--src/ui/CMakeLists.txt1
-rwxr-xr-xsrc/ui/KeyMgmt.cpp25
-rw-r--r--src/ui/keygen/KeygenDialog.cpp (renamed from src/ui/KeygenDialog.cpp)2
-rw-r--r--src/ui/keygen/KeygenThread.cpp (renamed from src/ui/KeygenThread.cpp)8
-rw-r--r--src/ui/keygen/SubkeyGenerateDialog.cpp292
-rw-r--r--src/ui/keygen/SubkeyGenerateThread.cpp19
-rw-r--r--src/ui/keypair_details/KeyPairSubkeyTab.cpp143
-rw-r--r--src/ui/keypair_details/KeyPairUIDTab.cpp5
-rw-r--r--src/ui/widgets/KeyList.cpp17
21 files changed, 719 insertions, 50 deletions
diff --git a/include/gpg/GpgContext.h b/include/gpg/GpgContext.h
index a68c6490..af390ff1 100644
--- a/include/gpg/GpgContext.h
+++ b/include/gpg/GpgContext.h
@@ -93,6 +93,8 @@ namespace GpgME {
bool generateKey(GenKeyInfo *params);
+ bool generateSubkey(const GpgKey &key, GenKeyInfo *params);
+
void deleteKeys(QStringList *uidList);
bool encrypt(QStringList *uidList, const QByteArray &inBuffer,
diff --git a/include/gpg/GpgGenKeyInfo.h b/include/gpg/GpgGenKeyInfo.h
index ee2b0262..a5936207 100644
--- a/include/gpg/GpgGenKeyInfo.h
+++ b/include/gpg/GpgGenKeyInfo.h
@@ -49,6 +49,8 @@ public:
static const QVector<QString> SupportedKeyAlgo;
+ static const QVector<QString> SupportedSubkeyAlgo;
+
[[nodiscard]] bool isSubKey() const {
return subKey;
}
diff --git a/include/ui/KeyMgmt.h b/include/ui/KeyMgmt.h
index c709fa86..c11280af 100755
--- a/include/ui/KeyMgmt.h
+++ b/include/ui/KeyMgmt.h
@@ -26,11 +26,11 @@
#define __KEYMGMT_H__
#include "ui/widgets/KeyList.h"
-#include "KeygenThread.h"
+#include "ui/keygen/KeygenThread.h"
#include "ui/keypair_details/KeyDetailsDialog.h"
#include "KeyImportDetailDialog.h"
#include "KeyServerImportDialog.h"
-#include "KeygenDialog.h"
+#include "ui/keygen/KeygenDialog.h"
class KeyMgmt : public QMainWindow {
diff --git a/include/ui/Wizard.h b/include/ui/Wizard.h
index 1003bc5d..da6fdf05 100644
--- a/include/ui/Wizard.h
+++ b/include/ui/Wizard.h
@@ -25,7 +25,7 @@
#ifndef WIZARD_H
#define WIZARD_H
-#include "ui/KeygenDialog.h"
+#include "ui/keygen/KeygenDialog.h"
#include "ui/KeyMgmt.h"
#include "gpg/GpgConstants.h"
#include "SettingsDialog.h"
diff --git a/include/ui/KeygenDialog.h b/include/ui/keygen/KeygenDialog.h
index b1feaa4a..a2718a1d 100644
--- a/include/ui/KeygenDialog.h
+++ b/include/ui/keygen/KeygenDialog.h
@@ -76,7 +76,6 @@ private:
void generateKeyDialog();
-
/**
* @details Refresh widgets state by GenKeyInfo
*/
diff --git a/include/ui/KeygenThread.h b/include/ui/keygen/KeygenThread.h
index 079409e5..e04e00d2 100644
--- a/include/ui/KeygenThread.h
+++ b/include/ui/keygen/KeygenThread.h
@@ -26,12 +26,6 @@
#include "gpg/GpgContext.h"
-
-QT_BEGIN_NAMESPACE
-class QMessageBox;
-
-QT_END_NAMESPACE
-
class KeyGenThread : public QThread {
Q_OBJECT
@@ -39,7 +33,6 @@ public:
KeyGenThread(GenKeyInfo *keyGenParams, GpgME::GpgContext *ctx);
signals:
-
void signalKeyGenerated(bool success);
private:
@@ -49,7 +42,8 @@ private:
QMutex mutex;
protected:
- void run();
+
+ void run() override;
};
diff --git a/include/ui/keygen/SubkeyGenerateDialog.h b/include/ui/keygen/SubkeyGenerateDialog.h
new file mode 100644
index 00000000..874b2451
--- /dev/null
+++ b/include/ui/keygen/SubkeyGenerateDialog.h
@@ -0,0 +1,79 @@
+//
+// Created by eric on 2021/5/30.
+//
+
+#ifndef GPGFRONTEND_SUBKEYGENERATEDIALOG_H
+#define GPGFRONTEND_SUBKEYGENERATEDIALOG_H
+
+#include "GpgFrontend.h"
+#include "gpg/GpgKey.h"
+#include "gpg/GpgSubKey.h"
+#include "gpg/GpgGenKeyInfo.h"
+
+#include "SubkeyGenerateThread.h"
+
+class SubkeyGenerateDialog : public QDialog {
+Q_OBJECT
+
+public:
+
+ explicit SubkeyGenerateDialog(GpgME::GpgContext *ctx, const GpgKey &key, QWidget *parent = nullptr);
+
+private:
+
+ GpgME::GpgContext *mCtx; /** The current gpg context */
+ const GpgKey &mKey;
+
+ GenKeyInfo genKeyInfo{};
+ SubkeyGenerateThread *kg{}; /** Thread for key generation */
+
+ QGroupBox *keyUsageGroupBox{};
+ QDialogButtonBox *buttonBox; /** Box for standardbuttons */
+ QLabel *errorLabel{}; /** Label containing error message */
+ QSpinBox *keySizeSpinBox{}; /** Spinbox for the keys size (in bit) */
+ QComboBox *keyTypeComboBox{}; /** Combobox for Keytpe */
+ QDateTimeEdit *dateEdit{}; /** Dateedit for expiration date */
+ QCheckBox *expireCheckBox{}; /** Checkbox, if key should expire */
+
+ // ENCR, SIGN, CERT, AUTH
+ std::vector<QCheckBox *> keyUsageCheckBoxes;
+
+ QGroupBox *create_key_usage_group_box();
+
+ QGroupBox *create_basic_info_group_box();
+
+ void set_signal_slot();
+
+ /**
+ * @details Refresh widgets state by GenKeyInfo
+ */
+ void refresh_widgets_state();
+
+private slots:
+
+ /**
+ * @details when expirebox was checked/unchecked, enable/disable the expiration date box
+ */
+ void slotExpireBoxChanged();
+
+ /**
+ * @details check all lineedits for false entries. Show error, when there is one, otherwise generate the key
+ */
+ void slotKeyGenAccept();
+
+ void slotEncryptionBoxChanged(int state);
+
+ void slotSigningBoxChanged(int state);
+
+ void slotCertificationBoxChanged(int state);
+
+ void slotAuthenticationBoxChanged(int state);
+
+ void slotActivatedKeyType(int index);
+
+ void slotKeyGenResult(bool success);
+
+};
+
+
+#endif //GPGFRONTEND_SUBKEYGENERATEDIALOG_H
diff --git a/include/ui/keygen/SubkeyGenerateThread.h b/include/ui/keygen/SubkeyGenerateThread.h
new file mode 100644
index 00000000..1f3a1cb5
--- /dev/null
+++ b/include/ui/keygen/SubkeyGenerateThread.h
@@ -0,0 +1,33 @@
+//
+// Created by eric on 2021/5/30.
+//
+
+#ifndef GPGFRONTEND_SUBKEYGENERATETHREAD_H
+#define GPGFRONTEND_SUBKEYGENERATETHREAD_H
+
+#include "gpg/GpgContext.h"
+
+class SubkeyGenerateThread : public QThread {
+ Q_OBJECT
+
+public:
+ SubkeyGenerateThread(GpgKey key, GenKeyInfo *keyGenParams, GpgME::GpgContext *ctx);
+
+signals:
+
+ void signalKeyGenerated(bool success);
+
+private:
+ const GpgKey mKey;
+ GenKeyInfo *keyGenParams;
+ GpgME::GpgContext *mCtx;
+ [[maybe_unused]] bool abort;
+ QMutex mutex;
+
+protected:
+
+ void run() override;
+};
+
+
+#endif //GPGFRONTEND_SUBKEYGENERATETHREAD_H
diff --git a/include/ui/keypair_details/KeyPairSubkeyTab.h b/include/ui/keypair_details/KeyPairSubkeyTab.h
index 11a9ab41..45abe7a1 100644
--- a/include/ui/keypair_details/KeyPairSubkeyTab.h
+++ b/include/ui/keypair_details/KeyPairSubkeyTab.h
@@ -28,6 +28,7 @@
#include "GpgFrontend.h"
#include "gpg/GpgContext.h"
+#include "ui/keygen/SubkeyGenerateDialog.h"
class KeyPairSubkeyTab : public QWidget {
Q_OBJECT
@@ -41,14 +42,30 @@ private:
void creatSubkeyList();
GpgME::GpgContext *mCtx;
+ const GpgKey &mKey;
+ QTableWidget *subkeyList;
+ QVector<const GpgSubKey *> buffered_subkeys;
- const GpgKey &key;
+ QGroupBox *listBox;
+ QGroupBox *detailBox;
- QTableWidget *subkeyList;
+
+ QLabel *keySizeVarLabel; /** Label containng the keys keysize */
+ QLabel *expireVarLabel; /** Label containng the keys expiration date */
+ QLabel *createdVarLabel; /** Label containng the keys creation date */
+ QLabel *algorithmVarLabel; /** Label containng the keys algorithm */
+ QLabel *keyidVarLabel; /** Label containng the keys keyid */
+ QLabel *fingerPrintVarLabel; /** Label containng the keys fingerprint */
+ QLabel *usageVarLabel;
private slots:
+ void slotAddSubkey();
+
+ void slotRefreshSubkeyList();
+
+ void slotRefreshSubkeyDetail();
};
diff --git a/include/ui/widgets/KeyList.h b/include/ui/widgets/KeyList.h
index 0d9b07dc..06e80910 100644
--- a/include/ui/widgets/KeyList.h
+++ b/include/ui/widgets/KeyList.h
@@ -67,6 +67,8 @@ public:
void setFilter(std::function<bool(const GpgKey&)> filter);
+ void setDoubleClickedAction(std::function<void(const GpgKey&, QWidget *)> action);
+
void setColumnWidth(int row, int size);
void addMenuAction(QAction *act);
@@ -108,12 +110,15 @@ private:
QVector<QString> excluded_key_ids;
std::function<bool(const GpgKey &)> mFilter = nullptr;
+ std::function<void(const GpgKey &, QWidget *)> mAction = nullptr;
private slots:
void uploadFinished();
+ void slotDoubleClicked(const QModelIndex &index);
+
protected:
diff --git a/src/gpg/GpgContext.cpp b/src/gpg/GpgContext.cpp
index 51ee07b5..d4736249 100644
--- a/src/gpg/GpgContext.cpp
+++ b/src/gpg/GpgContext.cpp
@@ -23,7 +23,7 @@
*/
#include "gpg/GpgContext.h"
-#include "ui/KeygenThread.h"
+#include "ui/keygen/KeygenThread.h"
#include <unistd.h> /* contains read/write */
@@ -321,28 +321,6 @@ namespace GpgME {
return;
}
-// list only private keys ( the 1 does )
-// gpgmeError = gpgme_op_keylist_start(mCtx, nullptr, 1);
-// if (gpg_err_code(gpgmeError) != GPG_ERR_NO_ERROR) {
-// checkErr(gpgmeError);
-// return;
-// }
-//
-// while ((gpgmeError = gpgme_op_keylist_next(mCtx, &key)) == GPG_ERR_NO_ERROR) {
-// if (key->subkeys) {
-// auto it = keys_map.find(key->subkeys->keyid);
-// if (it != keys_map.end()) {
-// it->is_private_key = true;
-// }
-// }
-// gpgme_key_unref(key);
-// }
-//
-// if (gpg_err_code(gpgmeError) != GPG_ERR_EOF) {
-// checkErr(gpgmeError);
-// return;
-// }
-
gpgmeError = gpgme_op_keylist_end(mCtx);
if (gpg_err_code(gpgmeError) != GPG_ERR_NO_ERROR) {
checkErr(gpgmeError);
@@ -1022,7 +1000,52 @@ namespace GpgME {
checkErr(gpgmeError);
return false;
}
- return false;
+ }
+
+ bool GpgContext::generateSubkey(const GpgKey &key, GenKeyInfo *params) {
+
+ if(!params->isSubKey()) {
+ return false;
+ }
+
+ auto algo_utf8 = (params->getAlgo() + params->getKeySizeStr()).toUtf8();
+ const char *algo = algo_utf8.constData();
+ unsigned long expires = QDateTime::currentDateTime().secsTo(params->getExpired());
+ unsigned int flags = 0;
+
+ if (!params->isSubKey()) {
+ flags |= GPGME_CREATE_CERT;
+ }
+
+ if (params->isAllowEncryption()) {
+ flags |= GPGME_CREATE_ENCR;
+ }
+
+ if (params->isAllowSigning()) {
+ flags |= GPGME_CREATE_SIGN;
+ }
+
+ if (params->isAllowAuthentication()) {
+ flags |= GPGME_CREATE_AUTH;
+ }
+
+ if (params->isNonExpired()) {
+ flags |= GPGME_CREATE_NOEXPIRE;
+ }
+
+ flags |= GPGME_CREATE_NOPASSWD;
+
+
+ auto gpgmeError = gpgme_op_createsubkey(mCtx, key.key_refer,
+ algo, 0, expires, flags);
+ if(gpgmeError == GPG_ERR_NO_ERROR) {
+ emit signalKeyUpdated(key.id);
+ return true;
+ }
+ else {
+ checkErr(gpgmeError);
+ return false;
+ }
}
}
diff --git a/src/gpg/GpgGenKeyInfo.cpp b/src/gpg/GpgGenKeyInfo.cpp
index 9532e876..69da27e3 100644
--- a/src/gpg/GpgGenKeyInfo.cpp
+++ b/src/gpg/GpgGenKeyInfo.cpp
@@ -30,6 +30,13 @@ const QVector<QString> GenKeyInfo::SupportedKeyAlgo = {
"ED25519"
};
+const QVector<QString> GenKeyInfo::SupportedSubkeyAlgo = {
+ "RSA",
+ "DSA",
+ "ED25519",
+ "ELG"
+};
+
void GenKeyInfo::setAlgo(const QString &m_algo) {
qDebug() << "set algo " << m_algo;
@@ -38,9 +45,12 @@ void GenKeyInfo::setAlgo(const QString &m_algo) {
if (!this->subKey) {
this->setAllowCertification(true);
- this->allowChangeCertification = false;
+ } else {
+ this->setAllowCertification(false);
}
+ this->allowChangeCertification = false;
+
auto lower_algo = m_algo.toLower();
if(lower_algo == "rsa") {
@@ -81,6 +91,21 @@ void GenKeyInfo::setAlgo(const QString &m_algo) {
suggestMaxKeySize = -1;
suggestSizeAdditionStep = -1;
setKeySize(-1);
+ } else if (lower_algo == "elg") {
+ /**
+ * GnuPG supports the Elgamal asymmetric encryption algorithm in key lengths ranging from 1024 to 4096 bits.
+ */
+
+ setAllowAuthentication(false);
+ allowChangeAuthentication = false;
+
+ setAllowSigning(false);
+ allowChangeSigning = false;
+
+ suggestMinKeySize = 1024;
+ suggestMaxKeySize = 4096;
+ suggestSizeAdditionStep = 1024;
+ setKeySize(2048);
}
GenKeyInfo::algo = lower_algo;
}
diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt
index 5456c2a4..2527da46 100644
--- a/src/ui/CMakeLists.txt
+++ b/src/ui/CMakeLists.txt
@@ -1,6 +1,7 @@
aux_source_directory(. QTUI_SOURCE)
aux_source_directory(./keypair_details QTUI_SOURCE)
aux_source_directory(./widgets QTUI_SOURCE)
+aux_source_directory(./keygen QTUI_SOURCE)
add_library(qtui STATIC ${QTUI_SOURCE} ${QTUI_KEYPAIR_DETAILS_SOURCE})
diff --git a/src/ui/KeyMgmt.cpp b/src/ui/KeyMgmt.cpp
index d40928ee..3b04a22f 100755
--- a/src/ui/KeyMgmt.cpp
+++ b/src/ui/KeyMgmt.cpp
@@ -35,6 +35,9 @@ KeyMgmt::KeyMgmt(GpgME::GpgContext *ctx, QWidget *parent ) : QMainWindow(parent
mKeyList->setColumnWidth(2, 250);
mKeyList->setColumnWidth(3, 250);
setCentralWidget(mKeyList);
+ mKeyList->setDoubleClickedAction([this] (const GpgKey &key, QWidget *parent) {
+ new KeyDetailsDialog(mCtx, key, parent);
+ });
createActions();
createMenus();
@@ -62,7 +65,7 @@ KeyMgmt::KeyMgmt(GpgME::GpgContext *ctx, QWidget *parent ) : QMainWindow(parent
this->resize(QSize(800, 400));
}
- setWindowTitle(tr("Keymanagement"));
+ setWindowTitle(tr("KeyPairs Management"));
mKeyList->addMenuAction(deleteSelectedKeysAct);
mKeyList->addMenuAction(showKeyDetailsAct);
}
@@ -80,9 +83,9 @@ void KeyMgmt::createActions()
generateKeyPairAct->setToolTip(tr("Generate KeyPair"));
connect(generateKeyPairAct, SIGNAL(triggered()), this, SLOT(slotGenerateKeyDialog()));
- generateSubKeyAct = new QAction(tr("Generate SubKey"), this);
+ generateSubKeyAct = new QAction(tr("Generate Subkey For Selected"), this);
generateSubKeyAct->setIcon(QIcon(":key_generate.png"));
- generateSubKeyAct->setToolTip(tr("Generate SubKey Of KeyPair"));
+ generateSubKeyAct->setToolTip(tr("Generate Subkey For Selected KeyPair"));
connect(generateSubKeyAct, SIGNAL(triggered()), this, SLOT(slotGenerateSubKey()));
importKeyFromFileAct = new QAction(tr("&File"), this);
@@ -311,5 +314,21 @@ void KeyMgmt::closeEvent(QCloseEvent *event)
}
void KeyMgmt::slotGenerateSubKey() {
+ auto selectedList = mKeyList->getSelected();
+ if(selectedList->empty()) {
+ QMessageBox::information(nullptr,
+ tr("Invalid Operation"),
+ tr("Please select one KeyPair before doing this operation."));
+ return;
+ }
+ const auto &key = mCtx->getKeyById(selectedList->first());
+ if(!key.is_private_key) {
+ QMessageBox::critical(nullptr,
+ tr("Invalid Operation"),
+ tr("If a key pair does not have a private key then it will not be able to generate sub-keys."));
+ return;
+ }
+ auto dialog = new SubkeyGenerateDialog(mCtx, key, this);
+ dialog->show();
}
diff --git a/src/ui/KeygenDialog.cpp b/src/ui/keygen/KeygenDialog.cpp
index d930fffe..347ebedc 100644
--- a/src/ui/KeygenDialog.cpp
+++ b/src/ui/keygen/KeygenDialog.cpp
@@ -22,7 +22,7 @@
*
*/
-#include "ui/KeygenDialog.h"
+#include "ui/keygen/KeygenDialog.h"
KeyGenDialog::KeyGenDialog(GpgME::GpgContext *ctx, QWidget *parent)
: QDialog(parent), mCtx(ctx) {
diff --git a/src/ui/KeygenThread.cpp b/src/ui/keygen/KeygenThread.cpp
index defc20bb..5922138d 100644
--- a/src/ui/KeygenThread.cpp
+++ b/src/ui/keygen/KeygenThread.cpp
@@ -22,12 +22,10 @@
*
*/
-#include "ui/KeygenThread.h"
+#include "ui/keygen/KeygenThread.h"
-KeyGenThread::KeyGenThread(GenKeyInfo* keyGenParams, GpgME::GpgContext *ctx): QThread(nullptr) {
- this->keyGenParams = keyGenParams;
- this->mCtx = ctx;
- abort = false;
+KeyGenThread::KeyGenThread(GenKeyInfo* keyGenParams, GpgME::GpgContext *ctx):
+mCtx(ctx), keyGenParams(keyGenParams), abort(false), QThread(nullptr) {
connect(this, &KeyGenThread::finished, this, &KeyGenThread::deleteLater);
}
diff --git a/src/ui/keygen/SubkeyGenerateDialog.cpp b/src/ui/keygen/SubkeyGenerateDialog.cpp
new file mode 100644
index 00000000..fd3b9b9e
--- /dev/null
+++ b/src/ui/keygen/SubkeyGenerateDialog.cpp
@@ -0,0 +1,292 @@
+//
+// Created by eric on 2021/5/30.
+//
+
+#include "ui/keygen/SubkeyGenerateDialog.h"
+
+SubkeyGenerateDialog::SubkeyGenerateDialog(GpgME::GpgContext *ctx, const GpgKey &key, QWidget *parent)
+ : genKeyInfo(true), mCtx(ctx), mKey(key), QDialog(parent) {
+
+ buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+
+ keyUsageGroupBox = create_key_usage_group_box();
+
+ auto *groupGrid = new QGridLayout(this);
+ groupGrid->addWidget(create_basic_info_group_box(), 0, 0);
+ groupGrid->addWidget(keyUsageGroupBox, 1, 0);
+
+ auto *nameList = new QWidget(this);
+ nameList->setLayout(groupGrid);
+
+ auto *vbox2 = new QVBoxLayout();
+ vbox2->addWidget(nameList);
+ vbox2->addWidget(errorLabel);
+ vbox2->addWidget(buttonBox);
+
+ this->setWindowTitle(tr("Generate Subkey"));
+
+ this->setLayout(vbox2);
+ this->setModal(true);
+
+ set_signal_slot();
+ refresh_widgets_state();
+}
+
+QGroupBox *SubkeyGenerateDialog::create_key_usage_group_box() {
+ auto *groupBox = new QGroupBox(this);
+ auto *grid = new QGridLayout(this);
+
+ groupBox->setTitle("Key Usage");
+
+ auto* encrypt = new QCheckBox(tr("Encryption"), groupBox);
+ encrypt->setTristate(false);
+
+ auto* sign = new QCheckBox(tr("Signing"),groupBox);
+ sign->setTristate(false);
+
+ auto* cert = new QCheckBox(tr("Certification"),groupBox);
+ cert->setTristate(false);
+
+ auto* auth = new QCheckBox(tr("Authentication"), groupBox);
+ auth->setTristate(false);
+
+ keyUsageCheckBoxes.push_back(encrypt);
+ keyUsageCheckBoxes.push_back(sign);
+ keyUsageCheckBoxes.push_back(cert);
+ keyUsageCheckBoxes.push_back(auth);
+
+ grid->addWidget(encrypt, 0, 0);
+ grid->addWidget(sign, 0, 1);
+ grid->addWidget(cert, 1, 0);
+ grid->addWidget(auth, 1, 1);
+
+ groupBox->setLayout(grid);
+
+ return groupBox;
+}
+
+QGroupBox *SubkeyGenerateDialog::create_basic_info_group_box() {
+ errorLabel = new QLabel(tr(""));
+ keySizeSpinBox = new QSpinBox(this);
+ keyTypeComboBox = new QComboBox(this);
+
+ for(auto &algo : GenKeyInfo::SupportedSubkeyAlgo) {
+ keyTypeComboBox->addItem(algo);
+ }
+ if(!GenKeyInfo::SupportedKeyAlgo.isEmpty()) {
+ keyTypeComboBox->setCurrentIndex(0);
+ }
+
+ QDateTime maxDateTime = QDateTime::currentDateTime().addYears(2);
+
+ dateEdit = new QDateTimeEdit(maxDateTime, this);
+ dateEdit->setMinimumDateTime(QDateTime::currentDateTime());
+ dateEdit->setMaximumDateTime(maxDateTime);
+ dateEdit->setDisplayFormat("dd/MM/yyyy hh:mm:ss");
+ dateEdit->setCalendarPopup(true);
+ dateEdit->setEnabled(true);
+
+ expireCheckBox = new QCheckBox(this);
+ expireCheckBox->setCheckState(Qt::Unchecked);
+
+ auto *vbox1 = new QGridLayout;
+
+ vbox1->addWidget(new QLabel(tr("Expiration Date:")), 2, 0);
+ vbox1->addWidget(new QLabel(tr("Never Expire")), 2, 3);
+ vbox1->addWidget(new QLabel(tr("KeySize (in Bit):")), 1, 0);
+ vbox1->addWidget(new QLabel(tr("Key Type:")), 0, 0);
+
+ vbox1->addWidget(dateEdit, 2, 1);
+ vbox1->addWidget(expireCheckBox, 2, 2);
+ vbox1->addWidget(keySizeSpinBox, 1, 1);
+ vbox1->addWidget(keyTypeComboBox, 0, 1);
+
+ auto basicInfoGroupBox = new QGroupBox();
+ basicInfoGroupBox->setLayout(vbox1);
+ basicInfoGroupBox->setTitle(tr("Basic Information"));
+
+ return basicInfoGroupBox;
+}
+
+void SubkeyGenerateDialog::set_signal_slot() {
+ connect(buttonBox, SIGNAL(accepted()), this, SLOT(slotKeyGenAccept()));
+ connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
+
+ connect(expireCheckBox, SIGNAL(stateChanged(int)), this, SLOT(slotExpireBoxChanged()));
+
+ connect(keyUsageCheckBoxes[0], SIGNAL(stateChanged(int)), this, SLOT(slotEncryptionBoxChanged(int)));
+ connect(keyUsageCheckBoxes[1], SIGNAL(stateChanged(int)), this, SLOT(slotSigningBoxChanged(int)));
+ connect(keyUsageCheckBoxes[2], SIGNAL(stateChanged(int)), this, SLOT(slotCertificationBoxChanged(int)));
+ connect(keyUsageCheckBoxes[3], SIGNAL(stateChanged(int)), this, SLOT(slotAuthenticationBoxChanged(int)));
+
+ connect(keyTypeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotActivatedKeyType(int)));
+}
+
+void SubkeyGenerateDialog::slotExpireBoxChanged() {
+ if (expireCheckBox->checkState()) {
+ dateEdit->setEnabled(false);
+ } else {
+ dateEdit->setEnabled(true);
+ }
+}
+
+void SubkeyGenerateDialog::refresh_widgets_state() {
+ qDebug() << "refresh_widgets_state called";
+
+ if(genKeyInfo.isAllowEncryption())
+ keyUsageCheckBoxes[0]->setCheckState(Qt::CheckState::Checked);
+ else
+ keyUsageCheckBoxes[0]->setCheckState(Qt::CheckState::Unchecked);
+
+ if(genKeyInfo.isAllowChangeEncryption())
+ keyUsageCheckBoxes[0]->setDisabled(false);
+ else
+ keyUsageCheckBoxes[0]->setDisabled(true);
+
+
+ if(genKeyInfo.isAllowSigning())
+ keyUsageCheckBoxes[1]->setCheckState(Qt::CheckState::Checked);
+ else
+ keyUsageCheckBoxes[1]->setCheckState(Qt::CheckState::Unchecked);
+
+ if(genKeyInfo.isAllowChangeSigning())
+ keyUsageCheckBoxes[1]->setDisabled(false);
+ else
+ keyUsageCheckBoxes[1]->setDisabled(true);
+
+
+ if(genKeyInfo.isAllowCertification())
+ keyUsageCheckBoxes[2]->setCheckState(Qt::CheckState::Checked);
+ else
+ keyUsageCheckBoxes[2]->setCheckState(Qt::CheckState::Unchecked);
+
+ if(genKeyInfo.isAllowChangeCertification())
+ keyUsageCheckBoxes[2]->setDisabled(false);
+ else
+ keyUsageCheckBoxes[2]->setDisabled(true);
+
+
+ if(genKeyInfo.isAllowAuthentication())
+ keyUsageCheckBoxes[3]->setCheckState(Qt::CheckState::Checked);
+ else
+ keyUsageCheckBoxes[3]->setCheckState(Qt::CheckState::Unchecked);
+
+ if(genKeyInfo.isAllowChangeAuthentication())
+ keyUsageCheckBoxes[3]->setDisabled(false);
+ else
+ keyUsageCheckBoxes[3]->setDisabled(true);
+
+
+ keySizeSpinBox->setRange(genKeyInfo.getSuggestMinKeySize(), genKeyInfo.getSuggestMaxKeySize());
+ keySizeSpinBox->setValue(genKeyInfo.getKeySize());
+ keySizeSpinBox->setSingleStep(genKeyInfo.getSizeChangeStep());
+
+}
+
+void SubkeyGenerateDialog::slotKeyGenAccept() {
+ QString errorString = "";
+
+ /**
+ * primary keys should have a reasonable expiration date (no more than 2 years in the future)
+ */
+ if(dateEdit->dateTime() > QDateTime::currentDateTime().addYears(2)) {
+
+ errorString.append(tr(" Expiration time no more than 2 years. "));
+ }
+
+ if (errorString.isEmpty()) {
+
+ genKeyInfo.setKeySize(keySizeSpinBox->value());
+
+ if (expireCheckBox->checkState()) {
+ genKeyInfo.setNonExpired(true);
+ } else {
+ genKeyInfo.setExpired(dateEdit->dateTime());
+ }
+
+ kg = new SubkeyGenerateThread(mKey ,&genKeyInfo, mCtx);
+ connect(kg, SIGNAL(signalKeyGenerated(bool)), this, SLOT(slotKeyGenResult(bool)));
+ kg->start();
+
+ this->accept();
+
+ auto *dialog = new QDialog(this, Qt::CustomizeWindowHint | Qt::WindowTitleHint);
+ dialog->setModal(true);
+ dialog->setWindowTitle(tr("Generating Subkey..."));
+
+ auto *waitMessage = new QLabel(
+ tr("Collecting random data for subkey generation.\n This may take a while.\n To speed up the process use your computer\n (e.g. browse the net, listen to music,...)"));
+ auto *pb = new QProgressBar();
+ pb->setRange(0, 0);
+
+ auto *layout = new QVBoxLayout(dialog);
+ layout->addWidget(waitMessage);
+ layout->addWidget(pb);
+ dialog->setLayout(layout);
+
+ dialog->show();
+
+ while (!kg->isFinished() && kg->isRunning()) {
+ QCoreApplication::processEvents();
+ }
+
+ dialog->close();
+
+ } else {
+ /**
+ * create error message
+ */
+ errorLabel->setAutoFillBackground(true);
+ QPalette error = errorLabel->palette();
+ error.setColor(QPalette::Background, "#ff8080");
+ errorLabel->setPalette(error);
+ errorLabel->setText(errorString);
+
+ this->show();
+ }
+}
+
+void SubkeyGenerateDialog::slotEncryptionBoxChanged(int state) {
+ if(state == 0) {
+ genKeyInfo.setAllowEncryption(false);
+ } else {
+ genKeyInfo.setAllowEncryption(true);
+ }
+}
+
+void SubkeyGenerateDialog::slotSigningBoxChanged(int state) {
+ if(state == 0) {
+ genKeyInfo.setAllowSigning(false);
+ } else {
+ genKeyInfo.setAllowSigning(true);
+ }
+}
+
+void SubkeyGenerateDialog::slotCertificationBoxChanged(int state) {
+ if(state == 0) {
+ genKeyInfo.setAllowCertification(false);
+ } else {
+ genKeyInfo.setAllowCertification(true);
+ }
+}
+
+void SubkeyGenerateDialog::slotAuthenticationBoxChanged(int state) {
+ if(state == 0) {
+ genKeyInfo.setAllowAuthentication(false);
+ } else {
+ genKeyInfo.setAllowAuthentication(true);
+ }
+}
+
+void SubkeyGenerateDialog::slotActivatedKeyType(int index) {
+ qDebug() << "key type index changed " << index;
+ genKeyInfo.setAlgo(this->keyTypeComboBox->itemText(index));
+ refresh_widgets_state();
+}
+
+void SubkeyGenerateDialog::slotKeyGenResult(bool success) {
+ if(success)
+ QMessageBox::information(nullptr, tr("Success"), tr("The new subkey has been generated."));
+ else
+ QMessageBox::critical(nullptr, tr("Failure"), tr("An error occurred during subkey generation."));
+}
diff --git a/src/ui/keygen/SubkeyGenerateThread.cpp b/src/ui/keygen/SubkeyGenerateThread.cpp
new file mode 100644
index 00000000..8ccec66f
--- /dev/null
+++ b/src/ui/keygen/SubkeyGenerateThread.cpp
@@ -0,0 +1,19 @@
+//
+// Created by eric on 2021/5/30.
+//
+
+#include "ui/keygen/SubkeyGenerateThread.h"
+
+#include <utility>
+
+SubkeyGenerateThread::SubkeyGenerateThread(GpgKey key, GenKeyInfo *keyGenParams, GpgME::GpgContext *ctx)
+ : mKey(std::move(key)), keyGenParams(keyGenParams) , mCtx(ctx), abort(
+ false) {
+ connect(this, &SubkeyGenerateThread::finished, this, &SubkeyGenerateThread::deleteLater);
+}
+
+void SubkeyGenerateThread::run() {
+ bool success = mCtx->generateSubkey(mKey, keyGenParams);
+ emit signalKeyGenerated(success);
+ emit finished({});
+}
diff --git a/src/ui/keypair_details/KeyPairSubkeyTab.cpp b/src/ui/keypair_details/KeyPairSubkeyTab.cpp
index 2f2a49cf..a67c119e 100644
--- a/src/ui/keypair_details/KeyPairSubkeyTab.cpp
+++ b/src/ui/keypair_details/KeyPairSubkeyTab.cpp
@@ -24,14 +24,72 @@
#include "ui/keypair_details/KeyPairSubkeyTab.h"
-KeyPairSubkeyTab::KeyPairSubkeyTab(GpgME::GpgContext *ctx, const GpgKey &key, QWidget *parent) : mCtx(ctx), key(key), QWidget(parent) {
+KeyPairSubkeyTab::KeyPairSubkeyTab(GpgME::GpgContext *ctx, const GpgKey &key, QWidget *parent) : mCtx(ctx), mKey(key), QWidget(parent) {
+ creatSubkeyList();
+
+ listBox = new QGroupBox("Subkey List");
+ detailBox = new QGroupBox("Detail of Selected Subkey");
+
+ auto uidButtonsLayout = new QGridLayout();
+
+ auto addSubkeyButton = new QPushButton(tr("Add New Subkey"));
+ if(!mKey.is_private_key) {
+ addSubkeyButton->setDisabled(true);
+ setHidden(addSubkeyButton);
+ }
+
+ uidButtonsLayout->addWidget(addSubkeyButton, 0, 1);
+
+ auto *baseLayout = new QVBoxLayout();
+
+ auto subkeyListLayout = new QGridLayout();
+ subkeyListLayout->addWidget(subkeyList, 0, 0);
+ subkeyListLayout->addLayout(uidButtonsLayout, 1, 0);
+
+ auto *subkeyDetailLayout = new QGridLayout();
+
+ subkeyDetailLayout->addWidget(new QLabel(tr("Key ID: ")), 0, 0);
+ subkeyDetailLayout->addWidget(new QLabel(tr("Algorithm: ")), 1, 0);
+ subkeyDetailLayout->addWidget(new QLabel(tr("Key size:")), 2, 0);
+ subkeyDetailLayout->addWidget(new QLabel(tr("Usage: ")), 3, 0);
+ subkeyDetailLayout->addWidget(new QLabel(tr("Expires on: ")), 4, 0);
+ subkeyDetailLayout->addWidget(new QLabel(tr("Last Update: ")), 5, 0);
+
+ keyidVarLabel = new QLabel();
+ keySizeVarLabel = new QLabel();
+ expireVarLabel = new QLabel();
+ algorithmVarLabel = new QLabel();
+ createdVarLabel = new QLabel();
+ usageVarLabel = new QLabel();
+
+ subkeyDetailLayout->addWidget(keyidVarLabel, 0, 1);
+ subkeyDetailLayout->addWidget(keySizeVarLabel, 2, 1);
+ subkeyDetailLayout->addWidget(expireVarLabel, 4, 1);
+ subkeyDetailLayout->addWidget(algorithmVarLabel, 1, 1);
+ subkeyDetailLayout->addWidget(createdVarLabel, 5, 1);
+ subkeyDetailLayout->addWidget(usageVarLabel, 3, 1);
+
+ listBox->setLayout(subkeyListLayout);
+ detailBox->setLayout(subkeyDetailLayout);
+
+ baseLayout->addWidget(listBox);
+ baseLayout->addWidget(detailBox);
+
+ connect(addSubkeyButton, SIGNAL(clicked(bool)), this, SLOT(slotAddSubkey()));
+ connect(mCtx, SIGNAL(signalKeyInfoChanged()), this, SLOT(slotRefreshSubkeyList()));
+ connect(subkeyList, SIGNAL(itemSelectionChanged()), this, SLOT(slotRefreshSubkeyDetail()));
+
+ setLayout(baseLayout);
setAttribute(Qt::WA_DeleteOnClose, true);
+ slotRefreshSubkeyList();
+
}
void KeyPairSubkeyTab::creatSubkeyList() {
subkeyList = new QTableWidget(this);
+
subkeyList->setColumnCount(5);
subkeyList->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
subkeyList->verticalHeader()->hide();
@@ -52,3 +110,86 @@ void KeyPairSubkeyTab::creatSubkeyList() {
subkeyList->setHorizontalHeaderLabels(labels);
subkeyList->horizontalHeader()->setStretchLastSection(true);
}
+
+void KeyPairSubkeyTab::slotRefreshSubkeyList() {
+ int row = 0;
+
+ subkeyList->setSelectionMode(QAbstractItemView::SingleSelection);
+
+ this->buffered_subkeys.clear();
+
+ for(const auto &subkeys : mKey.subKeys) {
+ if(subkeys.disabled || subkeys.revoked) {
+ continue;
+ }
+ this->buffered_subkeys.push_back(&subkeys);
+ }
+
+ subkeyList->setRowCount(buffered_subkeys.size());
+
+ for(const auto& subkeys : buffered_subkeys) {
+
+ auto *tmp0 = new QTableWidgetItem(subkeys->id);
+ tmp0->setTextAlignment(Qt::AlignCenter);
+ subkeyList->setItem(row, 0, tmp0);
+
+ auto *tmp1 = new QTableWidgetItem(QString::number(subkeys->length));
+ tmp1->setTextAlignment(Qt::AlignCenter);
+ subkeyList->setItem(row, 1, tmp1);
+
+ auto *tmp2 = new QTableWidgetItem(subkeys->pubkey_algo);
+ tmp2->setTextAlignment(Qt::AlignCenter);
+ subkeyList->setItem(row, 2, tmp2);
+
+ auto *tmp3= new QTableWidgetItem(subkeys->timestamp.toString());
+ tmp3->setTextAlignment(Qt::AlignCenter);
+ subkeyList->setItem(row, 3, tmp3);
+
+ auto *tmp4= new QTableWidgetItem(subkeys->expires.toString());
+ tmp4->setTextAlignment(Qt::AlignCenter);
+ subkeyList->setItem(row, 4, tmp4);
+
+ row++;
+ }
+
+ if(subkeyList->rowCount() > 0) {
+ subkeyList->selectRow(0);
+ }
+}
+
+void KeyPairSubkeyTab::slotAddSubkey() {
+ auto dialog = new SubkeyGenerateDialog(mCtx, mKey, this);
+ dialog->show();
+}
+
+void KeyPairSubkeyTab::slotRefreshSubkeyDetail() {
+
+ int row = 0;
+
+ for(int i = 0 ; i < subkeyList->rowCount(); i++) {
+ if(subkeyList->item(row, 0)->isSelected()) break;
+ row++;
+ }
+
+ auto key = buffered_subkeys[row];
+
+ keyidVarLabel->setText(key->id);
+ keySizeVarLabel->setText(QString::number(key->length));
+ expireVarLabel->setText(key->expires.toString());
+ algorithmVarLabel->setText(key->pubkey_algo);
+ createdVarLabel->setText(key->timestamp.toString());
+
+ QString usage;
+ QTextStream usage_steam(&usage);
+
+ if(key->can_certify)
+ usage_steam << "Cert ";
+ if(key->can_encrypt)
+ usage_steam << "Encr ";
+ if(key->can_sign)
+ usage_steam << "Sign ";
+ if(key->can_authenticate)
+ usage_steam << "Auth ";
+
+ usageVarLabel->setText(usage);
+}
diff --git a/src/ui/keypair_details/KeyPairUIDTab.cpp b/src/ui/keypair_details/KeyPairUIDTab.cpp
index 40316163..73cd0bdb 100644
--- a/src/ui/keypair_details/KeyPairUIDTab.cpp
+++ b/src/ui/keypair_details/KeyPairUIDTab.cpp
@@ -124,6 +124,11 @@ void KeyPairUIDTab::slotRefreshUIDList() {
row++;
}
+
+ if(uidList->rowCount() > 0) {
+ uidList->selectRow(0);
+ }
+
slotRefreshSigList();
}
diff --git a/src/ui/widgets/KeyList.cpp b/src/ui/widgets/KeyList.cpp
index 2082bbc9..27e57817 100644
--- a/src/ui/widgets/KeyList.cpp
+++ b/src/ui/widgets/KeyList.cpp
@@ -86,6 +86,9 @@ KeyList::KeyList(GpgME::GpgContext *ctx,
setLayout(layout);
popupMenu = new QMenu(this);
+
+ connect(mKeyList, SIGNAL(doubleClicked(const QModelIndex &)),
+ this, SLOT(slotDoubleClicked(const QModelIndex &)));
connect(mCtx, SIGNAL(signalKeyInfoChanged()), this, SLOT(slotRefresh()));
setAcceptDrops(true);
slotRefresh();
@@ -434,5 +437,17 @@ void KeyList::setExcludeKeys(std::initializer_list<QString> key_ids) {
}
void KeyList::setFilter(std::function<bool(const GpgKey &)> filter) {
- this->mFilter = filter;
+ this->mFilter = std::move(filter);
+}
+
+void KeyList::slotDoubleClicked(const QModelIndex &index) {
+ if(mAction != nullptr) {
+ const auto &key = mCtx->getKeyById(buffered_keys[index.row()].id);
+ mAction(key, this);
+ }
+
+}
+
+void KeyList::setDoubleClickedAction(std::function<void(const GpgKey &, QWidget *)> action) {
+ this->mAction = std::move(action);
}