aboutsummaryrefslogtreecommitdiffstats
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/function/gpg/GpgAdvancedOperator.cpp7
-rw-r--r--src/core/function/gpg/GpgAssuanHelper.cpp24
-rw-r--r--src/core/function/gpg/GpgAssuanHelper.h75
-rw-r--r--src/core/function/gpg/GpgAutomatonHandler.cpp25
-rw-r--r--src/core/function/gpg/GpgAutomatonHandler.h38
-rw-r--r--src/core/function/gpg/GpgCommandExecutor.cpp32
-rw-r--r--src/core/function/gpg/GpgCommandExecutor.h13
-rw-r--r--src/core/function/gpg/GpgContext.cpp4
-rw-r--r--src/core/function/gpg/GpgSmartCardManager.cpp87
-rw-r--r--src/core/function/gpg/GpgSmartCardManager.h (renamed from src/core/model/GpgCardKeyPairInfo.h)48
-rw-r--r--src/core/model/GpgCardKeyPairInfo.cpp73
-rw-r--r--src/core/model/GpgOpenPGPCard.cpp172
-rw-r--r--src/core/model/GpgOpenPGPCard.h86
13 files changed, 534 insertions, 150 deletions
diff --git a/src/core/function/gpg/GpgAdvancedOperator.cpp b/src/core/function/gpg/GpgAdvancedOperator.cpp
index 0b103c9b..492297c6 100644
--- a/src/core/function/gpg/GpgAdvancedOperator.cpp
+++ b/src/core/function/gpg/GpgAdvancedOperator.cpp
@@ -54,13 +54,18 @@ void ExecuteGpgCommand(const QString &operation, const QStringList &extra_args,
std::atomic<int> completed_tasks{0};
std::vector<int> results(total_tasks, 0);
+ // kill default gpg-agent
+ key_dbs.push_back({});
+
int task_index = 0;
for (const auto &key_db : key_dbs) {
const int current_index = task_index++;
const auto target_home_dir =
QDir::toNativeSeparators(QFileInfo(key_db.path).canonicalFilePath());
- QStringList arguments = QStringList{"--homedir", target_home_dir};
+ QStringList arguments = !target_home_dir.isEmpty()
+ ? QStringList{"--homedir", target_home_dir}
+ : QStringList{};
arguments.append(extra_args);
GpgCommandExecutor::ExecuteSync(
diff --git a/src/core/function/gpg/GpgAssuanHelper.cpp b/src/core/function/gpg/GpgAssuanHelper.cpp
index 2351b9a2..8fa311dc 100644
--- a/src/core/function/gpg/GpgAssuanHelper.cpp
+++ b/src/core/function/gpg/GpgAssuanHelper.cpp
@@ -51,7 +51,7 @@ auto GpgAssuanHelper::ConnectToSocket(GpgComponentType type) -> bool {
auto socket_path = ctx_.ComponentDirectory(type);
if (socket_path.isEmpty()) {
- LOG_F() << "socket path of component: " << component_type_to_q_string(type)
+ LOG_W() << "socket path of component: " << component_type_to_q_string(type)
<< " is empty";
return false;
}
@@ -65,7 +65,7 @@ auto GpgAssuanHelper::ConnectToSocket(GpgComponentType type) -> bool {
launch_component(type);
if (!info.exists()) {
- LOG_F() << "socket path is still not exists: " << socket_path
+ LOG_W() << "socket path is still not exists: " << socket_path
<< "abort...";
return false;
}
@@ -77,17 +77,17 @@ auto GpgAssuanHelper::ConnectToSocket(GpgComponentType type) -> bool {
auto err = assuan_socket_connect(a_ctx, info.absoluteFilePath().toUtf8(),
ASSUAN_INVALID_PID, 0);
if (err != GPG_ERR_NO_ERROR) {
- LOG_F() << "failed to connect to socket:" << CheckGpgError(err);
+ LOG_W() << "failed to connect to socket:" << CheckGpgError(err);
return false;
}
LOG_D() << "connected to socket by assuan protocol: "
- << info.absoluteFilePath();
+ << info.absoluteFilePath() << "channel:" << GetChannel();
err = assuan_transact(a_ctx, "GETINFO pid", simple_data_callback, nullptr,
nullptr, nullptr, nullptr, nullptr);
if (err != GPG_ERR_NO_ERROR) {
- LOG_F() << "failed to test assuan connection:" << CheckGpgError(err);
+ LOG_W() << "failed to test assuan connection:" << CheckGpgError(err);
return false;
}
@@ -111,12 +111,19 @@ auto GpgAssuanHelper::SendCommand(GpgComponentType type, const QString& command,
context->status_cb = std::move(status_cb);
context->inquery_cb = std::move(inquery_cb);
+ LOG_D() << "sending assuan command: " << command;
+
auto err = assuan_transact(
assuan_ctx_[type], command.toUtf8(), default_data_callback, &context,
default_inquery_callback, &context, default_status_callback, &context);
if (err != GPG_ERR_NO_ERROR) {
- LOG_F() << "failed to send assuan command :" << CheckGpgError(err);
+ LOG_W() << "failed to send assuan command:" << CheckGpgError(err);
+
+ // broken pipe error, try reconnect next time
+ if (CheckGpgError(err) == 32877) {
+ assuan_ctx_.remove(type);
+ }
return false;
}
@@ -153,7 +160,6 @@ auto GpgAssuanHelper::SendStatusCommand(GpgComponentType type,
};
auto ret = SendCommand(type, command, d_cb, i_cb, s_cb);
-
return {ret, status_lines};
}
@@ -185,7 +191,7 @@ auto GpgAssuanHelper::default_inquery_callback(
void GpgAssuanHelper::launch_component(GpgComponentType type) {
if (gpgconf_path_.isEmpty()) {
- LOG_F() << "gpgconf_path is not collected by initializing";
+ LOG_W() << "gpgconf_path is not collected by initializing";
return;
}
@@ -199,7 +205,7 @@ void GpgAssuanHelper::launch_component(GpgComponentType type) {
process.start();
if (!process.waitForFinished()) {
- LOG_F() << "failed to execute gpgconf" << process.arguments();
+ LOG_E() << "failed to execute gpgconf" << process.arguments();
return;
}
}
diff --git a/src/core/function/gpg/GpgAssuanHelper.h b/src/core/function/gpg/GpgAssuanHelper.h
index 65ec325f..6e58e27c 100644
--- a/src/core/function/gpg/GpgAssuanHelper.h
+++ b/src/core/function/gpg/GpgAssuanHelper.h
@@ -62,15 +62,49 @@ class GPGFRONTEND_CORE_EXPORT GpgAssuanHelper
[[nodiscard]] auto SendData(const QByteArray& b) const -> gpg_error_t;
};
+ /**
+ * @brief Construct a new Gpg Assuan Helper object
+ *
+ * @param channel
+ */
explicit GpgAssuanHelper(int channel);
+
+ /**
+ * @brief Destroy the Gpg Assuan Helper object
+ *
+ */
~GpgAssuanHelper();
+ /**
+ * @brief
+ *
+ * @return true
+ * @return false
+ */
auto ConnectToSocket(GpgComponentType) -> bool;
+ /**
+ * @brief
+ *
+ * @param type
+ * @param command
+ * @param data_cb
+ * @param inquery_cb
+ * @param status_cb
+ * @return true
+ * @return false
+ */
auto SendCommand(GpgComponentType type, const QString& command,
DataCallback data_cb, InqueryCallback inquery_cb,
StatusCallback status_cb) -> bool;
+ /**
+ * @brief
+ *
+ * @param type
+ * @param command
+ * @return std::tuple<bool, QStringList>
+ */
auto SendStatusCommand(GpgComponentType type, const QString& command)
-> std::tuple<bool, QStringList>;
@@ -79,19 +113,60 @@ class GPGFRONTEND_CORE_EXPORT GpgAssuanHelper
GpgContext::GetInstance(SingletonFunctionObject::GetChannel());
QMap<GpgComponentType, assuan_context_t> assuan_ctx_;
+ /**
+ * @brief
+ *
+ * @param type
+ */
void launch_component(GpgComponentType type);
+ /**
+ * @brief
+ *
+ * @param type
+ * @return QString
+ */
static auto component_type_to_q_string(GpgComponentType type) -> QString;
+ /**
+ * @brief
+ *
+ * @param opaque
+ * @param buffer
+ * @param length
+ * @return gpgme_error_t
+ */
static auto simple_data_callback(void* opaque, const void* buffer,
size_t length) -> gpgme_error_t;
+ /**
+ * @brief
+ *
+ * @param opaque
+ * @param buffer
+ * @param length
+ * @return gpgme_error_t
+ */
static auto default_data_callback(void* opaque, const void* buffer,
size_t length) -> gpgme_error_t;
+ /**
+ * @brief
+ *
+ * @param opaque
+ * @param status
+ * @return gpgme_error_t
+ */
static auto default_status_callback(void* opaque,
const char* status) -> gpgme_error_t;
+ /**
+ * @brief
+ *
+ * @param opaque
+ * @param inquery
+ * @return gpgme_error_t
+ */
static auto default_inquery_callback(void* opaque,
const char* inquery) -> gpgme_error_t;
diff --git a/src/core/function/gpg/GpgAutomatonHandler.cpp b/src/core/function/gpg/GpgAutomatonHandler.cpp
index 802279ed..af2f0cba 100644
--- a/src/core/function/gpg/GpgAutomatonHandler.cpp
+++ b/src/core/function/gpg/GpgAutomatonHandler.cpp
@@ -28,6 +28,8 @@
#include "GpgAutomatonHandler.h"
+#include <utility>
+
#include "core/model/GpgData.h"
#include "core/model/GpgKey.h"
#include "core/utils/GpgUtils.h"
@@ -96,21 +98,30 @@ auto GpgAutomatonHandler::interator_cb_func(void* handle, const char* status,
auto GpgAutomatonHandler::DoInteract(
const GpgKey& key, AutomatonNextStateHandler next_state_handler,
- AutomatonActionHandler action_handler) -> bool {
- auto key_fpr = key.Fingerprint();
- AutomatonHandelStruct handel_struct(key_fpr);
+ AutomatonActionHandler action_handler, int flags) -> bool {
+ gpgme_key_t p_key =
+ flags == GPGME_INTERACT_CARD ? nullptr : static_cast<gpgme_key_t>(key);
+
+ AutomatonHandelStruct handel_struct(
+ flags == GPGME_INTERACT_CARD ? "" : key.Fingerprint());
handel_struct.SetHandler(std::move(next_state_handler),
std::move(action_handler));
GpgData data_out;
- auto err =
- gpgme_op_interact(ctx_.DefaultContext(), static_cast<gpgme_key_t>(key), 0,
- GpgAutomatonHandler::interator_cb_func,
- static_cast<void*>(&handel_struct), data_out);
+ auto err = gpgme_op_interact(ctx_.DefaultContext(), p_key, flags,
+ GpgAutomatonHandler::interator_cb_func,
+ static_cast<void*>(&handel_struct), data_out);
return CheckGpgError(err) == GPG_ERR_NO_ERROR && handel_struct.Success();
}
+auto GpgAutomatonHandler::DoCardInteract(
+ AutomatonNextStateHandler next_state_handler,
+ AutomatonActionHandler action_handler) -> bool {
+ return DoInteract({}, std::move(next_state_handler),
+ std::move(action_handler), GPGME_INTERACT_CARD);
+}
+
auto GpgAutomatonHandler::AutomatonHandelStruct::NextState(
QString gpg_status, QString args) -> AutomatonState {
return next_state_handler_(current_state_, std::move(gpg_status),
diff --git a/src/core/function/gpg/GpgAutomatonHandler.h b/src/core/function/gpg/GpgAutomatonHandler.h
index 78b20252..f86299e8 100644
--- a/src/core/function/gpg/GpgAutomatonHandler.h
+++ b/src/core/function/gpg/GpgAutomatonHandler.h
@@ -87,16 +87,46 @@ class GpgAutomatonHandler
explicit GpgAutomatonHandler(
int channel = SingletonFunctionObject::GetDefaultChannel());
+ /**
+ * @brief
+ *
+ * @param key
+ * @param next_state_handler
+ * @param action_handler
+ * @param flags
+ * @return true
+ * @return false
+ */
auto DoInteract(const GpgKey& key,
AutomatonNextStateHandler next_state_handler,
- AutomatonActionHandler action_handler) -> bool;
+ AutomatonActionHandler action_handler, int flags = 0) -> bool;
- private:
- static auto interator_cb_func(void* handle, const char* status,
- const char* args, int fd) -> gpgme_error_t;
+ /**
+ * @brief
+ *
+ * @param next_state_handler
+ * @param action_handler
+ * @return true
+ * @return false
+ */
+ auto DoCardInteract(AutomatonNextStateHandler next_state_handler,
+ AutomatonActionHandler action_handler) -> bool;
+ private:
GpgContext& ctx_ =
GpgContext::GetInstance(SingletonFunctionObject::GetChannel()); ///<
+
+ /**
+ * @brief
+ *
+ * @param handle
+ * @param status
+ * @param args
+ * @param fd
+ * @return gpgme_error_t
+ */
+ static auto interator_cb_func(void* handle, const char* status,
+ const char* args, int fd) -> gpgme_error_t;
};
using AutomatonNextStateHandler =
diff --git a/src/core/function/gpg/GpgCommandExecutor.cpp b/src/core/function/gpg/GpgCommandExecutor.cpp
index a4ab8990..191c1259 100644
--- a/src/core/function/gpg/GpgCommandExecutor.cpp
+++ b/src/core/function/gpg/GpgCommandExecutor.cpp
@@ -31,6 +31,7 @@
#include "core/model/DataObject.h"
#include "core/module/Module.h"
+#include "core/module/ModuleManager.h"
#include "core/thread/Task.h"
#include "core/thread/TaskRunnerGetter.h"
@@ -234,4 +235,35 @@ GpgCommandExecutor::ExecuteContext::ExecuteContext(
int_func(std::move(int_func)),
task_runner(std::move(task_runner)) {}
+GpgCommandExecutor::GpgCommandExecutor(int channel)
+ : GpgFrontend::SingletonFunctionObject<GpgCommandExecutor>(channel) {}
+
+void GpgCommandExecutor::GpgExecuteSync(const ExecuteContext &context) {
+ const auto gpg_path = Module::RetrieveRTValueTypedOrDefault<>(
+ "core", "gpgme.ctx.app_path", QString{});
+
+ if (context.cmd.isEmpty() && gpg_path.isEmpty()) {
+ LOG_E() << "failed to execute gpg command, gpg binary path is empty.";
+ return;
+ }
+
+ LOG_D() << "got gpg binary path:" << gpg_path;
+ LOG_D() << "context channel:" << GetChannel()
+ << "home path: " << ctx_.HomeDirectory();
+
+ ExecuteContext ctx = {
+ context.cmd.isEmpty() ? gpg_path : context.cmd,
+ context.arguments,
+ context.cb_func,
+ context.task_runner,
+ context.int_func,
+ };
+
+ if (!ctx.arguments.contains("--homedir") && !ctx_.HomeDirectory().isEmpty()) {
+ ctx.arguments.append("--homedir");
+ ctx.arguments.append(ctx_.HomeDirectory());
+ }
+
+ return ExecuteSync(ctx);
+}
} // namespace GpgFrontend \ No newline at end of file
diff --git a/src/core/function/gpg/GpgCommandExecutor.h b/src/core/function/gpg/GpgCommandExecutor.h
index 5a2f13db..fd2181d3 100644
--- a/src/core/function/gpg/GpgCommandExecutor.h
+++ b/src/core/function/gpg/GpgCommandExecutor.h
@@ -28,6 +28,8 @@
#pragma once
+#include "core/function/basic/GpgFunctionObject.h"
+#include "core/function/gpg/GpgContext.h"
#include "core/module/Module.h"
namespace GpgFrontend {
@@ -39,7 +41,8 @@ using GpgCommandExecutorInterator = std::function<void(QProcess *)>;
* @brief Extra commands related to GPG
*
*/
-class GPGFRONTEND_CORE_EXPORT GpgCommandExecutor {
+class GPGFRONTEND_CORE_EXPORT GpgCommandExecutor
+ : public SingletonFunctionObject<GpgCommandExecutor> {
public:
struct GPGFRONTEND_CORE_EXPORT ExecuteContext {
QString cmd;
@@ -58,6 +61,8 @@ class GPGFRONTEND_CORE_EXPORT GpgCommandExecutor {
using ExecuteContexts = QContainer<ExecuteContext>;
+ explicit GpgCommandExecutor(int channel = kGpgFrontendDefaultChannel);
+
/**
* @brief Excuting a command
*
@@ -69,6 +74,12 @@ class GPGFRONTEND_CORE_EXPORT GpgCommandExecutor {
static void ExecuteConcurrentlyAsync(ExecuteContexts);
static void ExecuteConcurrentlySync(ExecuteContexts);
+
+ void GpgExecuteSync(const ExecuteContext &);
+
+ private:
+ GpgContext &ctx_ =
+ GpgContext::GetInstance(SingletonFunctionObject::GetChannel());
};
} // namespace GpgFrontend
diff --git a/src/core/function/gpg/GpgContext.cpp b/src/core/function/gpg/GpgContext.cpp
index ce84ed2e..88eb3c1b 100644
--- a/src/core/function/gpg/GpgContext.cpp
+++ b/src/core/function/gpg/GpgContext.cpp
@@ -386,11 +386,11 @@ class GpgContext::Impl {
QProcess process;
process.setProgram(gpgconf_path);
- process.setArguments({"--list-dirs"});
+ process.setArguments({"--list-dirs", "--homedir", database_path_});
process.start();
if (!process.waitForFinished()) {
- LOG_F() << "failed to execute gpgconf --list-dirs";
+ LOG_W() << "failed to execute gpgconf --list-dirs";
return;
}
diff --git a/src/core/function/gpg/GpgSmartCardManager.cpp b/src/core/function/gpg/GpgSmartCardManager.cpp
new file mode 100644
index 00000000..06010756
--- /dev/null
+++ b/src/core/function/gpg/GpgSmartCardManager.cpp
@@ -0,0 +1,87 @@
+/**
+ * Copyright (C) 2021-2024 Saturneric <[email protected]>
+ *
+ * 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 "GpgSmartCardManager.h"
+
+#include "core/function/gpg/GpgAutomatonHandler.h"
+
+namespace GpgFrontend {
+
+GpgSmartCardManager::GpgSmartCardManager(int channel)
+ : SingletonFunctionObject<GpgSmartCardManager>(channel) {}
+
+auto GpgSmartCardManager::Fetch(const QString& serial_number) -> bool {
+ GpgAutomatonHandler::AutomatonNextStateHandler next_state_handler =
+ [=](AutomatonState state, QString status, QString args) {
+ auto tokens = args.split(' ');
+
+ switch (state) {
+ case GpgAutomatonHandler::AS_START:
+ if (status == "CARDCTRL" && args.contains(serial_number)) {
+ return GpgAutomatonHandler::AS_START;
+ } else if (status == "GET_LINE" && args == "cardedit.prompt") {
+ return GpgAutomatonHandler::AS_COMMAND;
+ }
+ return GpgAutomatonHandler::AS_ERROR;
+ case GpgAutomatonHandler::AS_COMMAND:
+ if (status == "GET_LINE" && args == "cardedit.prompt") {
+ return GpgAutomatonHandler::AS_QUIT;
+ }
+ return GpgAutomatonHandler::AS_ERROR;
+ case GpgAutomatonHandler::AS_QUIT:
+ case GpgAutomatonHandler::AS_ERROR:
+ if (status == "GET_LINE" && args == "keyedit.prompt") {
+ return GpgAutomatonHandler::AS_QUIT;
+ }
+ return GpgAutomatonHandler::AS_ERROR;
+ default:
+ return GpgAutomatonHandler::AS_ERROR;
+ };
+ };
+
+ AutomatonActionHandler action_handler = [](AutomatonHandelStruct& handler,
+ AutomatonState state) {
+ switch (state) {
+ case GpgAutomatonHandler::AS_COMMAND:
+ return QString("fetch");
+ case GpgAutomatonHandler::AS_QUIT:
+ return QString("quit");
+ case GpgAutomatonHandler::AS_START:
+ case GpgAutomatonHandler::AS_ERROR:
+ return QString("");
+ default:
+ return QString("");
+ }
+ return QString("");
+ };
+
+ return GpgAutomatonHandler::GetInstance(GetChannel())
+ .DoCardInteract(next_state_handler, action_handler);
+}
+
+} // namespace GpgFrontend \ No newline at end of file
diff --git a/src/core/model/GpgCardKeyPairInfo.h b/src/core/function/gpg/GpgSmartCardManager.h
index 132ded84..9c8cc8bb 100644
--- a/src/core/model/GpgCardKeyPairInfo.h
+++ b/src/core/function/gpg/GpgSmartCardManager.h
@@ -28,21 +28,47 @@
#pragma once
+#include "core/function/basic/GpgFunctionObject.h"
+#include "core/function/gpg/GpgContext.h"
+#include "core/typedef/GpgTypedef.h"
+
namespace GpgFrontend {
-struct GpgCardKeyPairInfo {
- explicit GpgCardKeyPairInfo(const QString &status);
+/**
+ * @brief
+ *
+ */
+class GPGFRONTEND_CORE_EXPORT GpgSmartCardManager
+ : public SingletonFunctionObject<GpgSmartCardManager> {
+ public:
+ /**
+ * @brief Construct a new Gpg Key Manager object
+ *
+ * @param channel
+ */
+ explicit GpgSmartCardManager(
+ int channel = SingletonFunctionObject::GetDefaultChannel());
+
+ /**
+ * @brief
+ *
+ * @param key
+ * @param subkey_index
+ * @return true
+ * @return false
+ */
+ auto Fetch(const QString& serial_number) -> bool;
- [[nodiscard]] auto CanAuthenticate() const -> bool;
- [[nodiscard]] auto CanCertify() const -> bool;
- [[nodiscard]] auto CanEncrypt() const -> bool;
- [[nodiscard]] auto CanSign() const -> bool;
+ /**
+ * @brief
+ *
+ * @return std::tuple<bool, QString>
+ */
+ auto ModifyAttr() -> std::tuple<bool, QString>;
- QString key_ref;
- QString grip;
- QString usage;
- QDateTime time;
- QString algorithm;
+ private:
+ GpgContext& ctx_ =
+ GpgContext::GetInstance(SingletonFunctionObject::GetChannel()); ///<
};
} // namespace GpgFrontend
diff --git a/src/core/model/GpgCardKeyPairInfo.cpp b/src/core/model/GpgCardKeyPairInfo.cpp
deleted file mode 100644
index 481d88ea..00000000
--- a/src/core/model/GpgCardKeyPairInfo.cpp
+++ /dev/null
@@ -1,73 +0,0 @@
-/**
- * Copyright (C) 2021-2024 Saturneric <[email protected]>
- *
- * 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 "GpgCardKeyPairInfo.h"
-
-namespace GpgFrontend {
-
-GpgCardKeyPairInfo::GpgCardKeyPairInfo(const QString &status) {
- const auto values = status.split(QLatin1Char(' '));
- if (values.size() < 2) {
- return;
- }
-
- grip = values[0];
- key_ref = values[1];
- if (values.size() >= 3) {
- usage = values[2];
- }
-
- if (values.size() >= 4 && !values[3].isEmpty() && values[3] != "-") {
- bool ok;
- const qint64 seconds_since_epoch = values[3].toLongLong(&ok);
- if (ok) {
- time =
- QDateTime::fromSecsSinceEpoch(seconds_since_epoch, QTimeZone::utc());
- }
- }
-
- if (values.size() >= 5) {
- algorithm = values[4];
- }
-}
-
-auto GpgCardKeyPairInfo::CanAuthenticate() const -> bool {
- return usage.contains('a');
-}
-
-auto GpgCardKeyPairInfo::CanCertify() const -> bool {
- return usage.contains('c');
-}
-
-auto GpgCardKeyPairInfo::CanEncrypt() const -> bool {
- return usage.contains('e');
-}
-
-auto GpgCardKeyPairInfo::CanSign() const -> bool { return usage.contains('s'); }
-
-} // namespace GpgFrontend
diff --git a/src/core/model/GpgOpenPGPCard.cpp b/src/core/model/GpgOpenPGPCard.cpp
index f0d8c9cd..ddc9a40e 100644
--- a/src/core/model/GpgOpenPGPCard.cpp
+++ b/src/core/model/GpgOpenPGPCard.cpp
@@ -28,7 +28,6 @@
#include "GpgOpenPGPCard.h"
-#include "core/model/GpgCardKeyPairInfo.h"
#include "core/utils/CommonUtils.h"
namespace GpgFrontend {
@@ -46,39 +45,33 @@ void GpgFrontend::GpgOpenPGPCard::parse_card_info(const QString& name,
std::reverse(list.begin(), list.end());
card_holder =
list.join(QLatin1Char(' ')).replace(QLatin1Char('<'), QLatin1Char(' '));
- } else if (name == "KEYPAIRINFO") {
- const GpgCardKeyPairInfo info = GpgCardKeyPairInfo(value);
- if (info.grip.isEmpty()) {
- LOG_W() << "invalid KEYPAIRINFO status line" << value;
- good = false;
- }
- } else if (name == "KEY-FPR") {
- const auto values = value.split(QLatin1Char(' '));
- if (values.size() < 2) {
- LOG_W() << "invalid KEY-FPR status line" << value;
- good = false;
- return;
- }
-
- const auto& key_number = values[0].toInt();
- const auto& fpr = values[1];
- fprs.insert(key_number, fpr);
-
+ } else if (name == "KEYPAIRINFO" || name == "KEY-FPR" || name == "KEY-TIME") {
+ parse_card_key_info(name, value);
} else if (name == "MANUFACTURER") {
- // the value of MANUFACTURER is the manufacturer ID as unsigned number
- // optionally followed by the name of the manufacturer, e.g.
- // 6 Yubico
- // 65534 unmanaged S/N range
- // for PKCS#15 cards the manufacturer ID is always 0, e.g.
- // 0 www.atos.net/cardos [R&S]
- auto space_index = value.indexOf(' ');
- if (space_index != -1) {
- card_infos.insert(name, value.mid(space_index + 1).trimmed());
- }
+ const auto values = value.split(QLatin1Char(' '));
+ if (values.size() < 2) return;
+ manufacturer_id = values.front().toInt();
+ manufacturer = values.back();
+ } else if (name == "DISP-SEX") {
+ display_sex = value == "1" ? "Male" : value == "2" ? "Female" : "Unknown";
+ } else if (name == "CHV-STATUS") {
+ parse_chv_status(value);
+ } else if (name == "EXTCAP") {
+ parse_ext_capability(value);
+ } else if (name == "KDF") {
+ parse_kdf_status(value);
+ } else if (name.startsWith("UIF-")) {
+ parse_uif(name, value);
} else {
- card_infos.insert(name, value);
+ additional_card_infos.insert(name, value);
}
+
+ reader = additional_card_infos.value("READER").replace('+', ' ');
+ serial_number = additional_card_infos.value("SERIALNO");
+ app_type = additional_card_infos.value("APPTYPE");
+ display_language = additional_card_infos.value("DISP-LANG");
}
+
GpgOpenPGPCard::GpgOpenPGPCard(const QStringList& status) : good(true) {
for (const QString& line : status) {
auto tokens = line.split(' ', Qt::SkipEmptyParts);
@@ -88,4 +81,123 @@ GpgOpenPGPCard::GpgOpenPGPCard(const QStringList& status) : good(true) {
parse_card_info(name, value);
}
}
+
+void GpgOpenPGPCard::parse_chv_status(const QString& value) {
+ auto tokens = value.trimmed().split('+', Qt::SkipEmptyParts);
+
+ int index = 0;
+
+ if (index < tokens.size()) chv1_cached = tokens[index++].toInt();
+
+ // chv_max_len[3]
+ for (int i = 0; i < 3 && index < tokens.size(); ++i) {
+ chv_max_len[i] = tokens[index++].toInt();
+ }
+
+ // chv_retry[3]
+ for (int i = 0; i < 3 && index < tokens.size(); ++i) {
+ chv_retry[i] = tokens[index++].toInt();
+ }
+}
+
+void GpgOpenPGPCard::parse_ext_capability(const QString& value) {
+ auto parts = value.trimmed().split("+");
+
+ for (const QString& part : parts) {
+ auto equal_pos = part.indexOf('=');
+ if (equal_pos == -1) continue;
+
+ auto key = part.left(equal_pos).trimmed();
+ auto value = part.mid(equal_pos + 1).trimmed();
+
+ bool ok = false;
+ int ivalue = value.toInt(&ok);
+
+ if (key == "ki") {
+ ext_cap.ki = (ivalue == 1);
+ } else if (key == "aac") {
+ ext_cap.aac = (ivalue == 1);
+ } else if (key == "bt") {
+ ext_cap.bt = (ivalue == 1);
+ } else if (key == "kdf") {
+ ext_cap.kdf = (ivalue == 1);
+ } else if (key == "si" && ok) {
+ ext_cap.status_indicator = ivalue;
+ }
+ }
+}
+
+void GpgOpenPGPCard::parse_kdf_status(const QString& value) {
+ auto decoded = QByteArray::fromPercentEncoding(value.toUtf8());
+
+ if (decoded.size() < 23) {
+ kdf_do_enabled = 0;
+ return;
+ }
+
+ if (static_cast<quint8>(decoded[2]) != 0x03) {
+ kdf_do_enabled = 0;
+ } else if (static_cast<quint8>(decoded[22]) != 0x85) {
+ kdf_do_enabled = 1;
+ } else {
+ kdf_do_enabled = 2;
+ }
+}
+
+void GpgOpenPGPCard::parse_uif(const QString& name, const QString& value) {
+ auto index = name.back().digitValue() - 1;
+ if (index < 0 || index > 2) return;
+
+ auto decoded = QByteArray::fromPercentEncoding(value.toUtf8());
+ bool enabled = !decoded.isEmpty() && static_cast<quint8>(decoded[0]) != 0xFF;
+
+ switch (index) {
+ case 0:
+ uif.sign = enabled;
+ break;
+ case 1:
+ uif.encrypt = enabled;
+ break;
+ case 2:
+ uif.auth = enabled;
+ break;
+ }
+}
+
+void GpgOpenPGPCard::parse_card_key_info(const QString& name,
+ const QString& value) {
+ if (name == "KEY-FPR") {
+ auto tokens = value.split(' ');
+ if (tokens.size() >= 2) {
+ int no = tokens[0].toInt();
+ card_keys_info[no].fingerprint = tokens[1].toUpper();
+ }
+ } else if (name == "KEY-TIME") {
+ auto tokens = value.split(' ');
+ if (tokens.size() >= 2) {
+ int no = tokens.front().toInt();
+ qint64 ts = tokens.back().toLongLong();
+ card_keys_info[no].created =
+ QDateTime::fromSecsSinceEpoch(ts, QTimeZone::UTC);
+ }
+ } else if (name == "KEYPAIRINFO") {
+ auto tokens = value.split(' ');
+ if (tokens.size() < 2) return;
+
+ auto key_type_tokens = tokens[1].split('.');
+ if (key_type_tokens.size() < 2) return;
+
+ int no = key_type_tokens[1].toInt();
+ card_keys_info[no].key_type = key_type_tokens[0];
+ card_keys_info[no].grip = tokens[0].toUpper();
+
+ if (tokens.size() >= 3) {
+ card_keys_info[no].usage = tokens[2].toUpper();
+ }
+
+ if (tokens.size() >= 5) {
+ card_keys_info[no].algo = tokens[4].toUpper();
+ }
+ }
+}
} // namespace GpgFrontend
diff --git a/src/core/model/GpgOpenPGPCard.h b/src/core/model/GpgOpenPGPCard.h
index 78b7d854..b037811d 100644
--- a/src/core/model/GpgOpenPGPCard.h
+++ b/src/core/model/GpgOpenPGPCard.h
@@ -28,9 +28,6 @@
#pragma once
-#include "core/model/GpgCardKeyPairInfo.h"
-#include "core/typedef/CoreTypedef.h"
-
namespace GpgFrontend {
struct GPGFRONTEND_CORE_EXPORT GpgOpenPGPCard {
@@ -41,23 +38,44 @@ struct GPGFRONTEND_CORE_EXPORT GpgOpenPGPCard {
int card_version;
QString app_type;
int app_version;
- QString ext_capability;
+ int manufacturer_id;
QString manufacturer;
QString card_holder;
QString display_language;
QString display_sex;
- QString chv_status;
int sig_counter = 0;
- QContainer<GpgCardKeyPairInfo> keys;
- QMap<int, QString> fprs;
- QMap<QString, QString> card_infos;
+ struct GpgCardKeyInfo {
+ QString fingerprint;
+ QDateTime created;
+ QString grip;
+ QString key_type;
+ QString algo;
+ QString usage;
+ };
+
+ QMap<int, GpgCardKeyInfo> card_keys_info;
+
+ int kdf_do_enabled;
+ struct UIFStatus {
+ bool sign = false;
+ bool encrypt = false;
+ bool auth = false;
+ } uif;
- QString kdf;
- QString uif1;
- QString uif2;
- QString uif3;
+ int chv1_cached = -1;
+ std::array<int, 3> chv_max_len = {-1, -1, -1};
+ std::array<int, 3> chv_retry = {-1, -1, -1};
+ struct ExtCapability {
+ bool ki = false;
+ bool aac = false;
+ bool bt = false;
+ bool kdf = false;
+ int status_indicator = -1;
+ } ext_cap;
+
+ QMap<QString, QString> additional_card_infos;
bool good = false;
GpgOpenPGPCard() = default;
@@ -65,7 +83,51 @@ struct GPGFRONTEND_CORE_EXPORT GpgOpenPGPCard {
explicit GpgOpenPGPCard(const QStringList& status);
private:
+ /**
+ * @brief
+ *
+ * @param name
+ * @param value
+ */
void parse_card_info(const QString& name, const QString& value);
+
+ /**
+ * @brief
+ *
+ * @param name
+ * @param value
+ */
+ void parse_chv_status(const QString& value);
+
+ /**
+ * @brief
+ *
+ * @param value
+ */
+ void parse_ext_capability(const QString& value);
+
+ /**
+ * @brief
+ *
+ * @param value
+ */
+ void parse_kdf_status(const QString& value);
+
+ /**
+ * @brief
+ *
+ * @param name
+ * @param value
+ */
+ void parse_uif(const QString& name, const QString& value);
+
+ /**
+ * @brief
+ *
+ * @param keyword
+ * @param value
+ */
+ void parse_card_key_info(const QString& name, const QString& value);
};
} // namespace GpgFrontend \ No newline at end of file