aboutsummaryrefslogtreecommitdiffstats
path: root/src/ui/main_window/KeyMgmt.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/main_window/KeyMgmt.cpp')
-rw-r--r--src/ui/main_window/KeyMgmt.cpp462
1 files changed, 462 insertions, 0 deletions
diff --git a/src/ui/main_window/KeyMgmt.cpp b/src/ui/main_window/KeyMgmt.cpp
new file mode 100644
index 00000000..6dc2b14f
--- /dev/null
+++ b/src/ui/main_window/KeyMgmt.cpp
@@ -0,0 +1,462 @@
+/**
+ * Copyright (C) 2021 Saturneric
+ *
+ * This file is part of GpgFrontend.
+ *
+ * GpgFrontend is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GpgFrontend is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GpgFrontend. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * The initial version of the source code is inherited from
+ * the gpg4usb project, which is under GPL-3.0-or-later.
+ *
+ * All the source code of GpgFrontend was modified and released by
+ * Saturneric<[email protected]> starting on May 12, 2021.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ */
+
+#include "KeyMgmt.h"
+
+#include <utility>
+
+#include "core/function/GlobalSettingStation.h"
+#include "core/function/KeyPackageOperator.h"
+#include "core/function/gpg/GpgKeyGetter.h"
+#include "core/function/gpg/GpgKeyImportExporter.h"
+#include "core/function/gpg/GpgKeyOpera.h"
+#include "ui/SignalStation.h"
+#include "ui/UserInterfaceUtils.h"
+#include "ui/dialog/import_export/ExportKeyPackageDialog.h"
+#include "ui/dialog/key_generate/SubkeyGenerateDialog.h"
+#include "ui/main_window/MainWindow.h"
+
+namespace GpgFrontend::UI {
+
+KeyMgmt::KeyMgmt(QWidget* parent)
+ : GeneralMainWindow("key_management", parent) {
+ /* the list of Keys available*/
+ key_list_ = new KeyList(KeyMenuAbility::ALL, this);
+
+ key_list_->AddListGroupTab(_("All"), KeyListRow::SECRET_OR_PUBLIC_KEY);
+
+ key_list_->AddListGroupTab(
+ _("Only Public Key"), KeyListRow::SECRET_OR_PUBLIC_KEY,
+ KeyListColumn::TYPE | KeyListColumn::NAME | KeyListColumn::EmailAddress |
+ KeyListColumn::Usage | KeyListColumn::Validity,
+ [](const GpgKey& key) -> bool {
+ return !key.IsPrivateKey() &&
+ !(key.IsRevoked() || key.IsDisabled() || key.IsExpired());
+ });
+
+ key_list_->AddListGroupTab(
+ _("Has Private Key"), KeyListRow::SECRET_OR_PUBLIC_KEY,
+ KeyListColumn::TYPE | KeyListColumn::NAME | KeyListColumn::EmailAddress |
+ KeyListColumn::Usage | KeyListColumn::Validity,
+ [](const GpgKey& key) -> bool {
+ return key.IsPrivateKey() &&
+ !(key.IsRevoked() || key.IsDisabled() || key.IsExpired());
+ });
+
+ key_list_->AddListGroupTab(
+ _("No Primary Key"), KeyListRow::SECRET_OR_PUBLIC_KEY,
+ KeyListColumn::TYPE | KeyListColumn::NAME | KeyListColumn::EmailAddress |
+ KeyListColumn::Usage | KeyListColumn::Validity,
+ [](const GpgKey& key) -> bool {
+ return !key.IsHasMasterKey() &&
+ !(key.IsRevoked() || key.IsDisabled() || key.IsExpired());
+ });
+
+ key_list_->AddListGroupTab(
+ _("Revoked"), KeyListRow::SECRET_OR_PUBLIC_KEY,
+ KeyListColumn::TYPE | KeyListColumn::NAME | KeyListColumn::EmailAddress |
+ KeyListColumn::Usage | KeyListColumn::Validity,
+ [](const GpgKey& key) -> bool { return key.IsRevoked(); });
+
+ key_list_->AddListGroupTab(
+ _("Expired"), KeyListRow::SECRET_OR_PUBLIC_KEY,
+ KeyListColumn::TYPE | KeyListColumn::NAME | KeyListColumn::EmailAddress |
+ KeyListColumn::Usage | KeyListColumn::Validity,
+ [](const GpgKey& key) -> bool { return key.IsExpired(); });
+
+ setCentralWidget(key_list_);
+ key_list_->SetDoubleClickedAction([this](const GpgKey& key, QWidget* parent) {
+ new KeyDetailsDialog(key, parent);
+ });
+
+ key_list_->SlotRefresh();
+
+ create_actions();
+ create_menus();
+ create_tool_bars();
+
+ connect(this, &KeyMgmt::SignalStatusBarChanged,
+ qobject_cast<MainWindow*>(this->parent()),
+ &MainWindow::SlotSetStatusBarText);
+
+ auto& settings = GlobalSettingStation::GetInstance().GetUISettings();
+
+ this->statusBar()->show();
+
+ setWindowTitle(_("KeyPair Management"));
+ key_list_->AddMenuAction(delete_selected_keys_act_);
+ key_list_->AddMenuAction(show_key_details_act_);
+
+ connect(this, &KeyMgmt::SignalKeyStatusUpdated, SignalStation::GetInstance(),
+ &SignalStation::SignalKeyDatabaseRefresh);
+ connect(SignalStation::GetInstance(), &SignalStation::SignalRefreshStatusBar,
+ this, [=](const QString& message, int timeout) {
+ statusBar()->showMessage(message, timeout);
+ });
+}
+
+void KeyMgmt::create_actions() {
+ open_key_file_act_ = new QAction(_("Open"), this);
+ open_key_file_act_->setShortcut(QKeySequence(_("Ctrl+O")));
+ open_key_file_act_->setToolTip(_("Open Key File"));
+ connect(open_key_file_act_, &QAction::triggered, this,
+ [&]() { CommonUtils::GetInstance()->SlotImportKeyFromFile(this); });
+
+ close_act_ = new QAction(_("Close"), this);
+ close_act_->setShortcut(QKeySequence(_("Ctrl+Q")));
+ close_act_->setIcon(QIcon(":exit.png"));
+ close_act_->setToolTip(_("Close"));
+ connect(close_act_, &QAction::triggered, this, &KeyMgmt::close);
+
+ generate_key_pair_act_ = new QAction(_("New Keypair"), this);
+ generate_key_pair_act_->setShortcut(QKeySequence(_("Ctrl+N")));
+ generate_key_pair_act_->setIcon(QIcon(":key_generate.png"));
+ generate_key_pair_act_->setToolTip(_("Generate KeyPair"));
+ connect(generate_key_pair_act_, &QAction::triggered, this,
+ &KeyMgmt::SlotGenerateKeyDialog);
+
+ generate_subkey_act_ = new QAction(_("New Subkey"), this);
+ generate_subkey_act_->setShortcut(QKeySequence(_("Ctrl+Shift+N")));
+ generate_subkey_act_->setIcon(QIcon(":key_generate.png"));
+ generate_subkey_act_->setToolTip(_("Generate Subkey For Selected KeyPair"));
+ connect(generate_subkey_act_, &QAction::triggered, this,
+ &KeyMgmt::SlotGenerateSubKey);
+
+ import_key_from_file_act_ = new QAction(_("File"), this);
+ import_key_from_file_act_->setIcon(QIcon(":import_key_from_file.png"));
+ import_key_from_file_act_->setToolTip(_("Import New Key From File"));
+ connect(import_key_from_file_act_, &QAction::triggered, this,
+ [&]() { CommonUtils::GetInstance()->SlotImportKeyFromFile(this); });
+
+ import_key_from_clipboard_act_ = new QAction(_("Clipboard"), this);
+ import_key_from_clipboard_act_->setIcon(
+ QIcon(":import_key_from_clipboard.png"));
+ import_key_from_clipboard_act_->setToolTip(
+ _("Import New Key From Clipboard"));
+ connect(import_key_from_clipboard_act_, &QAction::triggered, this, [&]() {
+ CommonUtils::GetInstance()->SlotImportKeyFromClipboard(this);
+ });
+
+ import_key_from_key_server_act_ = new QAction(_("Keyserver"), this);
+ import_key_from_key_server_act_->setIcon(
+ QIcon(":import_key_from_server.png"));
+ import_key_from_key_server_act_->setToolTip(
+ _("Import New Key From Keyserver"));
+ connect(import_key_from_key_server_act_, &QAction::triggered, this, [&]() {
+ CommonUtils::GetInstance()->SlotImportKeyFromKeyServer(this);
+ });
+
+ import_keys_from_key_package_act_ = new QAction(_("Key Package"), this);
+ import_keys_from_key_package_act_->setIcon(QIcon(":key_package.png"));
+ import_keys_from_key_package_act_->setToolTip(
+ _("Import Key(s) From a Key Package"));
+ connect(import_keys_from_key_package_act_, &QAction::triggered, this,
+ &KeyMgmt::SlotImportKeyPackage);
+
+ export_key_to_clipboard_act_ = new QAction(_("Export To Clipboard"), this);
+ export_key_to_clipboard_act_->setIcon(QIcon(":export_key_to_clipboard.png"));
+ export_key_to_clipboard_act_->setToolTip(
+ _("Export Selected Key(s) To Clipboard"));
+ connect(export_key_to_clipboard_act_, &QAction::triggered, this,
+ &KeyMgmt::SlotExportKeyToClipboard);
+
+ export_key_to_file_act_ = new QAction(_("Export To Key Package"), this);
+ export_key_to_file_act_->setIcon(QIcon(":key_package.png"));
+ export_key_to_file_act_->setToolTip(
+ _("Export Checked Key(s) To a Key Package"));
+ connect(export_key_to_file_act_, &QAction::triggered, this,
+ &KeyMgmt::SlotExportKeyToKeyPackage);
+
+ export_key_as_open_ssh_format_ = new QAction(_("Export As OpenSSH"), this);
+ export_key_as_open_ssh_format_->setIcon(QIcon(":ssh-key.png"));
+ export_key_as_open_ssh_format_->setToolTip(
+ _("Export Selected Key(s) As OpenSSH Format to File"));
+ connect(export_key_as_open_ssh_format_, &QAction::triggered, this,
+ &KeyMgmt::SlotExportAsOpenSSHFormat);
+
+ delete_selected_keys_act_ = new QAction(_("Delete Selected Key(s)"), this);
+ delete_selected_keys_act_->setToolTip(_("Delete the Selected keys"));
+ connect(delete_selected_keys_act_, &QAction::triggered, this,
+ &KeyMgmt::SlotDeleteSelectedKeys);
+
+ delete_checked_keys_act_ = new QAction(_("Delete Checked Key(s)"), this);
+ delete_checked_keys_act_->setToolTip(_("Delete the Checked keys"));
+ delete_checked_keys_act_->setIcon(QIcon(":button_delete.png"));
+ connect(delete_checked_keys_act_, &QAction::triggered, this,
+ &KeyMgmt::SlotDeleteCheckedKeys);
+
+ show_key_details_act_ = new QAction(_("Show Key Details"), this);
+ show_key_details_act_->setToolTip(_("Show Details for this Key"));
+ connect(show_key_details_act_, &QAction::triggered, this,
+ &KeyMgmt::SlotShowKeyDetails);
+}
+
+void KeyMgmt::create_menus() {
+ file_menu_ = menuBar()->addMenu(_("File"));
+ file_menu_->addAction(open_key_file_act_);
+ file_menu_->addAction(close_act_);
+
+ key_menu_ = menuBar()->addMenu(_("Key"));
+ generate_key_menu_ = key_menu_->addMenu(_("Generate Key"));
+ generate_key_menu_->addAction(generate_key_pair_act_);
+ generate_key_menu_->addAction(generate_subkey_act_);
+
+ import_key_menu_ = key_menu_->addMenu(_("Import Key"));
+ import_key_menu_->addAction(import_key_from_file_act_);
+ import_key_menu_->addAction(import_key_from_clipboard_act_);
+ import_key_menu_->addAction(import_key_from_key_server_act_);
+ import_key_menu_->addAction(import_keys_from_key_package_act_);
+
+ key_menu_->addAction(export_key_to_file_act_);
+ key_menu_->addAction(export_key_to_clipboard_act_);
+ key_menu_->addAction(export_key_as_open_ssh_format_);
+ key_menu_->addSeparator();
+ key_menu_->addAction(delete_checked_keys_act_);
+}
+
+void KeyMgmt::create_tool_bars() {
+ QToolBar* keyToolBar = addToolBar(_("Key"));
+ keyToolBar->setObjectName("keytoolbar");
+
+ // add button with popup menu for import
+ auto* generateToolButton = new QToolButton(this);
+ generateToolButton->setMenu(generate_key_menu_);
+ generateToolButton->setPopupMode(QToolButton::InstantPopup);
+ generateToolButton->setIcon(QIcon(":key_generate.png"));
+ generateToolButton->setText(_("Generate"));
+ generateToolButton->setToolTip(_("Generate A New Keypair or Subkey"));
+ generateToolButton->setToolButtonStyle(icon_style_);
+ keyToolBar->addWidget(generateToolButton);
+
+ // add button with popup menu for import
+ auto* toolButton = new QToolButton(this);
+ toolButton->setMenu(import_key_menu_);
+ toolButton->setPopupMode(QToolButton::InstantPopup);
+ toolButton->setIcon(QIcon(":key_import.png"));
+ toolButton->setToolTip(_("Import key"));
+ toolButton->setText(_("Import Key"));
+ toolButton->setToolButtonStyle(icon_style_);
+ keyToolBar->addWidget(toolButton);
+
+ keyToolBar->addSeparator();
+ keyToolBar->addAction(delete_checked_keys_act_);
+ keyToolBar->addSeparator();
+ keyToolBar->addAction(export_key_to_file_act_);
+ keyToolBar->addAction(export_key_to_clipboard_act_);
+ keyToolBar->addAction(export_key_as_open_ssh_format_);
+}
+
+void KeyMgmt::SlotDeleteSelectedKeys() {
+ delete_keys_with_warning(key_list_->GetSelected());
+}
+
+void KeyMgmt::SlotDeleteCheckedKeys() {
+ delete_keys_with_warning(key_list_->GetChecked());
+}
+
+void KeyMgmt::delete_keys_with_warning(KeyIdArgsListPtr uidList) {
+ /**
+ * TODO: Different Messages for private/public key, check if
+ * more than one selected... compare to seahorse "delete-dialog"
+ */
+
+ if (uidList->empty()) return;
+ QString keynames;
+ for (const auto& key_id : *uidList) {
+ auto key = GpgKeyGetter::GetInstance().GetKey(key_id);
+ if (!key.IsGood()) continue;
+ keynames.append(QString::fromStdString(key.GetName()));
+ keynames.append("<i> &lt;");
+ keynames.append(QString::fromStdString(key.GetEmail()));
+ keynames.append("&gt; </i><br/>");
+ }
+
+ int ret = QMessageBox::warning(
+ this, _("Deleting Keys"),
+ "<b>" +
+ QString(
+ _("Are you sure that you want to delete the following keys?")) +
+ "</b><br/><br/>" + keynames + +"<br/>" +
+ _("The action can not be undone."),
+ QMessageBox::No | QMessageBox::Yes);
+
+ if (ret == QMessageBox::Yes) {
+ GpgKeyOpera::GetInstance().DeleteKeys(std::move(uidList));
+ emit SignalKeyStatusUpdated();
+ }
+}
+
+void KeyMgmt::SlotShowKeyDetails() {
+ auto keys_selected = key_list_->GetSelected();
+ if (keys_selected->empty()) return;
+
+ auto key = GpgKeyGetter::GetInstance().GetKey(keys_selected->front());
+
+ if (!key.IsGood()) {
+ QMessageBox::critical(this, _("Error"), _("Key Not Found."));
+ return;
+ }
+
+ new KeyDetailsDialog(key);
+}
+
+void KeyMgmt::SlotExportKeyToKeyPackage() {
+ auto keys_checked = key_list_->GetChecked();
+ if (keys_checked->empty()) {
+ QMessageBox::critical(
+ this, _("Forbidden"),
+ _("Please check some keys before doing this operation."));
+ return;
+ }
+ auto dialog = new ExportKeyPackageDialog(std::move(keys_checked), this);
+ dialog->exec();
+ emit SignalStatusBarChanged(QString(_("key(s) exported")));
+}
+
+void KeyMgmt::SlotExportKeyToClipboard() {
+ auto keys_checked = key_list_->GetChecked();
+ if (keys_checked->empty()) {
+ QMessageBox::critical(
+ this, _("Forbidden"),
+ _("Please check some keys before doing this operation."));
+ return;
+ }
+
+ ByteArrayPtr key_export_data = nullptr;
+ if (!GpgKeyImportExporter::GetInstance().ExportKeys(keys_checked,
+ key_export_data)) {
+ return;
+ }
+ QApplication::clipboard()->setText(QString::fromStdString(*key_export_data));
+}
+
+void KeyMgmt::SlotGenerateKeyDialog() {
+ auto* keyGenDialog = new KeyGenDialog(this);
+ keyGenDialog->show();
+}
+
+void KeyMgmt::SlotGenerateSubKey() {
+ auto keys_selected = key_list_->GetSelected();
+ if (keys_selected->empty()) {
+ QMessageBox::information(
+ this, _("Invalid Operation"),
+ _("Please select one KeyPair before doing this operation."));
+ return;
+ }
+ const auto key = GpgKeyGetter::GetInstance().GetKey(keys_selected->front());
+ if (!key.IsGood()) {
+ QMessageBox::critical(this, _("Error"), _("Key Not Found."));
+ return;
+ }
+ if (!key.IsPrivateKey()) {
+ QMessageBox::critical(this, _("Invalid Operation"),
+ _("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(key.GetId(), this);
+ dialog->show();
+}
+
+void KeyMgmt::SlotExportAsOpenSSHFormat() {
+ ByteArrayPtr key_export_data = nullptr;
+ auto keys_checked = key_list_->GetChecked();
+
+ if (keys_checked->empty()) {
+ QMessageBox::critical(
+ this, _("Forbidden"),
+ _("Please select a key before performing this operation. If you select "
+ "multiple keys, only the first key will be exported."));
+ return;
+ }
+
+ auto key = GpgKeyGetter::GetInstance().GetKey(keys_checked->front());
+ if (!GpgKeyImportExporter::GetInstance().ExportKeyOpenSSH(key,
+ key_export_data)) {
+ QMessageBox::critical(this, _("Error"), _("An error occur in exporting."));
+ return;
+ }
+
+ if (key_export_data->empty()) {
+ QMessageBox::critical(
+ this, _("Error"),
+ _("This key may not be able to export as OpenSSH format. Please check "
+ "the key-size of the subkey(s) used to sign."));
+ return;
+ }
+
+ key = GpgKeyGetter::GetInstance().GetKey(keys_checked->front());
+ if (!key.IsGood()) {
+ QMessageBox::critical(this, _("Error"), _("Key Not Found."));
+ return;
+ }
+ QString fileString = QString::fromStdString(
+ key.GetName() + " " + key.GetEmail() + "(" + key.GetId() + ").pub");
+
+ QString file_name = QFileDialog::getSaveFileName(
+ this, _("Export OpenSSH Key To File"), fileString,
+ QString(_("OpenSSH Public Key Files")) + " (*.pub);;All Files (*)");
+
+ if (!file_name.isEmpty()) {
+ write_buffer_to_file(file_name.toStdString(), *key_export_data);
+ emit SignalStatusBarChanged(QString(_("key(s) exported")));
+ }
+}
+
+void KeyMgmt::SlotImportKeyPackage() {
+ LOG(INFO) << "Importing key package...";
+
+ auto key_package_file_name = QFileDialog::getOpenFileName(
+ this, _("Import Key Package"), {},
+ QString(_("Key Package")) + " (*.gfepack);;All Files (*)");
+
+ auto key_file_name = QFileDialog::getOpenFileName(
+ this, _("Import Key Package Passphrase File"), {},
+ QString(_("Key Package Passphrase File")) + " (*.key);;All Files (*)");
+
+ if (key_package_file_name.isEmpty() || key_file_name.isEmpty()) return;
+
+ GpgImportInformation info;
+
+ LOG(INFO) << "Importing key package: " << key_package_file_name.toStdString();
+
+ if (KeyPackageOperator::ImportKeyPackage(key_package_file_name.toStdString(),
+ key_file_name.toStdString(), info)) {
+ emit SignalStatusBarChanged(QString(_("key(s) imported")));
+ emit SignalKeyStatusUpdated();
+
+ auto dialog = new KeyImportDetailDialog(info, false, this);
+ dialog->exec();
+ } else {
+ QMessageBox::critical(this, _("Error"),
+ _("An error occur in importing key package."));
+ }
+}
+
+} // namespace GpgFrontend::UI