aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSaturneric <[email protected]>2021-05-29 19:28:40 +0000
committerSaturneric <[email protected]>2021-05-29 19:28:40 +0000
commitfe67bfb777a79b25149efa3e045dc043dc144ad0 (patch)
tree4d75a10f210b86d7e116d188569bc08e4e72adb7 /src
parentMake eligible keys enter the signature candidate list. (diff)
downloadGpgFrontend-fe67bfb777a79b25149efa3e045dc043dc144ad0.tar.gz
GpgFrontend-fe67bfb777a79b25149efa3e045dc043dc144ad0.zip
New page and function for generating subkeys.
Define the interface and functions of the subkey management tab. When there is an item in the UID list, the first item is selected by default. Compile the API for generating sub-keys and the corresponding calling thread. Set GpgGenKeyInfo to apply to the subkey Generate a subkey for the selected key pair of the management key pair interface. Adjust the project structure and add a new classification key generation category. Double-click the item in KeyList in the key pair management interface to enter the key details page. Adjust the title of the key pair management interface. Optimize part of the code of KeyGenThread. Signed-off-by: Saturneric <[email protected]>
Diffstat (limited to '')
-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
11 files changed, 574 insertions, 36 deletions
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);
}