feat: add pinentry module and paper key module
This commit is contained in:
parent
469eb2b4e0
commit
c27c541257
@ -29,6 +29,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <GFSDKUI.h>
|
#include <GFSDKUI.h>
|
||||||
|
#include <qsharedpointer.h>
|
||||||
|
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
@ -113,6 +114,71 @@ inline auto QMapToGFModuleMetaDataList(const QMap<QString, QString>& map)
|
|||||||
return head;
|
return head;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline auto ConvertEventParamsToMap(GFModuleEventParam* params)
|
||||||
|
-> QMap<QString, QString> {
|
||||||
|
QMap<QString, QString> param_map;
|
||||||
|
GFModuleEventParam* current = params;
|
||||||
|
GFModuleEventParam* last;
|
||||||
|
|
||||||
|
while (current != nullptr) {
|
||||||
|
param_map[current->name] = UDUP(current->value);
|
||||||
|
|
||||||
|
last = current;
|
||||||
|
current = current->next;
|
||||||
|
GFFreeMemory(last);
|
||||||
|
}
|
||||||
|
|
||||||
|
return param_map;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto ConvertMapToParams(const QMap<QString, QString>& param_map)
|
||||||
|
-> GFModuleEventParam* {
|
||||||
|
GFModuleEventParam* head = nullptr;
|
||||||
|
GFModuleEventParam* prev = nullptr;
|
||||||
|
|
||||||
|
for (const auto& [key, value] : param_map.asKeyValueRange()) {
|
||||||
|
auto* param = static_cast<GFModuleEventParam*>(
|
||||||
|
GFAllocateMemory(sizeof(GFModuleEventParam)));
|
||||||
|
|
||||||
|
param->name = DUP(key.toUtf8());
|
||||||
|
param->value = DUP(value.toUtf8());
|
||||||
|
param->next = nullptr;
|
||||||
|
|
||||||
|
if (prev == nullptr) {
|
||||||
|
head = param;
|
||||||
|
} else {
|
||||||
|
prev->next = param;
|
||||||
|
}
|
||||||
|
prev = param;
|
||||||
|
}
|
||||||
|
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto ConvertEventToMap(GFModuleEvent* event) -> QMap<QString, QString> {
|
||||||
|
QMap<QString, QString> event_map;
|
||||||
|
|
||||||
|
event_map["event_id"] = UDUP(event->id);
|
||||||
|
event_map["trigger_id"] = UDUP(event->trigger_id);
|
||||||
|
event_map.insert(ConvertEventParamsToMap(event->params));
|
||||||
|
|
||||||
|
GFFreeMemory(event);
|
||||||
|
|
||||||
|
return event_map;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto ConvertMapToEvent(QMap<QString, QString> event_map)
|
||||||
|
-> GFModuleEvent* {
|
||||||
|
auto* event =
|
||||||
|
static_cast<GFModuleEvent*>(GFAllocateMemory(sizeof(GFModuleEvent)));
|
||||||
|
|
||||||
|
event->id = DUP(event_map["event_id"].toUtf8());
|
||||||
|
event->trigger_id = DUP(event_map["trigger_id"].toUtf8());
|
||||||
|
event->params = nullptr;
|
||||||
|
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
inline auto AllocBufferAndCopy(const QByteArray& b) -> char* {
|
inline auto AllocBufferAndCopy(const QByteArray& b) -> char* {
|
||||||
auto* p = static_cast<char*>(GFAllocateMemory(sizeof(char) * b.size()));
|
auto* p = static_cast<char*>(GFAllocateMemory(sizeof(char) * b.size()));
|
||||||
memcpy(p, b.constData(), b.size());
|
memcpy(p, b.constData(), b.size());
|
||||||
@ -136,6 +202,55 @@ auto SecureCreateSharedObject(Args&&... args) -> std::shared_ptr<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class PointerConverter {
|
||||||
|
public:
|
||||||
|
explicit PointerConverter(void* ptr) : ptr_(ptr) {}
|
||||||
|
|
||||||
|
auto AsType() const -> T* { return static_cast<T*>(ptr_); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void* ptr_;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief
|
||||||
|
*
|
||||||
|
* @tparam T
|
||||||
|
* @return T*
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
auto SecureMallocAsType(std::size_t size) -> T* {
|
||||||
|
return PointerConverter<T>(GFAllocateMemory(size)).AsType();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief
|
||||||
|
*
|
||||||
|
* @return void*
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
auto SecureReallocAsType(T* ptr, std::size_t size) -> T* {
|
||||||
|
return PointerConverter<T>(GFReallocateMemory(ptr, size)).AsType();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename... Args>
|
||||||
|
auto SecureCreateQSharedObject(Args&&... args) -> QSharedPointer<T> {
|
||||||
|
void* mem = GFAllocateMemory(sizeof(T));
|
||||||
|
if (!mem) throw std::bad_alloc();
|
||||||
|
|
||||||
|
try {
|
||||||
|
T* obj = new (mem) T(std::forward<Args>(args)...);
|
||||||
|
return QSharedPointer<T>(obj, [](T* ptr) {
|
||||||
|
ptr->~T();
|
||||||
|
GFFreeMemory(ptr);
|
||||||
|
});
|
||||||
|
} catch (...) {
|
||||||
|
GFFreeMemory(mem);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inline auto CharArrayToQStringList(char** pl_components,
|
inline auto CharArrayToQStringList(char** pl_components,
|
||||||
int size) -> QStringList {
|
int size) -> QStringList {
|
||||||
QStringList list;
|
QStringList list;
|
||||||
@ -145,4 +260,4 @@ inline auto CharArrayToQStringList(char** pl_components,
|
|||||||
}
|
}
|
||||||
GFFreeMemory(pl_components);
|
GFFreeMemory(pl_components);
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
@ -25,4 +25,6 @@
|
|||||||
|
|
||||||
# modules
|
# modules
|
||||||
add_subdirectory(m_ver_check)
|
add_subdirectory(m_ver_check)
|
||||||
add_subdirectory(m_gpg_info)
|
add_subdirectory(m_gpg_info)
|
||||||
|
add_subdirectory(m_pinentry)
|
||||||
|
add_subdirectory(m_paper_key)
|
@ -49,9 +49,6 @@ endif()
|
|||||||
# using std c++ 17
|
# using std c++ 17
|
||||||
target_compile_features(mod_gpg_info PRIVATE cxx_std_17)
|
target_compile_features(mod_gpg_info PRIVATE cxx_std_17)
|
||||||
|
|
||||||
# ui
|
|
||||||
set(CMAKE_AUTOUIC_SEARCH_PATHS ${CMAKE_AUTOUIC_SEARCH_PATHS} ${CMAKE_CURRENT_SOURCE_DIR}/ui)
|
|
||||||
|
|
||||||
# i18n
|
# i18n
|
||||||
set(LOCALE_TS_PATH ${CMAKE_CURRENT_SOURCE_DIR}/ts)
|
set(LOCALE_TS_PATH ${CMAKE_CURRENT_SOURCE_DIR}/ts)
|
||||||
set(TS_FILES "${LOCALE_TS_PATH}/ModuleGnuPGInfoGathering.en_US.ts"
|
set(TS_FILES "${LOCALE_TS_PATH}/ModuleGnuPGInfoGathering.en_US.ts"
|
||||||
|
@ -109,11 +109,8 @@ auto GFExecuteModule(GFModuleEvent *event) -> int {
|
|||||||
|
|
||||||
StartGatheringGnuPGInfo();
|
StartGatheringGnuPGInfo();
|
||||||
|
|
||||||
char **event_argv =
|
GFModuleTriggerModuleEventCallback(event, GFGetModuleID(), 1,
|
||||||
static_cast<char **>(GFAllocateMemory(sizeof(char **) * 1));
|
ConvertMapToParams({{"ret", "0"}}));
|
||||||
event_argv[0] = DUP("0");
|
|
||||||
|
|
||||||
GFModuleTriggerModuleEventCallback(event, GFGetModuleID(), 1, event_argv);
|
|
||||||
|
|
||||||
MLogDebug("gnupg external info gathering done");
|
MLogDebug("gnupg external info gathering done");
|
||||||
return 0;
|
return 0;
|
||||||
|
50
src/m_paper_key/CMakeLists.txt
Normal file
50
src/m_paper_key/CMakeLists.txt
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
# Copyright (C) 2021 Saturneric <eric@bktus.com>
|
||||||
|
#
|
||||||
|
# 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 <eric@bktus.com> starting on May 12, 2021.
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
# com.bktus.gpgfrontend.module.integrated.gnupg_info_gathering
|
||||||
|
|
||||||
|
aux_source_directory(. INTEGRATED_MODULE_SOURCE)
|
||||||
|
|
||||||
|
# define libgpgfrontend_module
|
||||||
|
add_library(mod_paper_key SHARED ${INTEGRATED_MODULE_SOURCE})
|
||||||
|
|
||||||
|
# install dir
|
||||||
|
install(TARGETS mod_paper_key
|
||||||
|
LIBRARY DESTINATION "${CMAKE_INSTALL_PREFIX}/modules")
|
||||||
|
|
||||||
|
# link sdk
|
||||||
|
target_link_libraries(mod_paper_key PRIVATE
|
||||||
|
gpgfrontend_module_sdk)
|
||||||
|
|
||||||
|
if(GPGFRONTEND_QT5_BUILD)
|
||||||
|
# link Qt core
|
||||||
|
target_link_libraries(mod_paper_key PRIVATE Qt5::Core)
|
||||||
|
else()
|
||||||
|
# link Qt core
|
||||||
|
target_link_libraries(mod_paper_key PRIVATE Qt6::Core)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# using std c++ 17
|
||||||
|
target_compile_features(mod_paper_key PRIVATE cxx_std_17)
|
184
src/m_paper_key/PaperKeyModule.cpp
Normal file
184
src/m_paper_key/PaperKeyModule.cpp
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2021 Saturneric <eric@bktus.com>
|
||||||
|
*
|
||||||
|
* 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 <eric@bktus.com> starting on May 12, 2021.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "PaperKeyModule.h"
|
||||||
|
|
||||||
|
#include <QtCore>
|
||||||
|
|
||||||
|
#include "GFModuleCommonUtils.hpp"
|
||||||
|
#include "GFSDKBuildInfo.h"
|
||||||
|
#include "extract.h"
|
||||||
|
|
||||||
|
auto GFGetModuleGFSDKVersion() -> const char * {
|
||||||
|
return DUP(GF_SDK_VERSION_STR);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto GFGetModuleQtEnvVersion() -> const char * { return DUP(QT_VERSION_STR); }
|
||||||
|
|
||||||
|
auto GFGetModuleID() -> const char * {
|
||||||
|
return DUP("com.bktus.gpgfrontend.module.paper_key");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto GFGetModuleVersion() -> const char * { return DUP("1.0.0"); }
|
||||||
|
|
||||||
|
auto GFGetModuleMetaData() -> GFModuleMetaData * {
|
||||||
|
return QMapToGFModuleMetaDataList(
|
||||||
|
{{"Name", "PaperKey"},
|
||||||
|
{"Description", "Integrated PaperKey Functions."},
|
||||||
|
{"Author", "Saturneric"}});
|
||||||
|
}
|
||||||
|
|
||||||
|
auto GFRegisterModule() -> int {
|
||||||
|
MLogDebug("paper key module registering");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto GFActiveModule() -> int {
|
||||||
|
MLogDebug("paper key module activating");
|
||||||
|
GFModuleListenEvent(GFGetModuleID(), DUP("REQUEST_TRANS_KEY_2_PAPER_KEY"));
|
||||||
|
GFModuleListenEvent(GFGetModuleID(), DUP("REQUEST_TRANS_PAPER_KEY_2_KEY"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto GFExecuteModule(GFModuleEvent *p_event) -> int {
|
||||||
|
MLogDebug(
|
||||||
|
QString("paper key module executing, event id: %1").arg(p_event->id));
|
||||||
|
|
||||||
|
auto event = ConvertEventToMap(p_event);
|
||||||
|
|
||||||
|
if (event["event_id"] == "REQUEST_TRANS_KEY_2_PAPER_KEY") {
|
||||||
|
if (event["secret_key"].isEmpty() || event["output_path"].isEmpty()) {
|
||||||
|
GFModuleTriggerModuleEventCallback(
|
||||||
|
ConvertMapToEvent(event), GFGetModuleID(), 1,
|
||||||
|
ConvertMapToParams(
|
||||||
|
{{"ret", "-1"},
|
||||||
|
{"reason", "secret key or output path is empty"}}));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray secret_key_data =
|
||||||
|
QByteArray::fromBase64(event["secret_key"].toUtf8());
|
||||||
|
|
||||||
|
QTemporaryFile secret_key_t_file;
|
||||||
|
if (!secret_key_t_file.open()) {
|
||||||
|
qWarning() << "Unable to open temporary file";
|
||||||
|
MLogWarn("unable to open temporary file");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
secret_key_t_file.write(secret_key_data);
|
||||||
|
secret_key_t_file.flush();
|
||||||
|
secret_key_t_file.seek(0);
|
||||||
|
|
||||||
|
FILE *file = fdopen(secret_key_t_file.handle(), "rb");
|
||||||
|
if (file == nullptr) {
|
||||||
|
qDebug() << "Unable to convert QTemporaryFile to FILE*";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
extract(file, event["output_path"].toUtf8(), AUTO);
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
} else if (event["event_id"] == "REQUEST_TRANS_PAPER_KEY_2_KEY") {
|
||||||
|
if (event["public_key"].isEmpty() || event["paper_key_secrets"].isEmpty()) {
|
||||||
|
GFModuleTriggerModuleEventCallback(
|
||||||
|
ConvertMapToEvent(event), GFGetModuleID(), 1,
|
||||||
|
ConvertMapToParams(
|
||||||
|
{{"ret", "-1"},
|
||||||
|
{"reason", "public key or paper key secrets is empty"}}));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray public_key_data =
|
||||||
|
QByteArray::fromBase64(event["public_key"].toUtf8());
|
||||||
|
|
||||||
|
QTemporaryFile public_key_t_file;
|
||||||
|
if (!public_key_t_file.open()) {
|
||||||
|
GFModuleTriggerModuleEventCallback(
|
||||||
|
ConvertMapToEvent(event), GFGetModuleID(), 1,
|
||||||
|
ConvertMapToParams(
|
||||||
|
{{"ret", "-1"}, {"reason", "unable to open temporary file"}}));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public_key_t_file.write(public_key_data);
|
||||||
|
public_key_t_file.flush();
|
||||||
|
public_key_t_file.seek(0);
|
||||||
|
|
||||||
|
FILE *pubring = fdopen(public_key_t_file.handle(), "rb");
|
||||||
|
if (pubring == nullptr) {
|
||||||
|
GFModuleTriggerModuleEventCallback(
|
||||||
|
ConvertMapToEvent(event), GFGetModuleID(), 1,
|
||||||
|
ConvertMapToParams(
|
||||||
|
{{"ret", "-1"},
|
||||||
|
{"reason", "unable to convert QTemporaryFile to FILE*"}}));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray secrets_data =
|
||||||
|
QByteArray::fromBase64(event["paper_key_secrets"].toUtf8());
|
||||||
|
|
||||||
|
QTemporaryFile secrets_data_file;
|
||||||
|
if (!secrets_data_file.open()) {
|
||||||
|
GFModuleTriggerModuleEventCallback(
|
||||||
|
ConvertMapToEvent(event), GFGetModuleID(), 1,
|
||||||
|
ConvertMapToParams(
|
||||||
|
{{"ret", "-1"}, {"reason", "unable to open temporary file"}}));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
secrets_data_file.write(public_key_data);
|
||||||
|
secrets_data_file.flush();
|
||||||
|
secrets_data_file.seek(0);
|
||||||
|
|
||||||
|
FILE *secrets = fdopen(secrets_data_file.handle(), "rb");
|
||||||
|
if (secrets == nullptr) {
|
||||||
|
GFModuleTriggerModuleEventCallback(
|
||||||
|
ConvertMapToEvent(event), GFGetModuleID(), 1,
|
||||||
|
ConvertMapToParams(
|
||||||
|
{{"ret", "-1"},
|
||||||
|
{"reason", "unable to convert QTemporaryFile to FILE*"}}));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
restore(pubring, secrets, AUTO, )
|
||||||
|
}
|
||||||
|
|
||||||
|
GFModuleTriggerModuleEventCallback(ConvertMapToEvent(event), GFGetModuleID(),
|
||||||
|
1, ConvertMapToParams({{"ret", "0"}}));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto GFDeactiveModule() -> int { return 0; }
|
||||||
|
|
||||||
|
auto GFUnregisterModule() -> int {
|
||||||
|
MLogDebug("paper key module unregistering");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
56
src/m_paper_key/PaperKeyModule.h
Normal file
56
src/m_paper_key/PaperKeyModule.h
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2021 Saturneric <eric@bktus.com>
|
||||||
|
*
|
||||||
|
* 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 <eric@bktus.com> starting on May 12, 2021.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <GFSDKModule.h>
|
||||||
|
|
||||||
|
#include "GFModuleExport.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
auto GF_MODULE_EXPORT GFGetModuleGFSDKVersion() -> const char *;
|
||||||
|
|
||||||
|
auto GF_MODULE_EXPORT GFGetModuleQtEnvVersion() -> const char *;
|
||||||
|
|
||||||
|
auto GF_MODULE_EXPORT GFGetModuleID() -> const char *;
|
||||||
|
|
||||||
|
auto GF_MODULE_EXPORT GFGetModuleVersion() -> const char *;
|
||||||
|
|
||||||
|
auto GF_MODULE_EXPORT GFGetModuleMetaData() -> GFModuleMetaData *;
|
||||||
|
|
||||||
|
auto GF_MODULE_EXPORT GFRegisterModule() -> int;
|
||||||
|
|
||||||
|
auto GF_MODULE_EXPORT GFActiveModule() -> int;
|
||||||
|
|
||||||
|
auto GF_MODULE_EXPORT GFExecuteModule(GFModuleEvent *) -> int;
|
||||||
|
|
||||||
|
auto GF_MODULE_EXPORT GFDeactiveModule() -> int;
|
||||||
|
|
||||||
|
auto GF_MODULE_EXPORT GFUnregisterModule() -> int;
|
||||||
|
};
|
93
src/m_paper_key/extract.cpp
Normal file
93
src/m_paper_key/extract.cpp
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2007, 2017 David Shaw <dshaw@jabberwocky.com>
|
||||||
|
*
|
||||||
|
* This program 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 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "extract.h"
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
#include "output.h"
|
||||||
|
#include "packets.h"
|
||||||
|
#include "parse.h"
|
||||||
|
|
||||||
|
extern int verbose;
|
||||||
|
|
||||||
|
int extract(FILE *input, const char *outname, enum data_type output_type) {
|
||||||
|
struct packet *packet;
|
||||||
|
int offset;
|
||||||
|
unsigned char fingerprint[20];
|
||||||
|
unsigned char version = 0;
|
||||||
|
|
||||||
|
packet = parse(input, 5, 0);
|
||||||
|
if (!packet) {
|
||||||
|
fprintf(stderr, "Unable to find secret key packet\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset = extract_secrets(packet);
|
||||||
|
if (offset == -1) return 1;
|
||||||
|
|
||||||
|
if (verbose > 1) fprintf(stderr, "Secret offset is %d\n", offset);
|
||||||
|
|
||||||
|
calculate_fingerprint(packet, offset, fingerprint);
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
fprintf(stderr, "Primary key fingerprint: ");
|
||||||
|
print_bytes(stderr, fingerprint, 20);
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
output_start(outname, output_type, fingerprint);
|
||||||
|
output_bytes(&version, 1);
|
||||||
|
output_bytes(packet->buf, 1);
|
||||||
|
output_bytes(fingerprint, 20);
|
||||||
|
output_length16(packet->len - offset);
|
||||||
|
output_bytes(&packet->buf[offset], packet->len - offset);
|
||||||
|
|
||||||
|
free_packet(packet);
|
||||||
|
|
||||||
|
while ((packet = parse(input, 7, 5))) {
|
||||||
|
offset = extract_secrets(packet);
|
||||||
|
if (offset == -1) return 1;
|
||||||
|
|
||||||
|
if (verbose > 1) fprintf(stderr, "Secret subkey offset is %d\n", offset);
|
||||||
|
|
||||||
|
calculate_fingerprint(packet, offset, fingerprint);
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
fprintf(stderr, "Subkey fingerprint: ");
|
||||||
|
print_bytes(stderr, fingerprint, 20);
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
output_bytes(packet->buf, 1);
|
||||||
|
output_bytes(fingerprint, 20);
|
||||||
|
output_length16(packet->len - offset);
|
||||||
|
output_bytes(&packet->buf[offset], packet->len - offset);
|
||||||
|
|
||||||
|
free_packet(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
output_finish();
|
||||||
|
|
||||||
|
if (input == stdin) {
|
||||||
|
/* Consume everything else on input */
|
||||||
|
while ((fgetc(input) != EOF));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
26
src/m_paper_key/extract.h
Normal file
26
src/m_paper_key/extract.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2007 David Shaw <dshaw@jabberwocky.com>
|
||||||
|
*
|
||||||
|
* This program 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 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
#include "output.h"
|
||||||
|
|
||||||
|
auto extract(FILE *input, const char *outname,
|
||||||
|
enum data_type output_type) -> int;
|
304
src/m_paper_key/output.cpp
Normal file
304
src/m_paper_key/output.cpp
Normal file
@ -0,0 +1,304 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2007, 2008, 2009, 2012, 2016 David Shaw <dshaw@jabberwocky.com>
|
||||||
|
*
|
||||||
|
* This program 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 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <io.h>
|
||||||
|
#endif
|
||||||
|
#include "output.h"
|
||||||
|
#include "packets.h"
|
||||||
|
|
||||||
|
extern unsigned int output_width;
|
||||||
|
extern char *comment;
|
||||||
|
|
||||||
|
static enum data_type output_type;
|
||||||
|
static FILE *output;
|
||||||
|
static unsigned int line_items;
|
||||||
|
static unsigned long all_crc = CRC24_INIT;
|
||||||
|
|
||||||
|
#define CRC24_POLY 0x864CFBL
|
||||||
|
|
||||||
|
void do_crc24(unsigned long *crc, const unsigned char *buf, size_t len) {
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
int j;
|
||||||
|
|
||||||
|
*crc ^= buf[i] << 16;
|
||||||
|
for (j = 0; j < 8; j++) {
|
||||||
|
*crc <<= 1;
|
||||||
|
if (*crc & 0x1000000) *crc ^= CRC24_POLY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_base16(const unsigned char *buf, size_t length) {
|
||||||
|
static unsigned long line_crc = CRC24_INIT;
|
||||||
|
static unsigned int line = 0;
|
||||||
|
|
||||||
|
if (buf) {
|
||||||
|
size_t i;
|
||||||
|
static unsigned int offset = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < length; i++, offset++) {
|
||||||
|
if (offset % line_items == 0) {
|
||||||
|
if (line) {
|
||||||
|
fprintf(output, "%06lX\n", line_crc & 0xFFFFFFL);
|
||||||
|
line_crc = CRC24_INIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(output, "%3u: ", ++line);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(output, "%02X ", buf[i]);
|
||||||
|
|
||||||
|
do_crc24(&line_crc, &buf[i], 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fprintf(output, "%06lX\n", line_crc & 0xFFFFFFL);
|
||||||
|
fprintf(output, "%3u: %06lX\n", line + 1, all_crc & 0xFFFFFFL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_bytes(FILE *stream, const unsigned char *buf, size_t length) {
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < length; i++) fprintf(stream, "%02X", buf[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void output_file_format(FILE *stream, const char *prefix) {
|
||||||
|
fprintf(stream, "%sFile format:\n", prefix);
|
||||||
|
fprintf(stream,
|
||||||
|
"%sa) 1 octet: Version of the paperkey format (currently 0).\n",
|
||||||
|
prefix);
|
||||||
|
fprintf(stream,
|
||||||
|
"%sb) 1 octet: OpenPGP key or subkey version (currently 4)\n",
|
||||||
|
prefix);
|
||||||
|
fprintf(stream,
|
||||||
|
"%sc) n octets: Key fingerprint (20 octets for a version 4 key or "
|
||||||
|
"subkey)\n",
|
||||||
|
prefix);
|
||||||
|
fprintf(
|
||||||
|
stream,
|
||||||
|
"%sd) 2 octets: 16-bit big endian length of the following secret data\n",
|
||||||
|
prefix);
|
||||||
|
fprintf(stream,
|
||||||
|
"%se) n octets: Secret data: a partial OpenPGP secret key or subkey "
|
||||||
|
"packet as\n",
|
||||||
|
prefix);
|
||||||
|
fprintf(stream,
|
||||||
|
"%s specified in RFC 4880, starting with the "
|
||||||
|
"string-to-key usage\n",
|
||||||
|
prefix);
|
||||||
|
fprintf(stream,
|
||||||
|
"%s octet and continuing until the end of the packet.\n",
|
||||||
|
prefix);
|
||||||
|
fprintf(stream,
|
||||||
|
"%sRepeat fields b through e as needed to cover all subkeys.\n",
|
||||||
|
prefix);
|
||||||
|
fprintf(stream, "%s\n", prefix);
|
||||||
|
fprintf(
|
||||||
|
stream,
|
||||||
|
"%sTo recover a secret key without using the paperkey program, use the\n",
|
||||||
|
prefix);
|
||||||
|
fprintf(stream,
|
||||||
|
"%skey fingerprint to match an existing public key packet with the\n",
|
||||||
|
prefix);
|
||||||
|
fprintf(stream,
|
||||||
|
"%scorresponding secret data from the paper key. Next, append this "
|
||||||
|
"secret\n",
|
||||||
|
prefix);
|
||||||
|
fprintf(stream,
|
||||||
|
"%sdata to the public key packet. Finally, switch the public key "
|
||||||
|
"packet tag\n",
|
||||||
|
prefix);
|
||||||
|
fprintf(stream,
|
||||||
|
"%sfrom 6 to 5 (14 to 7 for subkeys). This will recreate the "
|
||||||
|
"original secret\n",
|
||||||
|
prefix);
|
||||||
|
fprintf(stream,
|
||||||
|
"%skey or secret subkey packet. Repeat as needed for all public key "
|
||||||
|
"or subkey\n",
|
||||||
|
prefix);
|
||||||
|
fprintf(stream,
|
||||||
|
"%spackets in the public key. All other packets (user IDs, "
|
||||||
|
"signatures, etc.)\n",
|
||||||
|
prefix);
|
||||||
|
fprintf(stream, "%smay simply be copied from the public key.\n", prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
int output_start(const char *name, enum data_type type,
|
||||||
|
unsigned char fingerprint[20]) {
|
||||||
|
if (name) {
|
||||||
|
if (type == RAW)
|
||||||
|
output = fopen(name, "wb");
|
||||||
|
else
|
||||||
|
output = fopen(name, "w");
|
||||||
|
|
||||||
|
if (!output) return -1;
|
||||||
|
} else {
|
||||||
|
if (type == RAW) set_binary_mode(stdout);
|
||||||
|
|
||||||
|
output = stdout;
|
||||||
|
}
|
||||||
|
|
||||||
|
output_type = type;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case RAW:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AUTO:
|
||||||
|
case BASE16: {
|
||||||
|
time_t now = time(NULL);
|
||||||
|
|
||||||
|
line_items = (output_width - 5 - 6) / 3;
|
||||||
|
fprintf(output, "# Secret portions of key ");
|
||||||
|
print_bytes(output, fingerprint, 20);
|
||||||
|
fprintf(output, "\n");
|
||||||
|
fprintf(output, "# Base16 data extracted %.24s\n", ctime(&now));
|
||||||
|
fprintf(output,
|
||||||
|
"# Created with "
|
||||||
|
"Paper Key Module of GpgFrontend"
|
||||||
|
" by Saturneric\n#\n");
|
||||||
|
output_file_format(output, "# ");
|
||||||
|
fprintf(output,
|
||||||
|
"#\n# Each base16 line ends with a CRC-24 of that line.\n");
|
||||||
|
fprintf(output,
|
||||||
|
"# The entire block of data ends with a CRC-24 of the entire "
|
||||||
|
"block of data.\n\n");
|
||||||
|
// if (comment != nullptr) fprintf(output, "# %s\n\n", comment);
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t output_bytes(const unsigned char *buf, size_t length) {
|
||||||
|
ssize_t ret = -1;
|
||||||
|
|
||||||
|
do_crc24(&all_crc, buf, length);
|
||||||
|
|
||||||
|
switch (output_type) {
|
||||||
|
case RAW:
|
||||||
|
if (buf == NULL) {
|
||||||
|
unsigned char crc[3];
|
||||||
|
|
||||||
|
crc[0] = (all_crc & 0xFFFFFFL) >> 16;
|
||||||
|
crc[1] = (all_crc & 0xFFFFFFL) >> 8;
|
||||||
|
crc[2] = (all_crc & 0xFFFFFFL);
|
||||||
|
|
||||||
|
ret = fwrite(crc, 1, 3, output);
|
||||||
|
} else
|
||||||
|
ret = fwrite(buf, 1, length, output);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AUTO:
|
||||||
|
case BASE16:
|
||||||
|
print_base16(buf, length);
|
||||||
|
ret = length;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t output_length16(size_t length) {
|
||||||
|
unsigned char encoded[2];
|
||||||
|
|
||||||
|
assert(length <= 65535);
|
||||||
|
|
||||||
|
encoded[0] = length >> 8;
|
||||||
|
encoded[1] = length;
|
||||||
|
|
||||||
|
return output_bytes(encoded, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t output_openpgp_header(unsigned char tag, size_t length) {
|
||||||
|
unsigned char encoded[6];
|
||||||
|
size_t bytes;
|
||||||
|
|
||||||
|
/* We use the same "tag under 16, use old-style packets" rule that
|
||||||
|
many OpenPGP programs do. This helps make the resulting key
|
||||||
|
byte-for-byte identical. It's not a guarantee, as it is legal
|
||||||
|
for the generating program to use whatever packet style it likes,
|
||||||
|
but does help avoid questions why the input to paperkey might not
|
||||||
|
equal the output. */
|
||||||
|
|
||||||
|
if (tag < 16) {
|
||||||
|
if (length > 65535) {
|
||||||
|
encoded[0] = 0x80 | (tag << 2) | 2;
|
||||||
|
encoded[1] = length >> 24;
|
||||||
|
encoded[2] = length >> 16;
|
||||||
|
encoded[3] = length >> 8;
|
||||||
|
encoded[4] = length;
|
||||||
|
bytes = 5;
|
||||||
|
} else if (length > 255) {
|
||||||
|
encoded[0] = 0x80 | (tag << 2) | 1;
|
||||||
|
encoded[1] = length >> 8;
|
||||||
|
encoded[2] = length;
|
||||||
|
bytes = 3;
|
||||||
|
} else {
|
||||||
|
encoded[0] = 0x80 | (tag << 2);
|
||||||
|
encoded[1] = length;
|
||||||
|
bytes = 2;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
encoded[0] = 0xC0 | tag;
|
||||||
|
|
||||||
|
if (length > 8383) {
|
||||||
|
encoded[1] = 0xFF;
|
||||||
|
encoded[2] = length >> 24;
|
||||||
|
encoded[3] = length >> 16;
|
||||||
|
encoded[4] = length >> 8;
|
||||||
|
encoded[5] = length;
|
||||||
|
bytes = 6;
|
||||||
|
} else if (length > 191) {
|
||||||
|
encoded[1] = 192 + ((length - 192) >> 8);
|
||||||
|
encoded[2] = (length - 192);
|
||||||
|
bytes = 3;
|
||||||
|
} else {
|
||||||
|
encoded[1] = length;
|
||||||
|
bytes = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output_bytes(encoded, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void output_finish(void) {
|
||||||
|
output_bytes(nullptr, 0);
|
||||||
|
if (output != nullptr && output != stdout) fclose(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_binary_mode(FILE *stream) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (_setmode(_fileno(stream), _O_BINARY) == -1) {
|
||||||
|
fprintf(stderr, "Unable to set stream mode to binary: %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
(void)stream;
|
||||||
|
#endif
|
||||||
|
}
|
37
src/m_paper_key/output.h
Normal file
37
src/m_paper_key/output.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2007, 2012, 2016 David Shaw <dshaw@jabberwocky.com>
|
||||||
|
*
|
||||||
|
* This program 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 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
enum data_type { AUTO, BASE16, RAW };
|
||||||
|
|
||||||
|
#define CRC24_INIT 0xB704CEL
|
||||||
|
|
||||||
|
void do_crc24(unsigned long *crc, const unsigned char *buf, size_t len);
|
||||||
|
void print_bytes(FILE *stream, const unsigned char *buf, size_t length);
|
||||||
|
void output_file_format(FILE *stream, const char *prefix);
|
||||||
|
int output_start(const char *name, enum data_type type,
|
||||||
|
unsigned char fingerprint[20]);
|
||||||
|
ssize_t output_bytes(const unsigned char *buf, size_t length);
|
||||||
|
#define output_packet(_packet) output_bytes((_packet)->buf, (_packet)->len)
|
||||||
|
ssize_t output_length16(size_t length);
|
||||||
|
ssize_t output_openpgp_header(unsigned char tag, size_t length);
|
||||||
|
void output_finish(void);
|
||||||
|
void set_binary_mode(FILE *stream);
|
60
src/m_paper_key/packets.cpp
Normal file
60
src/m_paper_key/packets.cpp
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2007 David Shaw <dshaw@jabberwocky.com>
|
||||||
|
*
|
||||||
|
* This program 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 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "packets.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "GFSDKBasic.h"
|
||||||
|
#include "output.h"
|
||||||
|
|
||||||
|
extern int verbose;
|
||||||
|
|
||||||
|
auto append_packet(struct packet *packet, unsigned char *buf,
|
||||||
|
size_t len) -> struct packet * {
|
||||||
|
if (packet != nullptr) {
|
||||||
|
while (packet->size - packet->len < len) {
|
||||||
|
packet->size += 100;
|
||||||
|
packet->buf = static_cast<unsigned char *>(
|
||||||
|
GFReallocateMemory(packet->buf, packet->size));
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(&packet->buf[packet->len], buf, len);
|
||||||
|
packet->len += len;
|
||||||
|
} else {
|
||||||
|
packet =
|
||||||
|
static_cast<struct packet *>(GFAllocateMemory(sizeof(struct packet)));
|
||||||
|
packet->type = 0;
|
||||||
|
packet->buf = static_cast<unsigned char *>(GFAllocateMemory(len));
|
||||||
|
packet->len = len;
|
||||||
|
packet->size = len;
|
||||||
|
|
||||||
|
memcpy(packet->buf, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_packet(struct packet *packet) {
|
||||||
|
if (packet != nullptr) {
|
||||||
|
GFFreeMemory(packet->buf);
|
||||||
|
GFFreeMemory(packet);
|
||||||
|
}
|
||||||
|
}
|
34
src/m_paper_key/packets.h
Normal file
34
src/m_paper_key/packets.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2007 David Shaw <dshaw@jabberwocky.com>
|
||||||
|
*
|
||||||
|
* This program 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 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
struct packet {
|
||||||
|
unsigned char type;
|
||||||
|
unsigned char *buf;
|
||||||
|
/* The length the data we've put into buf. */
|
||||||
|
size_t len;
|
||||||
|
/* The length we've malloced for buf. */
|
||||||
|
size_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto append_packet(struct packet *packet, unsigned char *buf,
|
||||||
|
size_t len) -> struct packet *;
|
||||||
|
void free_packet(struct packet *packet);
|
87
src/m_paper_key/paperkey.cpp
Normal file
87
src/m_paper_key/paperkey.cpp
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2007, 2008, 2009, 2012, 2016 David Shaw <dshaw@jabberwocky.com>
|
||||||
|
*
|
||||||
|
* This program 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 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include "extract.h"
|
||||||
|
#include "output.h"
|
||||||
|
|
||||||
|
int verbose = 0, ignore_crc_error = 0;
|
||||||
|
unsigned int output_width = 78;
|
||||||
|
char *comment = nullptr;
|
||||||
|
|
||||||
|
enum options {
|
||||||
|
OPT_HELP = 256,
|
||||||
|
OPT_VERSION,
|
||||||
|
OPT_VERBOSE,
|
||||||
|
OPT_OUTPUT,
|
||||||
|
OPT_INPUT_TYPE,
|
||||||
|
OPT_OUTPUT_TYPE,
|
||||||
|
OPT_OUTPUT_WIDTH,
|
||||||
|
OPT_SECRET_KEY,
|
||||||
|
OPT_PUBRING,
|
||||||
|
OPT_SECRETS,
|
||||||
|
OPT_IGNORE_CRC_ERROR,
|
||||||
|
OPT_FILE_FORMAT,
|
||||||
|
OPT_COMMENT
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct option long_options[] = {
|
||||||
|
{"help", no_argument, NULL, OPT_HELP},
|
||||||
|
{"version", no_argument, NULL, OPT_VERSION},
|
||||||
|
{"verbose", no_argument, NULL, OPT_VERBOSE},
|
||||||
|
{"output", required_argument, NULL, OPT_OUTPUT},
|
||||||
|
{"input-type", required_argument, NULL, OPT_INPUT_TYPE},
|
||||||
|
{"output-type", required_argument, NULL, OPT_OUTPUT_TYPE},
|
||||||
|
{"output-width", required_argument, NULL, OPT_OUTPUT_WIDTH},
|
||||||
|
{"secret-key", required_argument, NULL, OPT_SECRET_KEY},
|
||||||
|
{"pubring", required_argument, NULL, OPT_PUBRING},
|
||||||
|
{"secrets", required_argument, NULL, OPT_SECRETS},
|
||||||
|
{"ignore-crc-error", no_argument, NULL, OPT_IGNORE_CRC_ERROR},
|
||||||
|
{"file-format", no_argument, NULL, OPT_FILE_FORMAT},
|
||||||
|
{"comment", required_argument, NULL, OPT_COMMENT},
|
||||||
|
{NULL, 0, NULL, 0}};
|
||||||
|
|
||||||
|
static void usage(void) {
|
||||||
|
printf("Usage: paperkey [OPTIONS]\n");
|
||||||
|
printf(" --help (-h)\n");
|
||||||
|
printf(" --version (-V)\n");
|
||||||
|
printf(" --verbose (-v) be more verbose\n");
|
||||||
|
printf(" --output (-o) write output to this file\n");
|
||||||
|
printf(" --input-type auto, base16 or raw (binary)\n");
|
||||||
|
printf(" --output-type base16 or raw (binary)\n");
|
||||||
|
printf(" --output-width maximum width of base16 output\n");
|
||||||
|
printf(
|
||||||
|
" --secret-key"
|
||||||
|
" extract secret data from this secret key\n");
|
||||||
|
printf(
|
||||||
|
" --pubring"
|
||||||
|
" public keyring to find non-secret data\n");
|
||||||
|
printf(
|
||||||
|
" --secrets file containing secret"
|
||||||
|
" data to join with the public key\n");
|
||||||
|
printf(" --ignore-crc-error don't reject corrupted input\n");
|
||||||
|
printf(" --file-format show the paperkey file format\n");
|
||||||
|
printf(" --comment add a comment to the base16 output\n");
|
||||||
|
}
|
537
src/m_paper_key/parse.cpp
Normal file
537
src/m_paper_key/parse.cpp
Normal file
@ -0,0 +1,537 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2007, 2008, 2012, 2017 David Shaw <dshaw@jabberwocky.com>
|
||||||
|
*
|
||||||
|
* This program 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 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "parse.h"
|
||||||
|
|
||||||
|
#include <qcryptographichash.h>
|
||||||
|
#include <qdatastream.h>
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "GFSDKBasic.h"
|
||||||
|
#include "output.h"
|
||||||
|
#include "packets.h"
|
||||||
|
|
||||||
|
extern int verbose;
|
||||||
|
extern int ignore_crc_error;
|
||||||
|
|
||||||
|
struct packet *parse2(QDataStream &stream, unsigned char want,
|
||||||
|
unsigned char stop) {
|
||||||
|
int byte;
|
||||||
|
struct packet *packet = NULL;
|
||||||
|
|
||||||
|
while (!stream.atEnd()) {
|
||||||
|
stream >> byte;
|
||||||
|
|
||||||
|
unsigned char type;
|
||||||
|
unsigned int length;
|
||||||
|
|
||||||
|
if ((byte & 0x80) != 0) {
|
||||||
|
int tmp;
|
||||||
|
|
||||||
|
type = byte & 0x3F;
|
||||||
|
|
||||||
|
/* Old-style packet type */
|
||||||
|
if (!(byte & 0x40)) type >>= 2;
|
||||||
|
|
||||||
|
if (type == stop) {
|
||||||
|
stream << byte;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (byte & 0x40) {
|
||||||
|
/* New-style packets */
|
||||||
|
stream >> byte;
|
||||||
|
if (byte == EOF) goto fail;
|
||||||
|
|
||||||
|
if (byte == 255) {
|
||||||
|
/* 4-byte length */
|
||||||
|
stream >> tmp;
|
||||||
|
if (tmp == EOF) goto fail;
|
||||||
|
length = tmp << 24;
|
||||||
|
stream >> tmp;
|
||||||
|
if (tmp == EOF) goto fail;
|
||||||
|
length |= tmp << 16;
|
||||||
|
stream >> tmp;
|
||||||
|
if (tmp == EOF) goto fail;
|
||||||
|
length |= tmp << 8;
|
||||||
|
stream >> tmp;
|
||||||
|
if (tmp == EOF) goto fail;
|
||||||
|
length |= tmp;
|
||||||
|
} else if (byte >= 224) {
|
||||||
|
/* Partial body length, so fail (keys can't use
|
||||||
|
partial body) */
|
||||||
|
fprintf(stderr, "Invalid partial packet encoding\n");
|
||||||
|
goto fail;
|
||||||
|
} else if (byte >= 192) {
|
||||||
|
/* 2-byte length */
|
||||||
|
stream >> tmp;
|
||||||
|
if (tmp == EOF) goto fail;
|
||||||
|
length = ((byte - 192) << 8) + tmp + 192;
|
||||||
|
} else
|
||||||
|
length = byte;
|
||||||
|
} else {
|
||||||
|
/* Old-style packets */
|
||||||
|
switch (byte & 0x03) {
|
||||||
|
case 0:
|
||||||
|
/* 1-byte length */
|
||||||
|
stream >> byte;
|
||||||
|
if (byte == EOF) goto fail;
|
||||||
|
length = byte;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
/* 2-byte length */
|
||||||
|
stream >> byte;
|
||||||
|
if (byte == EOF) goto fail;
|
||||||
|
stream >> tmp;
|
||||||
|
if (tmp == EOF) goto fail;
|
||||||
|
length = byte << 8;
|
||||||
|
length |= tmp;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
/* 4-byte length */
|
||||||
|
stream >> tmp;
|
||||||
|
if (tmp == EOF) goto fail;
|
||||||
|
length = tmp << 24;
|
||||||
|
stream >> tmp;
|
||||||
|
if (tmp == EOF) goto fail;
|
||||||
|
length |= tmp << 16;
|
||||||
|
stream >> tmp;
|
||||||
|
if (tmp == EOF) goto fail;
|
||||||
|
length |= tmp << 8;
|
||||||
|
stream >> tmp;
|
||||||
|
if (tmp == EOF) goto fail;
|
||||||
|
length |= tmp;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Error: unable to parse old-style length\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verbose > 1)
|
||||||
|
fprintf(stderr, "Found packet of type %d, length %d\n", type, length);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Error: unable to parse OpenPGP packets"
|
||||||
|
" (is this armored data?)\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (want == 0 || type == want) {
|
||||||
|
packet =
|
||||||
|
static_cast<struct packet *>(GFAllocateMemory(sizeof(struct packet)));
|
||||||
|
packet->type = type;
|
||||||
|
packet->buf = static_cast<unsigned char *>(GFAllocateMemory(length));
|
||||||
|
packet->len = length;
|
||||||
|
packet->size = length;
|
||||||
|
if (stream.readRawData(reinterpret_cast<char *>(packet->buf),
|
||||||
|
packet->len) < packet->len) {
|
||||||
|
fprintf(stderr, "Short read on packet type %d\n", type);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
/* We don't want it, so skip the packet. We don't use fseek
|
||||||
|
here since the input might be on stdin and that isn't
|
||||||
|
seekable. */
|
||||||
|
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < length; i++) stream >> byte;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return packet;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct packet *parse(FILE *input, unsigned char want, unsigned char stop) {
|
||||||
|
int byte;
|
||||||
|
struct packet *packet = NULL;
|
||||||
|
|
||||||
|
while ((byte = fgetc(input)) != EOF) {
|
||||||
|
unsigned char type;
|
||||||
|
unsigned int length;
|
||||||
|
|
||||||
|
if (byte & 0x80) {
|
||||||
|
int tmp;
|
||||||
|
|
||||||
|
type = byte & 0x3F;
|
||||||
|
|
||||||
|
/* Old-style packet type */
|
||||||
|
if (!(byte & 0x40)) type >>= 2;
|
||||||
|
|
||||||
|
if (type == stop) {
|
||||||
|
ungetc(byte, input);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (byte & 0x40) {
|
||||||
|
/* New-style packets */
|
||||||
|
byte = fgetc(input);
|
||||||
|
if (byte == EOF) goto fail;
|
||||||
|
|
||||||
|
if (byte == 255) {
|
||||||
|
/* 4-byte length */
|
||||||
|
tmp = fgetc(input);
|
||||||
|
if (tmp == EOF) goto fail;
|
||||||
|
length = tmp << 24;
|
||||||
|
tmp = fgetc(input);
|
||||||
|
if (tmp == EOF) goto fail;
|
||||||
|
length |= tmp << 16;
|
||||||
|
tmp = fgetc(input);
|
||||||
|
if (tmp == EOF) goto fail;
|
||||||
|
length |= tmp << 8;
|
||||||
|
tmp = fgetc(input);
|
||||||
|
if (tmp == EOF) goto fail;
|
||||||
|
length |= tmp;
|
||||||
|
} else if (byte >= 224) {
|
||||||
|
/* Partial body length, so fail (keys can't use
|
||||||
|
partial body) */
|
||||||
|
fprintf(stderr, "Invalid partial packet encoding\n");
|
||||||
|
goto fail;
|
||||||
|
} else if (byte >= 192) {
|
||||||
|
/* 2-byte length */
|
||||||
|
tmp = fgetc(input);
|
||||||
|
if (tmp == EOF) goto fail;
|
||||||
|
length = ((byte - 192) << 8) + tmp + 192;
|
||||||
|
} else
|
||||||
|
length = byte;
|
||||||
|
} else {
|
||||||
|
/* Old-style packets */
|
||||||
|
switch (byte & 0x03) {
|
||||||
|
case 0:
|
||||||
|
/* 1-byte length */
|
||||||
|
byte = fgetc(input);
|
||||||
|
if (byte == EOF) goto fail;
|
||||||
|
length = byte;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
/* 2-byte length */
|
||||||
|
byte = fgetc(input);
|
||||||
|
if (byte == EOF) goto fail;
|
||||||
|
tmp = fgetc(input);
|
||||||
|
if (tmp == EOF) goto fail;
|
||||||
|
length = byte << 8;
|
||||||
|
length |= tmp;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
/* 4-byte length */
|
||||||
|
tmp = fgetc(input);
|
||||||
|
if (tmp == EOF) goto fail;
|
||||||
|
length = tmp << 24;
|
||||||
|
tmp = fgetc(input);
|
||||||
|
if (tmp == EOF) goto fail;
|
||||||
|
length |= tmp << 16;
|
||||||
|
tmp = fgetc(input);
|
||||||
|
if (tmp == EOF) goto fail;
|
||||||
|
length |= tmp << 8;
|
||||||
|
tmp = fgetc(input);
|
||||||
|
if (tmp == EOF) goto fail;
|
||||||
|
length |= tmp;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Error: unable to parse old-style length\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verbose > 1)
|
||||||
|
fprintf(stderr, "Found packet of type %d, length %d\n", type, length);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Error: unable to parse OpenPGP packets"
|
||||||
|
" (is this armored data?)\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (want == 0 || type == want) {
|
||||||
|
packet =
|
||||||
|
static_cast<struct packet *>(GFAllocateMemory(sizeof(struct packet)));
|
||||||
|
packet->type = type;
|
||||||
|
packet->buf = static_cast<unsigned char *>(GFAllocateMemory(length));
|
||||||
|
packet->len = length;
|
||||||
|
packet->size = length;
|
||||||
|
if (fread(packet->buf, 1, packet->len, input) < packet->len) {
|
||||||
|
fprintf(stderr, "Short read on packet type %d\n", type);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
/* We don't want it, so skip the packet. We don't use fseek
|
||||||
|
here since the input might be on stdin and that isn't
|
||||||
|
seekable. */
|
||||||
|
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < length; i++) fgetc(input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return packet;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int calculate_fingerprint(struct packet *packet, size_t public_len,
|
||||||
|
unsigned char fingerprint[20]) {
|
||||||
|
if (packet->buf[0] == 3) {
|
||||||
|
return -1;
|
||||||
|
} else if (packet->buf[0] == 4) {
|
||||||
|
QCryptographicHash sha(QCryptographicHash::Sha1);
|
||||||
|
QByteArray head;
|
||||||
|
|
||||||
|
head.append(static_cast<char>(0x99));
|
||||||
|
head.append(static_cast<char>(public_len >> 8));
|
||||||
|
head.append(static_cast<char>(public_len & 0xFF));
|
||||||
|
|
||||||
|
sha.addData(head);
|
||||||
|
sha.addData(reinterpret_cast<const char *>(packet->buf), public_len);
|
||||||
|
QByteArray result = sha.result();
|
||||||
|
|
||||||
|
if (result.size() != 20) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::memcpy(fingerprint, result.constData(), 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MPI_LENGTH(_start) (((((_start)[0] << 8 | (_start)[1]) + 7) / 8) + 2)
|
||||||
|
|
||||||
|
ssize_t extract_secrets(struct packet *packet) {
|
||||||
|
size_t offset;
|
||||||
|
|
||||||
|
if (packet->len == 0) return -1;
|
||||||
|
|
||||||
|
/* Secret keys consist of a public key with some secret material
|
||||||
|
stuck on the end. To get to the secrets, we have to skip the
|
||||||
|
public stuff. */
|
||||||
|
|
||||||
|
if (packet->buf[0] == 3) {
|
||||||
|
fprintf(stderr, "Version 3 (PGP 2.x style) keys are not supported.\n");
|
||||||
|
return -1;
|
||||||
|
} else if (packet->buf[0] == 4) {
|
||||||
|
/* Jump 5 bytes in. That gets us past 1 byte of version, and 4
|
||||||
|
bytes of timestamp. */
|
||||||
|
|
||||||
|
offset = 5;
|
||||||
|
} else
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (packet->len <= offset) return -1;
|
||||||
|
|
||||||
|
switch (packet->buf[offset++]) {
|
||||||
|
case 1: /* RSA */
|
||||||
|
/* Skip 2 MPIs */
|
||||||
|
offset += MPI_LENGTH(&packet->buf[offset]);
|
||||||
|
if (packet->len <= offset) return -1;
|
||||||
|
offset += MPI_LENGTH(&packet->buf[offset]);
|
||||||
|
if (packet->len <= offset) return -1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 16: /* Elgamal */
|
||||||
|
/* Skip 3 MPIs */
|
||||||
|
offset += MPI_LENGTH(&packet->buf[offset]);
|
||||||
|
if (packet->len <= offset) return -1;
|
||||||
|
offset += MPI_LENGTH(&packet->buf[offset]);
|
||||||
|
if (packet->len <= offset) return -1;
|
||||||
|
offset += MPI_LENGTH(&packet->buf[offset]);
|
||||||
|
if (packet->len <= offset) return -1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 17: /* DSA */
|
||||||
|
/* Skip 4 MPIs */
|
||||||
|
offset += MPI_LENGTH(&packet->buf[offset]);
|
||||||
|
if (packet->len <= offset) return -1;
|
||||||
|
offset += MPI_LENGTH(&packet->buf[offset]);
|
||||||
|
if (packet->len <= offset) return -1;
|
||||||
|
offset += MPI_LENGTH(&packet->buf[offset]);
|
||||||
|
if (packet->len <= offset) return -1;
|
||||||
|
offset += MPI_LENGTH(&packet->buf[offset]);
|
||||||
|
if (packet->len <= offset) return -1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 18: /* ECDH */
|
||||||
|
/* Skip the curve ID and its length byte, plus an MPI, plus the
|
||||||
|
KDF parameters and their length byte */
|
||||||
|
offset += packet->buf[offset] + 1;
|
||||||
|
if (packet->len <= offset) return -1;
|
||||||
|
offset += MPI_LENGTH(&packet->buf[offset]);
|
||||||
|
if (packet->len <= offset) return -1;
|
||||||
|
offset += packet->buf[offset] + 1;
|
||||||
|
if (packet->len <= offset) return -1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 19: /* ECDSA */
|
||||||
|
case 22: /* EdDSA - note that this is from an expired draft
|
||||||
|
https://tools.ietf.org/html/draft-koch-eddsa-for-openpgp-04,
|
||||||
|
but GnuPG is using algorithm 22 for it. */
|
||||||
|
/* Skip the curve ID and its length byte, plus an MPI */
|
||||||
|
offset += packet->buf[offset] + 1;
|
||||||
|
if (packet->len <= offset) return -1;
|
||||||
|
offset += MPI_LENGTH(&packet->buf[offset]);
|
||||||
|
if (packet->len <= offset) return -1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
/* What algorithm? */
|
||||||
|
fprintf(stderr, "Unable to parse algorithm %u\n",
|
||||||
|
packet->buf[offset - 1]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct packet *read_secrets_file(FILE *secrets, enum data_type input_type) {
|
||||||
|
struct packet *packet = NULL;
|
||||||
|
int final_crc = 0;
|
||||||
|
unsigned long my_crc = 0;
|
||||||
|
|
||||||
|
if (input_type == RAW) {
|
||||||
|
unsigned char buffer[1024];
|
||||||
|
size_t got;
|
||||||
|
|
||||||
|
while ((got = fread(buffer, 1, 1024, secrets)))
|
||||||
|
packet = append_packet(packet, buffer, got);
|
||||||
|
|
||||||
|
if (got == 0 && !feof(secrets)) {
|
||||||
|
fprintf(stderr, "Error: unable to read secrets file\n");
|
||||||
|
free_packet(packet);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packet->len >= 3) {
|
||||||
|
/* Grab the last 3 bytes to be the CRC24 */
|
||||||
|
my_crc = packet->buf[packet->len - 3] << 16;
|
||||||
|
my_crc |= packet->buf[packet->len - 2] << 8;
|
||||||
|
my_crc |= packet->buf[packet->len - 1];
|
||||||
|
final_crc = 1;
|
||||||
|
packet->len -= 3;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
char line[1024];
|
||||||
|
unsigned int next_linenum = 1;
|
||||||
|
|
||||||
|
while (fgets(line, 1024, secrets)) {
|
||||||
|
unsigned int linenum, did_digit = 0;
|
||||||
|
unsigned long line_crc = CRC24_INIT;
|
||||||
|
char *tok;
|
||||||
|
|
||||||
|
if (line[0] == '#' || line[0] == '\n' || line[0] == '\r') continue;
|
||||||
|
|
||||||
|
linenum = atoi(line);
|
||||||
|
if (linenum != next_linenum) {
|
||||||
|
fprintf(stderr, "Error: missing line number %u (saw %u)\n",
|
||||||
|
next_linenum, linenum);
|
||||||
|
free_packet(packet);
|
||||||
|
return NULL;
|
||||||
|
} else
|
||||||
|
next_linenum = linenum + 1;
|
||||||
|
|
||||||
|
tok = strchr(line, ':');
|
||||||
|
if (tok) {
|
||||||
|
tok = strchr(tok, ' ');
|
||||||
|
|
||||||
|
while (tok) {
|
||||||
|
char *next;
|
||||||
|
|
||||||
|
while (*tok == ' ') tok++;
|
||||||
|
|
||||||
|
next = strchr(tok, ' ');
|
||||||
|
|
||||||
|
if (next == NULL) {
|
||||||
|
/* End of line, so check the CRC. */
|
||||||
|
unsigned long new_crc;
|
||||||
|
|
||||||
|
if (sscanf(tok, "%06lX", &new_crc)) {
|
||||||
|
if (did_digit) {
|
||||||
|
if ((new_crc & 0xFFFFFFL) != (line_crc & 0xFFFFFFL)) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"CRC on line %d does not"
|
||||||
|
" match (%06lX!=%06lX)\n",
|
||||||
|
linenum, new_crc & 0xFFFFFFL, line_crc & 0xFFFFFFL);
|
||||||
|
if (!ignore_crc_error) {
|
||||||
|
free_packet(packet);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
final_crc = 1;
|
||||||
|
my_crc = new_crc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unsigned int digit;
|
||||||
|
|
||||||
|
if (sscanf(tok, "%02X", &digit)) {
|
||||||
|
unsigned char d = digit;
|
||||||
|
packet = append_packet(packet, &d, 1);
|
||||||
|
do_crc24(&line_crc, &d, 1);
|
||||||
|
did_digit = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tok = next;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "No colon ':' found in line %u\n", linenum);
|
||||||
|
free_packet(packet);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (final_crc) {
|
||||||
|
unsigned long all_crc = CRC24_INIT;
|
||||||
|
|
||||||
|
do_crc24(&all_crc, packet->buf, packet->len);
|
||||||
|
|
||||||
|
if ((my_crc & 0xFFFFFFL) != (all_crc & 0xFFFFFFL)) {
|
||||||
|
fprintf(stderr, "CRC of secret does not match (%06lX!=%06lX)\n",
|
||||||
|
my_crc & 0xFFFFFFL, all_crc & 0xFFFFFFL);
|
||||||
|
if (!ignore_crc_error) {
|
||||||
|
free_packet(packet);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "CRC of secret is missing\n");
|
||||||
|
if (!ignore_crc_error) {
|
||||||
|
free_packet(packet);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return packet;
|
||||||
|
}
|
37
src/m_paper_key/parse.h
Normal file
37
src/m_paper_key/parse.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2007 David Shaw <dshaw@jabberwocky.com>
|
||||||
|
*
|
||||||
|
* This program 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 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <qstring.h>
|
||||||
|
|
||||||
|
#include "output.h"
|
||||||
|
|
||||||
|
auto parse(FILE *input, unsigned char want,
|
||||||
|
unsigned char stop) -> struct packet *;
|
||||||
|
|
||||||
|
struct packet *parse2(QDataStream &stream, unsigned char want,
|
||||||
|
unsigned char stop);
|
||||||
|
|
||||||
|
auto calculate_fingerprint(struct packet *packet, size_t public_len,
|
||||||
|
unsigned char fingerprint[20]) -> int;
|
||||||
|
|
||||||
|
auto extract_secrets(struct packet *packet) -> ssize_t;
|
||||||
|
|
||||||
|
auto read_secrets_file(FILE *secrets,
|
||||||
|
enum data_type input_type) -> struct packet *;
|
181
src/m_paper_key/restore.cpp
Normal file
181
src/m_paper_key/restore.cpp
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2007, 2012 David Shaw <dshaw@jabberwocky.com>
|
||||||
|
*
|
||||||
|
* This program 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 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "restore.h"
|
||||||
|
|
||||||
|
#include <cctype>
|
||||||
|
#include <cerrno>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "GFSDKBasic.h"
|
||||||
|
#include "output.h"
|
||||||
|
#include "packets.h"
|
||||||
|
#include "parse.h"
|
||||||
|
|
||||||
|
struct key {
|
||||||
|
unsigned char fpr[20];
|
||||||
|
struct packet *packet;
|
||||||
|
struct key *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
static auto extract_keys(struct packet *packet) -> struct key * {
|
||||||
|
struct key *key = NULL;
|
||||||
|
size_t idx = 1;
|
||||||
|
|
||||||
|
/* Check the version */
|
||||||
|
if (packet->len && packet->buf[0] != 0) {
|
||||||
|
fprintf(stderr, "Cannot handle secrets file version %d\n", packet->buf[0]);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (idx < packet->len) {
|
||||||
|
/* 1+20+2 == version + fingerprint + length */
|
||||||
|
if (idx + 1 + 20 + 2 <= packet->len) {
|
||||||
|
if (packet->buf[idx] == 4) {
|
||||||
|
unsigned int len;
|
||||||
|
struct key *newkey;
|
||||||
|
|
||||||
|
newkey =
|
||||||
|
static_cast<struct key *>(GFAllocateMemory(sizeof(struct key)));
|
||||||
|
newkey->next = NULL;
|
||||||
|
|
||||||
|
idx++;
|
||||||
|
memcpy(newkey->fpr, &packet->buf[idx], 20);
|
||||||
|
|
||||||
|
idx += 20;
|
||||||
|
|
||||||
|
len = packet->buf[idx++] << 8;
|
||||||
|
len |= packet->buf[idx++];
|
||||||
|
|
||||||
|
if (idx + len <= packet->len) {
|
||||||
|
newkey->packet = append_packet(NULL, &packet->buf[idx], len);
|
||||||
|
idx += len;
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Warning: Short data in secret image\n");
|
||||||
|
free(newkey);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
newkey->next = key;
|
||||||
|
key = newkey;
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Warning: Corrupt data in secret image\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Warning: Short header in secret image\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void free_keys(struct key *key) {
|
||||||
|
while (key) {
|
||||||
|
struct key *keytmp = key;
|
||||||
|
free_packet(key->packet);
|
||||||
|
key = key->next;
|
||||||
|
free(keytmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto restore(FILE *pubring, FILE *secrets, enum data_type input_type,
|
||||||
|
const char *outname) -> int {
|
||||||
|
struct packet *secret;
|
||||||
|
|
||||||
|
if (input_type == AUTO) {
|
||||||
|
int test = fgetc(secrets);
|
||||||
|
|
||||||
|
if (test == EOF) {
|
||||||
|
fprintf(stderr, "Unable to check type of secrets file\n");
|
||||||
|
return 1;
|
||||||
|
} else if (isascii(test) && isprint(test))
|
||||||
|
input_type = BASE16;
|
||||||
|
else
|
||||||
|
input_type = RAW;
|
||||||
|
|
||||||
|
ungetc(test, secrets);
|
||||||
|
}
|
||||||
|
|
||||||
|
secret = read_secrets_file(secrets, input_type);
|
||||||
|
if (secret) {
|
||||||
|
struct packet *pubkey;
|
||||||
|
struct key *keys;
|
||||||
|
int did_pubkey = 0;
|
||||||
|
|
||||||
|
/* Build a list of all keys. We need to do this since the
|
||||||
|
public key we are transforming might have the subkeys in a
|
||||||
|
different order than (or not match subkeys at all with) our
|
||||||
|
secret data. */
|
||||||
|
|
||||||
|
keys = extract_keys(secret);
|
||||||
|
if (keys) {
|
||||||
|
output_start(outname, RAW, NULL);
|
||||||
|
|
||||||
|
while ((pubkey = parse(pubring, 0, 0))) {
|
||||||
|
unsigned char ptag;
|
||||||
|
|
||||||
|
if (pubkey->type == 6 || pubkey->type == 14) {
|
||||||
|
/* Public key or subkey */
|
||||||
|
unsigned char fpr[20];
|
||||||
|
struct key *keyidx;
|
||||||
|
|
||||||
|
if (pubkey->type == 6 && did_pubkey) break;
|
||||||
|
|
||||||
|
calculate_fingerprint(pubkey, pubkey->len, fpr);
|
||||||
|
|
||||||
|
/* Do we have a secret key that matches? */
|
||||||
|
for (keyidx = keys; keyidx; keyidx = keyidx->next) {
|
||||||
|
if (memcmp(fpr, keyidx->fpr, 20) == 0) {
|
||||||
|
if (pubkey->type == 6) {
|
||||||
|
ptag = 5;
|
||||||
|
did_pubkey = 1;
|
||||||
|
} else
|
||||||
|
ptag = 7;
|
||||||
|
|
||||||
|
/* Match, so create a secret key. */
|
||||||
|
output_openpgp_header(ptag, pubkey->len + keyidx->packet->len);
|
||||||
|
output_packet(pubkey);
|
||||||
|
output_packet(keyidx->packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (did_pubkey) {
|
||||||
|
/* Copy the usual user ID, sigs, etc, so the key is
|
||||||
|
well-formed. */
|
||||||
|
output_openpgp_header(pubkey->type, pubkey->len);
|
||||||
|
output_packet(pubkey);
|
||||||
|
}
|
||||||
|
|
||||||
|
free_packet(pubkey);
|
||||||
|
}
|
||||||
|
|
||||||
|
free_keys(keys);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Unable to parse secret data\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Unable to read secrets file\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
27
src/m_paper_key/restore.h
Normal file
27
src/m_paper_key/restore.h
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2007 David Shaw <dshaw@jabberwocky.com>
|
||||||
|
*
|
||||||
|
* This program 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 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _RESTORE_H_
|
||||||
|
#define _RESTORE_H_
|
||||||
|
|
||||||
|
#include "output.h"
|
||||||
|
|
||||||
|
auto restore(FILE *pubring, FILE *secrets, enum data_type input_type,
|
||||||
|
const char *outname) -> int;
|
||||||
|
|
||||||
|
#endif /* !_RESTORE_H_ */
|
63
src/m_pinentry/CMakeLists.txt
Normal file
63
src/m_pinentry/CMakeLists.txt
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
# Copyright (C) 2021 Saturneric <eric@bktus.com>
|
||||||
|
#
|
||||||
|
# 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 <eric@bktus.com> starting on May 12, 2021.
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
aux_source_directory(. INTEGRATED_MODULE_SOURCE)
|
||||||
|
|
||||||
|
# capslock
|
||||||
|
list(APPEND INTEGRATED_MODULE_SOURCE "capslock/capslock.cpp")
|
||||||
|
if (MINGW)
|
||||||
|
list(APPEND INTEGRATED_MODULE_SOURCE "capslock/capslock_win.cpp")
|
||||||
|
else()
|
||||||
|
list(APPEND INTEGRATED_MODULE_SOURCE "capslock/capslock_unix.cpp")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
list(APPEND INTEGRATED_MODULE_SOURCE "pinentry.qrc")
|
||||||
|
|
||||||
|
# define module
|
||||||
|
add_library(mod_pinentry SHARED ${INTEGRATED_MODULE_SOURCE})
|
||||||
|
|
||||||
|
# install dir
|
||||||
|
install(TARGETS mod_pinentry
|
||||||
|
LIBRARY DESTINATION "${CMAKE_INSTALL_PREFIX}/modules")
|
||||||
|
|
||||||
|
|
||||||
|
# link options
|
||||||
|
|
||||||
|
# link sdk
|
||||||
|
target_link_libraries(mod_pinentry PRIVATE
|
||||||
|
gpgfrontend_module_sdk)
|
||||||
|
|
||||||
|
if(GPGFRONTEND_QT5_BUILD)
|
||||||
|
# link Qt core
|
||||||
|
target_link_libraries(mod_pinentry PUBLIC Qt5::Widgets)
|
||||||
|
else()
|
||||||
|
# link Qt core
|
||||||
|
target_link_libraries(mod_pinentry PUBLIC Qt6::Widgets)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
# using std c++ 17
|
||||||
|
target_compile_features(mod_pinentry PUBLIC cxx_std_17)
|
||||||
|
|
57
src/m_pinentry/GpgPassphraseContext.cpp
Normal file
57
src/m_pinentry/GpgPassphraseContext.cpp
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2021 Saturneric <eric@bktus.com>
|
||||||
|
*
|
||||||
|
* 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 <eric@bktus.com> starting on May 12, 2021.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "GpgPassphraseContext.h"
|
||||||
|
|
||||||
|
GpgPassphraseContext::GpgPassphraseContext(const QString& uids_info,
|
||||||
|
const QString& passphrase_info,
|
||||||
|
bool prev_was_bad, bool ask_for_new)
|
||||||
|
: passphrase_info_(passphrase_info),
|
||||||
|
uids_info_(uids_info),
|
||||||
|
prev_was_bad_(prev_was_bad),
|
||||||
|
ask_for_new_(ask_for_new) {}
|
||||||
|
|
||||||
|
GpgPassphraseContext::GpgPassphraseContext() = default;
|
||||||
|
|
||||||
|
auto GpgPassphraseContext::GetPassphrase() const -> QString {
|
||||||
|
return passphrase_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GpgPassphraseContext::SetPassphrase(const QString& passphrase) {
|
||||||
|
passphrase_ = passphrase;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto GpgPassphraseContext::GetUidsInfo() const -> QString { return uids_info_; }
|
||||||
|
|
||||||
|
auto GpgPassphraseContext::GetPassphraseInfo() const -> QString {
|
||||||
|
return passphrase_info_;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto GpgPassphraseContext::IsPreWasBad() const -> bool { return prev_was_bad_; }
|
||||||
|
|
||||||
|
auto GpgPassphraseContext::IsAskForNew() const -> bool { return ask_for_new_; }
|
61
src/m_pinentry/GpgPassphraseContext.h
Normal file
61
src/m_pinentry/GpgPassphraseContext.h
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2021 Saturneric <eric@bktus.com>
|
||||||
|
*
|
||||||
|
* 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 <eric@bktus.com> starting on May 12, 2021.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
class GpgPassphraseContext : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
GpgPassphraseContext(const QString& uids_info, const QString& passphrase_info,
|
||||||
|
bool prev_was_bad, bool ask_for_new);
|
||||||
|
|
||||||
|
GpgPassphraseContext();
|
||||||
|
|
||||||
|
void SetPassphrase(const QString& passphrase);
|
||||||
|
|
||||||
|
[[nodiscard]] auto GetPassphrase() const -> QString;
|
||||||
|
|
||||||
|
[[nodiscard]] auto GetUidsInfo() const -> QString;
|
||||||
|
|
||||||
|
[[nodiscard]] auto GetPassphraseInfo() const -> QString;
|
||||||
|
|
||||||
|
[[nodiscard]] auto IsPreWasBad() const -> bool;
|
||||||
|
|
||||||
|
[[nodiscard]] auto IsAskForNew() const -> bool;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString passphrase_info_;
|
||||||
|
QString uids_info_;
|
||||||
|
QString passphrase_;
|
||||||
|
bool prev_was_bad_;
|
||||||
|
bool ask_for_new_;
|
||||||
|
};
|
106
src/m_pinentry/PinentryModule.cpp
Normal file
106
src/m_pinentry/PinentryModule.cpp
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2021 Saturneric <eric@bktus.com>
|
||||||
|
*
|
||||||
|
* 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 <eric@bktus.com> starting on May 12, 2021.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "PinentryModule.h"
|
||||||
|
|
||||||
|
#include <qapplication.h>
|
||||||
|
#include <qobjectdefs.h>
|
||||||
|
#include <qthread.h>
|
||||||
|
|
||||||
|
#include "GFModuleCommonUtils.hpp"
|
||||||
|
#include "GFSDKBuildInfo.h"
|
||||||
|
#include "GpgPassphraseContext.h"
|
||||||
|
#include "RaisePinentry.h"
|
||||||
|
|
||||||
|
auto GFGetModuleGFSDKVersion() -> const char * {
|
||||||
|
return DUP(GF_SDK_VERSION_STR);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto GFGetModuleQtEnvVersion() -> const char * { return DUP(QT_VERSION_STR); }
|
||||||
|
|
||||||
|
auto GFGetModuleID() -> const char * {
|
||||||
|
return DUP("com.bktus.gpgfrontend.module.pinentry");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto GFGetModuleVersion() -> const char * { return DUP("1.0.0"); }
|
||||||
|
|
||||||
|
auto GFGetModuleMetaData() -> GFModuleMetaData * {
|
||||||
|
return QMapToGFModuleMetaDataList({{"Name", "Pinentry"},
|
||||||
|
{"Description", "A simple tiny pinentry."},
|
||||||
|
{"Author", "Saturneric"}});
|
||||||
|
}
|
||||||
|
|
||||||
|
auto GFRegisterModule() -> int {
|
||||||
|
MLogDebug("pinentry module registering");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto GFActiveModule() -> int {
|
||||||
|
MLogDebug("pinentry module activating");
|
||||||
|
|
||||||
|
GFModuleListenEvent(GFGetModuleID(), DUP("REQUEST_PIN_ENTRY"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto GFExecuteModule(GFModuleEvent *p_event) -> int {
|
||||||
|
MLogDebug(
|
||||||
|
QString("pinentry module executing, event id: %1").arg(p_event->id));
|
||||||
|
|
||||||
|
auto event = ConvertEventToMap(p_event);
|
||||||
|
|
||||||
|
QMetaObject::invokeMethod(
|
||||||
|
QApplication::instance()->thread(), [p_event, event]() -> int {
|
||||||
|
auto *p = new RaisePinentry(
|
||||||
|
nullptr,
|
||||||
|
SecureCreateQSharedObject<GpgPassphraseContext>(
|
||||||
|
event["uid_hint"], event["passphrase_info"],
|
||||||
|
event["prev_was_bad"].toInt(), event["ask_for_new"].toInt()));
|
||||||
|
|
||||||
|
QObject::connect(
|
||||||
|
p, &RaisePinentry::SignalUserInputPassphraseCallback, p,
|
||||||
|
[event](const QSharedPointer<GpgPassphraseContext> &c) {
|
||||||
|
GFModuleTriggerModuleEventCallback(
|
||||||
|
ConvertMapToEvent(event), GFGetModuleID(), 1,
|
||||||
|
ConvertMapToParams({{"passphrase", c->GetPassphrase()}}));
|
||||||
|
});
|
||||||
|
|
||||||
|
p->Exec();
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto GFDeactiveModule() -> int { return 0; }
|
||||||
|
|
||||||
|
auto GFUnregisterModule() -> int {
|
||||||
|
MLogDebug("pinentry module unregistering");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
56
src/m_pinentry/PinentryModule.h
Normal file
56
src/m_pinentry/PinentryModule.h
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2021 Saturneric <eric@bktus.com>
|
||||||
|
*
|
||||||
|
* 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 <eric@bktus.com> starting on May 12, 2021.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <GFSDKModule.h>
|
||||||
|
|
||||||
|
#include "GFModuleExport.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
auto GF_MODULE_EXPORT GFGetModuleGFSDKVersion() -> const char *;
|
||||||
|
|
||||||
|
auto GF_MODULE_EXPORT GFGetModuleQtEnvVersion() -> const char *;
|
||||||
|
|
||||||
|
auto GF_MODULE_EXPORT GFGetModuleID() -> const char *;
|
||||||
|
|
||||||
|
auto GF_MODULE_EXPORT GFGetModuleVersion() -> const char *;
|
||||||
|
|
||||||
|
auto GF_MODULE_EXPORT GFGetModuleMetaData() -> GFModuleMetaData *;
|
||||||
|
|
||||||
|
auto GF_MODULE_EXPORT GFRegisterModule() -> int;
|
||||||
|
|
||||||
|
auto GF_MODULE_EXPORT GFActiveModule() -> int;
|
||||||
|
|
||||||
|
auto GF_MODULE_EXPORT GFExecuteModule(GFModuleEvent *) -> int;
|
||||||
|
|
||||||
|
auto GF_MODULE_EXPORT GFDeactiveModule() -> int;
|
||||||
|
|
||||||
|
auto GF_MODULE_EXPORT GFUnregisterModule() -> int;
|
||||||
|
};
|
107
src/m_pinentry/RaisePinentry.cpp
Normal file
107
src/m_pinentry/RaisePinentry.cpp
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2021 Saturneric <eric@bktus.com>
|
||||||
|
*
|
||||||
|
* 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 <eric@bktus.com> starting on May 12, 2021.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "RaisePinentry.h"
|
||||||
|
|
||||||
|
#include <qapplication.h>
|
||||||
|
|
||||||
|
#include "GpgPassphraseContext.h"
|
||||||
|
#include "pinentrydialog.h"
|
||||||
|
|
||||||
|
auto FindTopMostWindow(QWidget* fallback) -> QWidget* {
|
||||||
|
QList<QWidget*> top_widgets = QApplication::topLevelWidgets();
|
||||||
|
foreach (QWidget* widget, top_widgets) {
|
||||||
|
if (widget->isActiveWindow()) {
|
||||||
|
return widget;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
RaisePinentry::RaisePinentry(QWidget* parent,
|
||||||
|
QSharedPointer<GpgPassphraseContext> context)
|
||||||
|
: QWidget(parent), context_(std::move(context)) {}
|
||||||
|
|
||||||
|
auto RaisePinentry::Exec() -> int {
|
||||||
|
bool ask_for_new = context_->IsAskForNew() &&
|
||||||
|
context_->GetPassphraseInfo().isEmpty() &&
|
||||||
|
context_->GetUidsInfo().isEmpty();
|
||||||
|
|
||||||
|
auto* pinentry =
|
||||||
|
new PinEntryDialog(FindTopMostWindow(this), 0, 15, true, ask_for_new,
|
||||||
|
ask_for_new ? tr("Repeat Passphrase:") : QString(),
|
||||||
|
tr("Show passphrase"), tr("Hide passphrase"));
|
||||||
|
|
||||||
|
if (context_->IsPreWasBad()) {
|
||||||
|
pinentry->setError(tr("Given Passphrase was wrong. Please retry."));
|
||||||
|
}
|
||||||
|
|
||||||
|
pinentry->setPrompt(tr("Passphrase:"));
|
||||||
|
|
||||||
|
if (!context_->GetUidsInfo().isEmpty()) {
|
||||||
|
pinentry->setDescription(QString("Please provide Passphrase of Key:\n%1\n")
|
||||||
|
.arg(context_->GetUidsInfo()));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct pinentry pinentry_info;
|
||||||
|
pinentry->setPinentryInfo(pinentry_info);
|
||||||
|
|
||||||
|
pinentry->setRepeatErrorText(tr("Passphrases do not match"));
|
||||||
|
pinentry->setGenpinLabel(QString(""));
|
||||||
|
pinentry->setGenpinTT(QString(""));
|
||||||
|
pinentry->setCapsLockHint(tr("Caps Lock is on"));
|
||||||
|
pinentry->setFormattedPassphrase({false, QString()});
|
||||||
|
pinentry->setConstraintsOptions({false, QString(), QString(), QString()});
|
||||||
|
|
||||||
|
pinentry->setWindowTitle(tr("Bundled Pinentry"));
|
||||||
|
|
||||||
|
/* If we reuse the same dialog window. */
|
||||||
|
pinentry->setPin(QString());
|
||||||
|
pinentry->setOkText(tr("Confirm"));
|
||||||
|
pinentry->setCancelText(tr("Cancel"));
|
||||||
|
|
||||||
|
connect(pinentry, &PinEntryDialog::finished, this,
|
||||||
|
[pinentry, this](int result) {
|
||||||
|
bool ret = result != 0;
|
||||||
|
|
||||||
|
if (!ret) {
|
||||||
|
emit SignalUserInputPassphraseCallback({});
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto pin = pinentry->pin().toUtf8();
|
||||||
|
|
||||||
|
context_->SetPassphrase(pin);
|
||||||
|
emit SignalUserInputPassphraseCallback(context_);
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
connect(pinentry, &PinEntryDialog::finished, this, &QWidget::deleteLater);
|
||||||
|
|
||||||
|
pinentry->open();
|
||||||
|
return 0;
|
||||||
|
}
|
58
src/m_pinentry/RaisePinentry.h
Normal file
58
src/m_pinentry/RaisePinentry.h
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2021 Saturneric <eric@bktus.com>
|
||||||
|
*
|
||||||
|
* 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 <eric@bktus.com> starting on May 12, 2021.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
class GpgPassphraseContext;
|
||||||
|
|
||||||
|
class RaisePinentry : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Construct a new Raise Pinentry object
|
||||||
|
*
|
||||||
|
* @param parent
|
||||||
|
*/
|
||||||
|
explicit RaisePinentry(QWidget *parent, QSharedPointer<GpgPassphraseContext>);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
auto Exec() -> int;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
void SignalUserInputPassphraseCallback(QSharedPointer<GpgPassphraseContext>);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QSharedPointer<GpgPassphraseContext> context_;
|
||||||
|
};
|
44
src/m_pinentry/accessibility.cpp
Normal file
44
src/m_pinentry/accessibility.cpp
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/* accessibility.cpp - Helpers for making pinentry accessible
|
||||||
|
* Copyright (C) 2021 g10 Code GmbH
|
||||||
|
*
|
||||||
|
* Software engineering by Ingo Klöcker <dev@ingo-kloecker.de>
|
||||||
|
*
|
||||||
|
* This program 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 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 this program; if not, see <https://www.gnu.org/licenses/>.
|
||||||
|
* SPDX-License-Identifier: GPL-2.0+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "accessibility.h"
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
namespace Accessibility {
|
||||||
|
|
||||||
|
void setDescription(QWidget *w, const QString &text) {
|
||||||
|
if (w) {
|
||||||
|
#ifndef QT_NO_ACCESSIBILITY
|
||||||
|
w->setAccessibleDescription(text);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setName(QWidget *w, const QString &text) {
|
||||||
|
if (w) {
|
||||||
|
#ifndef QT_NO_ACCESSIBILITY
|
||||||
|
w->setAccessibleName(text);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Accessibility
|
40
src/m_pinentry/accessibility.h
Normal file
40
src/m_pinentry/accessibility.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/* accessibility.h - Helpers for making pinentry accessible
|
||||||
|
* Copyright (C) 2021 g10 Code GmbH
|
||||||
|
*
|
||||||
|
* Software engineering by Ingo Klöcker <dev@ingo-kloecker.de>
|
||||||
|
*
|
||||||
|
* This program 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 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 this program; if not, see <https://www.gnu.org/licenses/>.
|
||||||
|
* SPDX-License-Identifier: GPL-2.0+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __PINENTRY_QT_ACCESSIBILITY_H__
|
||||||
|
#define __PINENTRY_QT_ACCESSIBILITY_H__
|
||||||
|
|
||||||
|
class QString;
|
||||||
|
class QWidget;
|
||||||
|
|
||||||
|
namespace Accessibility
|
||||||
|
{
|
||||||
|
|
||||||
|
/* Wrapper for QWidget::setAccessibleDescription which does nothing if
|
||||||
|
QT_NO_ACCESSIBILITY is defined. */
|
||||||
|
void setDescription(QWidget *w, const QString &text);
|
||||||
|
|
||||||
|
/* Wrapper for QWidget::setAccessibleName which does nothing if
|
||||||
|
QT_NO_ACCESSIBILITY is defined. */
|
||||||
|
void setName(QWidget *w, const QString &text);
|
||||||
|
|
||||||
|
} // namespace Accessibility
|
||||||
|
|
||||||
|
#endif // __PINENTRY_QT_ACCESSIBILITY_H__
|
41
src/m_pinentry/capslock/capslock.cpp
Normal file
41
src/m_pinentry/capslock/capslock.cpp
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/* capslock.cpp - Helper to check whether Caps Lock is on
|
||||||
|
* Copyright (C) 2021 g10 Code GmbH
|
||||||
|
*
|
||||||
|
* Software engineering by Ingo Klöcker <dev@ingo-kloecker.de>
|
||||||
|
*
|
||||||
|
* This program 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 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 this program; if not, see <https://www.gnu.org/licenses/>.
|
||||||
|
* SPDX-License-Identifier: GPL-2.0+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QGuiApplication>
|
||||||
|
|
||||||
|
#include "capslock.h"
|
||||||
|
|
||||||
|
CapsLockWatcher::Private::Private(CapsLockWatcher *q) : q{q} {
|
||||||
|
#ifdef PINENTRY_QT_WAYLAND
|
||||||
|
if (qApp->platformName() == QLatin1String("wayland")) {
|
||||||
|
watchWayland();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
CapsLockWatcher::CapsLockWatcher(QObject *parent)
|
||||||
|
: QObject{parent}, d{new Private{this}} {
|
||||||
|
if (qApp->platformName() == QLatin1String("wayland")) {
|
||||||
|
#ifndef PINENTRY_QT_WAYLAND
|
||||||
|
qWarning() << "CapsLockWatcher was compiled without support for Wayland";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
77
src/m_pinentry/capslock/capslock.h
Normal file
77
src/m_pinentry/capslock/capslock.h
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
/* capslock.h - Helper to check whether Caps Lock is on
|
||||||
|
* Copyright (C) 2021 g10 Code GmbH
|
||||||
|
*
|
||||||
|
* Software engineering by Ingo Klöcker <dev@ingo-kloecker.de>
|
||||||
|
*
|
||||||
|
* This program 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 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 this program; if not, see <https://www.gnu.org/licenses/>.
|
||||||
|
* SPDX-License-Identifier: GPL-2.0+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __PINENTRY_QT_CAPSLOCK_H__
|
||||||
|
#define __PINENTRY_QT_CAPSLOCK_H__
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
enum class LockState { Unknown = -1, Off, On };
|
||||||
|
|
||||||
|
LockState capsLockState();
|
||||||
|
|
||||||
|
#ifdef PINENTRY_QT_WAYLAND
|
||||||
|
namespace KWayland {
|
||||||
|
namespace Client {
|
||||||
|
class Registry;
|
||||||
|
class Seat;
|
||||||
|
} // namespace Client
|
||||||
|
} // namespace KWayland
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class CapsLockWatcher : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit CapsLockWatcher(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void stateChanged(bool locked);
|
||||||
|
|
||||||
|
private:
|
||||||
|
class Private;
|
||||||
|
std::unique_ptr<Private> d;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CapsLockWatcher::Private {
|
||||||
|
public:
|
||||||
|
explicit Private(CapsLockWatcher *);
|
||||||
|
#ifdef PINENTRY_QT_WAYLAND
|
||||||
|
void watchWayland();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
#ifdef PINENTRY_QT_WAYLAND
|
||||||
|
void registry_seatAnnounced(quint32, quint32);
|
||||||
|
void seat_hasKeyboardChanged(bool);
|
||||||
|
void keyboard_modifiersChanged(quint32);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
CapsLockWatcher *const q;
|
||||||
|
|
||||||
|
#ifdef PINENTRY_QT_WAYLAND
|
||||||
|
KWayland::Client::Registry *registry = nullptr;
|
||||||
|
KWayland::Client::Seat *seat = nullptr;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __PINENTRY_QT_CAPSLOCK_H__
|
137
src/m_pinentry/capslock/capslock_unix.cpp
Normal file
137
src/m_pinentry/capslock/capslock_unix.cpp
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
/* capslock_unix.cpp - Helper to check whether Caps Lock is on
|
||||||
|
* Copyright (C) 2021 g10 Code GmbH
|
||||||
|
*
|
||||||
|
* Software engineering by Ingo Klöcker <dev@ingo-kloecker.de>
|
||||||
|
*
|
||||||
|
* This program 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 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 this program; if not, see <https://www.gnu.org/licenses/>.
|
||||||
|
* SPDX-License-Identifier: GPL-2.0+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "capslock.h"
|
||||||
|
|
||||||
|
#ifdef PINENTRY_QT_WAYLAND
|
||||||
|
#include <KWayland/Client/connection_thread.h>
|
||||||
|
#include <KWayland/Client/keyboard.h>
|
||||||
|
#include <KWayland/Client/registry.h>
|
||||||
|
#include <KWayland/Client/seat.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <QGuiApplication>
|
||||||
|
|
||||||
|
#ifdef PINENTRY_QT_X11
|
||||||
|
#include <X11/XKBlib.h>
|
||||||
|
|
||||||
|
#include <QX11Info>
|
||||||
|
#undef Status
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#ifdef PINENTRY_QT_WAYLAND
|
||||||
|
using namespace KWayland::Client;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PINENTRY_QT_WAYLAND
|
||||||
|
static bool watchingWayland = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
LockState capsLockState() {
|
||||||
|
static bool reportUnsupportedPlatform = true;
|
||||||
|
#ifdef PINENTRY_QT_X11
|
||||||
|
if (qApp->platformName() == QLatin1String("xcb")) {
|
||||||
|
unsigned int state;
|
||||||
|
XkbGetIndicatorState(QX11Info::display(), XkbUseCoreKbd, &state);
|
||||||
|
return (state & 0x01) == 1 ? LockState::On : LockState::Off;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef PINENTRY_QT_WAYLAND
|
||||||
|
if (qApp->platformName() == QLatin1String("wayland")) {
|
||||||
|
if (!watchingWayland && reportUnsupportedPlatform) {
|
||||||
|
qDebug() << "Use CapsLockWatcher for checking for Caps Lock on Wayland";
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
if (reportUnsupportedPlatform) {
|
||||||
|
qWarning() << "Checking for Caps Lock not possible on unsupported platform:"
|
||||||
|
<< qApp->platformName();
|
||||||
|
}
|
||||||
|
reportUnsupportedPlatform = false;
|
||||||
|
return LockState::Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef PINENTRY_QT_WAYLAND
|
||||||
|
void CapsLockWatcher::Private::watchWayland() {
|
||||||
|
watchingWayland = true;
|
||||||
|
auto connection = ConnectionThread::fromApplication(q);
|
||||||
|
if (!connection) {
|
||||||
|
qWarning() << "Failed to get connection to Wayland server from QPA";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
registry = new Registry{q};
|
||||||
|
registry->create(connection);
|
||||||
|
if (!registry->isValid()) {
|
||||||
|
qWarning() << "Failed to create valid KWayland registry";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
registry->setup();
|
||||||
|
|
||||||
|
connect(registry, &Registry::seatAnnounced, q,
|
||||||
|
[this](quint32 name, quint32 version) {
|
||||||
|
registry_seatAnnounced(name, version);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void CapsLockWatcher::Private::registry_seatAnnounced(quint32 name,
|
||||||
|
quint32 version) {
|
||||||
|
Q_ASSERT(registry);
|
||||||
|
seat = registry->createSeat(name, version, q);
|
||||||
|
if (!seat->isValid()) {
|
||||||
|
qWarning() << "Failed to create valid KWayland seat";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(seat, &Seat::hasKeyboardChanged, q,
|
||||||
|
[this](bool hasKeyboard) { seat_hasKeyboardChanged(hasKeyboard); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void CapsLockWatcher::Private::seat_hasKeyboardChanged(bool hasKeyboard) {
|
||||||
|
Q_ASSERT(seat);
|
||||||
|
|
||||||
|
if (!hasKeyboard) {
|
||||||
|
qDebug() << "Seat has no keyboard";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto keyboard = seat->createKeyboard(q);
|
||||||
|
if (!keyboard->isValid()) {
|
||||||
|
qWarning() << "Failed to create valid KWayland keyboard";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(keyboard, &Keyboard::modifiersChanged, q,
|
||||||
|
[this](quint32, quint32, quint32 locked, quint32) {
|
||||||
|
keyboard_modifiersChanged(locked);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void CapsLockWatcher::Private::keyboard_modifiersChanged(quint32 locked) {
|
||||||
|
const bool capsLockIsLocked = (locked & 2u) != 0;
|
||||||
|
qDebug() << "Caps Lock is locked:" << capsLockIsLocked;
|
||||||
|
Q_EMIT q->stateChanged(capsLockIsLocked);
|
||||||
|
}
|
||||||
|
#endif
|
26
src/m_pinentry/capslock/capslock_win.cpp
Normal file
26
src/m_pinentry/capslock/capslock_win.cpp
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/* capslock_win.cpp - Helper to check whether Caps Lock is on
|
||||||
|
* Copyright (C) 2021 g10 Code GmbH
|
||||||
|
*
|
||||||
|
* Software engineering by Ingo Klöcker <dev@ingo-kloecker.de>
|
||||||
|
*
|
||||||
|
* This program 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 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 this program; if not, see <https://www.gnu.org/licenses/>.
|
||||||
|
* SPDX-License-Identifier: GPL-2.0+
|
||||||
|
*/
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include "capslock.h"
|
||||||
|
|
||||||
|
LockState capsLockState() {
|
||||||
|
return (GetKeyState(VK_CAPITAL) & 1) ? LockState::On : LockState::Off;
|
||||||
|
}
|
9
src/m_pinentry/icons/data-error.svg
Normal file
9
src/m_pinentry/icons/data-error.svg
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<svg version="1.1" viewBox="0 0 22 22" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<style type="text/css" id="current-color-scheme">
|
||||||
|
.ColorScheme-NegativeText {
|
||||||
|
color:#da4453;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<rect class="ColorScheme-NegativeText" x="3" y="3" width="16" height="16" rx="2" fill="currentColor"/>
|
||||||
|
<path d="M 6.414,5 5,6.414 9.586,11 5,15.586 6.414,17 11,12.414 15.586,17 17,15.586 12.414,11 17,6.414 15.586,5 11,9.586 Z" fill="#fff"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 471 B |
BIN
src/m_pinentry/icons/document-encrypt.png
Normal file
BIN
src/m_pinentry/icons/document-encrypt.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
13
src/m_pinentry/icons/hint.svg
Normal file
13
src/m_pinentry/icons/hint.svg
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||||
|
<defs id="defs3051">
|
||||||
|
<style type="text/css" id="current-color-scheme">
|
||||||
|
.ColorScheme-Text {
|
||||||
|
color:#232629;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<path style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||||
|
d="M 13.314453 2 L 2 13.294922 L 2.7148438 14 L 14 2.6972656 L 13.314453 2 z M 8 3 A 8.9999916 9.000003 0 0 0 0.12304688 7.6679688 C 0.25199187 8.0317035 0.48048562 8.3445563 0.77929688 8.5761719 A 7.9999926 8.0000028 0 0 1 8 4 A 3.9999993 4.0000007 0 0 0 4 8 A 3.9999993 4.0000007 0 0 0 4.1054688 8.8945312 L 5 8 A 2.9999993 3.0000005 0 0 1 8 5 L 8.8925781 4.1074219 A 3.9999993 4.0000007 0 0 0 8.3496094 4.0175781 A 7.9999926 8.0000028 0 0 1 8.9277344 4.0722656 L 9.8066406 3.1933594 A 8.9999916 9.000003 0 0 0 8 3 z M 13.835938 5.1640625 L 13.121094 5.8789062 A 7.9999926 8.0000028 0 0 1 15.220703 8.5761719 C 15.522218 8.3424607 15.752612 8.0261216 15.880859 7.6582031 A 8.9999916 9.000003 0 0 0 13.835938 5.1640625 z M 11.894531 7.1054688 L 11 8 A 2.9999993 3.0000005 0 0 1 8 11 L 7.1074219 11.892578 A 3.9999993 4.0000007 0 0 0 8 12 A 3.9999993 4.0000007 0 0 0 12 8 A 3.9999993 4.0000007 0 0 0 11.894531 7.1054688 z "
|
||||||
|
class="ColorScheme-Text"
|
||||||
|
/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
13
src/m_pinentry/icons/password-generate.svg
Normal file
13
src/m_pinentry/icons/password-generate.svg
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||||
|
<defs id="defs3051">
|
||||||
|
<style type="text/css" id="current-color-scheme">
|
||||||
|
.ColorScheme-Text {
|
||||||
|
color:#232629;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<path style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||||
|
d="m3.5 2l-.531.969-.969.531.969.531.531.969.531-.969.969-.531-.969-.531zm7.631 0l-9.125 9.125 2.875 2.875 9.125-9.125zm0 1.438l1.438 1.439-2.781 2.779-1.438-1.438z"
|
||||||
|
class="ColorScheme-Text"
|
||||||
|
/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 496 B |
21
src/m_pinentry/icons/visibility.svg
Normal file
21
src/m_pinentry/icons/visibility.svg
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||||
|
<defs id="defs3051">
|
||||||
|
<style type="text/css" id="current-color-scheme">
|
||||||
|
.ColorScheme-Text {
|
||||||
|
color:#232629;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<g
|
||||||
|
transform="translate(-421.71429,-531.79074)">
|
||||||
|
<g
|
||||||
|
transform="matrix(0.75,0,0,0.74999813,421.46429,-241.22897)">
|
||||||
|
<path
|
||||||
|
style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||||
|
d="M 8 3 A 8.9999925 9.0000023 0 0 0 0.12304688 7.6679688 C 0.2519919 8.0317178 0.48048563 8.3445725 0.77929688 8.5761719 A 7.9999935 8.0000021 0 0 1 8 4 A 3.9999996 4.0000004 0 0 0 4 8 A 3.9999996 4.0000004 0 0 0 8 12 A 3.9999996 4.0000004 0 0 0 12 8 A 3.9999996 4.0000004 0 0 0 8.3496094 4.0175781 A 7.9999935 8.0000021 0 0 1 15.220703 8.5761719 C 15.522218 8.3424725 15.752612 8.0260772 15.880859 7.6582031 A 8.9999925 9.0000023 0 0 0 8 3 z M 8 5 A 2.9999996 3.0000002 0 0 1 11 8 A 2.9999996 3.0000002 0 0 1 8 11 A 2.9999996 3.0000002 0 0 1 5 8 A 2.9999996 3.0000002 0 0 1 8 5 z M 8 6 A 1.9999999 2.0000003 0 0 0 6 8 A 1.9999999 2.0000003 0 0 0 8 10 A 1.9999999 2.0000003 0 0 0 10 8 A 1.9999999 2.0000003 0 0 0 9.9101562 7.4121094 A 0.9999999 1 0 0 1 9 8 A 0.9999999 1 0 0 1 8 7 A 0.9999999 1 0 0 1 8.5898438 6.0898438 A 1.9999999 2.0000003 0 0 0 8 6 z "
|
||||||
|
transform="matrix(1.3333333,0,0,1.3333367,0.33333333,1030.6955)"
|
||||||
|
class="ColorScheme-Text"
|
||||||
|
id="rect4170" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
304
src/m_pinentry/pinentry.cpp
Normal file
304
src/m_pinentry/pinentry.cpp
Normal file
@ -0,0 +1,304 @@
|
|||||||
|
/* pinentry.c - The PIN entry support library
|
||||||
|
* Copyright (C) 2002, 2003, 2007, 2008, 2010, 2015, 2016, 2021 g10 Code GmbH
|
||||||
|
*
|
||||||
|
* This file is part of PINENTRY.
|
||||||
|
*
|
||||||
|
* PINENTRY 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 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* PINENTRY 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 this program; if not, see <https://www.gnu.org/licenses/>.
|
||||||
|
* SPDX-License-Identifier: GPL-2.0+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "GFModuleCommonUtils.hpp"
|
||||||
|
#include "GFSDKBasic.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef WINDOWS
|
||||||
|
#include <errno.h>
|
||||||
|
#endif
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#ifndef WINDOWS
|
||||||
|
#include <sys/utsname.h>
|
||||||
|
#endif
|
||||||
|
#ifndef WINDOWS
|
||||||
|
#include <locale.h>
|
||||||
|
#endif
|
||||||
|
#include <limits.h>
|
||||||
|
#ifdef WINDOWS
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <assuan.h>
|
||||||
|
#include <qhash.h>
|
||||||
|
|
||||||
|
#include "pinentry.h"
|
||||||
|
|
||||||
|
#ifdef WINDOWS
|
||||||
|
#define getpid() GetCurrentProcessId()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Keep the name of our program here. */
|
||||||
|
static char this_pgmname[50];
|
||||||
|
|
||||||
|
struct pinentry pinentry;
|
||||||
|
|
||||||
|
static const char *flavor_flag;
|
||||||
|
|
||||||
|
/* Return a malloced copy of the commandline for PID. If this is not
|
||||||
|
* possible NULL is returned. */
|
||||||
|
#ifndef WINDOWS
|
||||||
|
static char *get_cmdline(unsigned long pid) {
|
||||||
|
char buffer[200];
|
||||||
|
FILE *fp;
|
||||||
|
size_t i, n;
|
||||||
|
|
||||||
|
snprintf(buffer, sizeof buffer, "/proc/%lu/cmdline", pid);
|
||||||
|
|
||||||
|
fp = fopen(buffer, "rb");
|
||||||
|
if (!fp) return NULL;
|
||||||
|
n = fread(buffer, 1, sizeof buffer - 1, fp);
|
||||||
|
if (n < sizeof buffer - 1 && ferror(fp)) {
|
||||||
|
/* Some error occurred. */
|
||||||
|
fclose(fp);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
if (n == 0) return NULL;
|
||||||
|
/* Arguments are delimited by Nuls. We should do proper quoting but
|
||||||
|
* that can be a bit complicated, thus we simply replace the Nuls by
|
||||||
|
* spaces. */
|
||||||
|
for (i = 0; i < n; i++)
|
||||||
|
if (!buffer[i] && i < n - 1) buffer[i] = ' ';
|
||||||
|
buffer[i] = 0; /* Make sure the last byte is the string terminator. */
|
||||||
|
|
||||||
|
return strdup(buffer);
|
||||||
|
}
|
||||||
|
#endif /*!WINDOWS*/
|
||||||
|
|
||||||
|
/* Atomically ask the kernel for information about process PID.
|
||||||
|
* Return a malloc'ed copy of the process name as long as the process
|
||||||
|
* uid matches UID. If it cannot determine that the process has uid
|
||||||
|
* UID, it returns NULL.
|
||||||
|
*
|
||||||
|
* This is not as informative as get_cmdline, but it verifies that the
|
||||||
|
* process does belong to the user in question.
|
||||||
|
*/
|
||||||
|
#ifndef WINDOWS
|
||||||
|
static char *get_pid_name_for_uid(unsigned long pid, int uid) {
|
||||||
|
char buffer[400];
|
||||||
|
FILE *fp;
|
||||||
|
size_t end, n;
|
||||||
|
char *uidstr;
|
||||||
|
|
||||||
|
snprintf(buffer, sizeof buffer, "/proc/%lu/status", pid);
|
||||||
|
|
||||||
|
fp = fopen(buffer, "rb");
|
||||||
|
if (!fp) return NULL;
|
||||||
|
n = fread(buffer, 1, sizeof buffer - 1, fp);
|
||||||
|
if (n < sizeof buffer - 1 && ferror(fp)) {
|
||||||
|
/* Some error occurred. */
|
||||||
|
fclose(fp);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
if (n == 0) return NULL;
|
||||||
|
buffer[n] = 0;
|
||||||
|
/* Fixme: Is it specified that "Name" is always the first line? For
|
||||||
|
* robustness I would prefer to have a real parser here. -wk */
|
||||||
|
if (strncmp(buffer, "Name:\t", 6)) return NULL;
|
||||||
|
end = strcspn(buffer + 6, "\n") + 6;
|
||||||
|
buffer[end] = 0;
|
||||||
|
|
||||||
|
/* check that uid matches what we expect */
|
||||||
|
uidstr = strstr(buffer + end + 1, "\nUid:\t");
|
||||||
|
if (!uidstr) return NULL;
|
||||||
|
if (atoi(uidstr + 6) != uid) return NULL;
|
||||||
|
|
||||||
|
return strdup(buffer + 6);
|
||||||
|
}
|
||||||
|
#endif /*!WINDOWS*/
|
||||||
|
|
||||||
|
const char *pinentry_get_pgmname(void) { return this_pgmname; }
|
||||||
|
|
||||||
|
/* Return a malloced string with the title. The caller mus free the
|
||||||
|
* string. If no title is available or the title string has an error
|
||||||
|
* NULL is returned. */
|
||||||
|
char *pinentry_get_title(pinentry_t pe) {
|
||||||
|
char *title;
|
||||||
|
|
||||||
|
if (pe->title) title = strdup(pe->title);
|
||||||
|
#ifndef WINDOWS
|
||||||
|
else if (pe->owner_pid) {
|
||||||
|
char buf[200];
|
||||||
|
struct utsname utsbuf;
|
||||||
|
char *pidname = NULL;
|
||||||
|
char *cmdline = NULL;
|
||||||
|
|
||||||
|
if (pe->owner_host && !uname(&utsbuf) &&
|
||||||
|
!strcmp(utsbuf.nodename, pe->owner_host)) {
|
||||||
|
pidname = get_pid_name_for_uid(pe->owner_pid, pe->owner_uid);
|
||||||
|
if (pidname) cmdline = get_cmdline(pe->owner_pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pe->owner_host && (cmdline || pidname))
|
||||||
|
snprintf(buf, sizeof buf, "[%lu]@%s (%s)", pe->owner_pid, pe->owner_host,
|
||||||
|
cmdline ? cmdline : pidname);
|
||||||
|
else if (pe->owner_host)
|
||||||
|
snprintf(buf, sizeof buf, "[%lu]@%s", pe->owner_pid, pe->owner_host);
|
||||||
|
else
|
||||||
|
snprintf(buf, sizeof buf, "[%lu] <unknown host>", pe->owner_pid);
|
||||||
|
free(pidname);
|
||||||
|
free(cmdline);
|
||||||
|
title = strdup(buf);
|
||||||
|
}
|
||||||
|
#endif /*!WINDOWS*/
|
||||||
|
else
|
||||||
|
title = strdup(this_pgmname);
|
||||||
|
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Run a quality inquiry for PASSPHRASE of LENGTH. (We need LENGTH
|
||||||
|
because not all backends might be able to return a proper
|
||||||
|
C-string.). Returns: A value between -100 and 100 to give an
|
||||||
|
estimate of the passphrase's quality. Negative values are use if
|
||||||
|
the caller won't even accept that passphrase. Note that we expect
|
||||||
|
just one data line which should not be escaped in any represent a
|
||||||
|
numeric signed decimal value. Extra data is currently ignored but
|
||||||
|
should not be send at all. */
|
||||||
|
int pinentry_inq_quality(const QString &passphrase) {
|
||||||
|
int score = 0;
|
||||||
|
|
||||||
|
score += std::min(40, static_cast<int>(passphrase.length()) * 2);
|
||||||
|
|
||||||
|
bool has_upper = false;
|
||||||
|
bool has_lower = false;
|
||||||
|
bool has_digit = false;
|
||||||
|
bool has_special = false;
|
||||||
|
for (const auto ch : passphrase) {
|
||||||
|
if (ch.isUpper()) has_upper = true;
|
||||||
|
if (ch.isLower()) has_lower = true;
|
||||||
|
if (ch.isDigit()) has_digit = true;
|
||||||
|
if (!ch.isLetterOrNumber()) has_special = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int const variety_count =
|
||||||
|
static_cast<int>(has_upper) + static_cast<int>(has_lower) +
|
||||||
|
static_cast<int>(has_digit) + static_cast<int>(has_special);
|
||||||
|
score += variety_count * 10;
|
||||||
|
|
||||||
|
for (auto i = 0; i < passphrase.length() - 1; ++i) {
|
||||||
|
if (passphrase[i] == passphrase[i + 1]) {
|
||||||
|
score -= 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<QChar, int> char_count;
|
||||||
|
for (const auto ch : passphrase) {
|
||||||
|
char_count[ch]++;
|
||||||
|
}
|
||||||
|
for (auto &p : char_count) {
|
||||||
|
if (p > 1) {
|
||||||
|
score -= (p - 1) * 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString const lower_password = passphrase.toLower();
|
||||||
|
if (lower_password.contains("password") ||
|
||||||
|
lower_password.contains("123456")) {
|
||||||
|
score -= 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::max(-100, std::min(100, score));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Run a genpin inquiry */
|
||||||
|
char *pinentry_inq_genpin(pinentry_t pin) {
|
||||||
|
assuan_context_t ctx = (assuan_context_t)pin->ctx_assuan;
|
||||||
|
const char prefix[] = "INQUIRE GENPIN";
|
||||||
|
char *line;
|
||||||
|
size_t linelen;
|
||||||
|
int gotvalue = 0;
|
||||||
|
char *value = NULL;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if (!ctx) return 0; /* Can't run the callback. */
|
||||||
|
|
||||||
|
rc = assuan_write_line(ctx, prefix);
|
||||||
|
if (rc) {
|
||||||
|
fprintf(stderr, "ASSUAN WRITE LINE failed: rc=%d\n", rc);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
do {
|
||||||
|
rc = assuan_read_line(ctx, &line, &linelen);
|
||||||
|
if (rc) {
|
||||||
|
fprintf(stderr, "ASSUAN READ LINE failed: rc=%d\n", rc);
|
||||||
|
free(value);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} while (*line == '#' || !linelen);
|
||||||
|
if (line[0] == 'E' && line[1] == 'N' && line[2] == 'D' &&
|
||||||
|
(!line[3] || line[3] == ' '))
|
||||||
|
break; /* END command received*/
|
||||||
|
if (line[0] == 'C' && line[1] == 'A' && line[2] == 'N' &&
|
||||||
|
(!line[3] || line[3] == ' '))
|
||||||
|
break; /* CAN command received*/
|
||||||
|
if (line[0] == 'E' && line[1] == 'R' && line[2] == 'R' &&
|
||||||
|
(!line[3] || line[3] == ' '))
|
||||||
|
break; /* ERR command received*/
|
||||||
|
if (line[0] != 'D' || line[1] != ' ' || linelen < 3 || gotvalue) continue;
|
||||||
|
gotvalue = 1;
|
||||||
|
value = strdup(line + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Try to make room for at least LEN bytes in the pinentry. Returns
|
||||||
|
new buffer on success and 0 on failure or when the old buffer is
|
||||||
|
sufficient. */
|
||||||
|
char *pinentry_setbufferlen(pinentry_t pin, int len) {
|
||||||
|
char *newp;
|
||||||
|
|
||||||
|
if (pin->pin_len)
|
||||||
|
assert(pin->pin);
|
||||||
|
else
|
||||||
|
assert(!pin->pin);
|
||||||
|
|
||||||
|
if (len < 2048) len = 2048;
|
||||||
|
|
||||||
|
if (len <= pin->pin_len) return pin->pin;
|
||||||
|
|
||||||
|
newp = SecureReallocAsType<char>(pin->pin, len);
|
||||||
|
if (newp) {
|
||||||
|
pin->pin = newp;
|
||||||
|
pin->pin_len = len;
|
||||||
|
} else {
|
||||||
|
GFFreeMemory(pin->pin);
|
||||||
|
pin->pin = 0;
|
||||||
|
pin->pin_len = 0;
|
||||||
|
}
|
||||||
|
return newp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the optional flag used with getinfo. */
|
||||||
|
void pinentry_set_flavor_flag(const char *string) { flavor_flag = string; }
|
339
src/m_pinentry/pinentry.h
Normal file
339
src/m_pinentry/pinentry.h
Normal file
@ -0,0 +1,339 @@
|
|||||||
|
/* pinentry.h - The interface for the PIN entry support library.
|
||||||
|
* Copyright (C) 2002, 2003, 2010, 2015, 2021 g10 Code GmbH
|
||||||
|
*
|
||||||
|
* This file is part of PINENTRY.
|
||||||
|
*
|
||||||
|
* PINENTRY 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 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* PINENTRY 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 this program; if not, see <https://www.gnu.org/licenses/>.
|
||||||
|
* SPDX-License-Identifier: GPL-2.0+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PINENTRY_H
|
||||||
|
#define PINENTRY_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#if 0
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
PINENTRY_COLOR_NONE,
|
||||||
|
PINENTRY_COLOR_DEFAULT,
|
||||||
|
PINENTRY_COLOR_BLACK,
|
||||||
|
PINENTRY_COLOR_RED,
|
||||||
|
PINENTRY_COLOR_GREEN,
|
||||||
|
PINENTRY_COLOR_YELLOW,
|
||||||
|
PINENTRY_COLOR_BLUE,
|
||||||
|
PINENTRY_COLOR_MAGENTA,
|
||||||
|
PINENTRY_COLOR_CYAN,
|
||||||
|
PINENTRY_COLOR_WHITE
|
||||||
|
} pinentry_color_t;
|
||||||
|
|
||||||
|
struct pinentry {
|
||||||
|
/* The window title, or NULL. (Assuan: "SETTITLE TITLE".) */
|
||||||
|
char *title;
|
||||||
|
/* The description to display, or NULL. (Assuan: "SETDESC
|
||||||
|
DESC".) */
|
||||||
|
char *description;
|
||||||
|
/* The error message to display, or NULL. (Assuan: "SETERROR
|
||||||
|
MESSAGE".) */
|
||||||
|
char *error;
|
||||||
|
/* The prompt to display, or NULL. (Assuan: "SETPROMPT
|
||||||
|
prompt".) */
|
||||||
|
char *prompt;
|
||||||
|
/* The OK button text to display, or NULL. (Assuan: "SETOK
|
||||||
|
OK".) */
|
||||||
|
char *ok;
|
||||||
|
/* The Not-OK button text to display, or NULL. This is the text for
|
||||||
|
the alternative option shown by the third button. (Assuan:
|
||||||
|
"SETNOTOK NOTOK".) */
|
||||||
|
char *notok;
|
||||||
|
/* The Cancel button text to display, or NULL. (Assuan: "SETCANCEL
|
||||||
|
CANCEL".) */
|
||||||
|
char *cancel;
|
||||||
|
|
||||||
|
/* The buffer to store the secret into. */
|
||||||
|
char *pin;
|
||||||
|
/* The length of the buffer. */
|
||||||
|
int pin_len;
|
||||||
|
/* Whether the pin was read from an external cache (1) or entered by
|
||||||
|
the user (0). */
|
||||||
|
int pin_from_cache;
|
||||||
|
|
||||||
|
/* The name of the X display to use if X is available and supported.
|
||||||
|
(Assuan: "OPTION display DISPLAY".) */
|
||||||
|
char *display;
|
||||||
|
/* The name of the terminal node to open if X not available or
|
||||||
|
supported. (Assuan: "OPTION ttyname TTYNAME".) */
|
||||||
|
char *ttyname;
|
||||||
|
/* The type of the terminal. (Assuan: "OPTION ttytype TTYTYPE".) */
|
||||||
|
char *ttytype_l;
|
||||||
|
/* Set the alert mode (none, beep or flash). */
|
||||||
|
char *ttyalert;
|
||||||
|
/* The LC_CTYPE value for the terminal. (Assuan: "OPTION lc-ctype
|
||||||
|
LC_CTYPE".) */
|
||||||
|
char *lc_ctype;
|
||||||
|
/* The LC_MESSAGES value for the terminal. (Assuan: "OPTION
|
||||||
|
lc-messages LC_MESSAGES".) */
|
||||||
|
char *lc_messages;
|
||||||
|
|
||||||
|
/* True if debug mode is requested. */
|
||||||
|
int debug;
|
||||||
|
|
||||||
|
/* The number of seconds before giving up while waiting for user input. */
|
||||||
|
int timeout;
|
||||||
|
|
||||||
|
/* True if caller should grab the keyboard. (Assuan: "OPTION grab"
|
||||||
|
or "OPTION no-grab".) */
|
||||||
|
int grab;
|
||||||
|
|
||||||
|
/* The PID of the owner or 0 if not known. The owner is the process
|
||||||
|
* which actually triggered the the pinentry. For example gpg. */
|
||||||
|
unsigned long owner_pid;
|
||||||
|
|
||||||
|
/* The numeric uid (user ID) of the owner process or -1 if not
|
||||||
|
* known. */
|
||||||
|
int owner_uid;
|
||||||
|
|
||||||
|
/* The malloced hostname of the owner or NULL. */
|
||||||
|
char *owner_host;
|
||||||
|
|
||||||
|
/* The window ID of the parent window over which the pinentry window
|
||||||
|
should be displayed. (Assuan: "OPTION parent-wid WID".) */
|
||||||
|
int parent_wid;
|
||||||
|
|
||||||
|
/* The name of an optional file which will be touched after a curses
|
||||||
|
entry has been displayed. (Assuan: "OPTION touch-file
|
||||||
|
FILENAME".) */
|
||||||
|
char *touch_file;
|
||||||
|
|
||||||
|
/* The frontend should set this to -1 if the user canceled the
|
||||||
|
request, and to the length of the PIN stored in pin
|
||||||
|
otherwise. */
|
||||||
|
int result;
|
||||||
|
|
||||||
|
/* The frontend should set this if the NOTOK button was pressed. */
|
||||||
|
int canceled;
|
||||||
|
|
||||||
|
/* The frontend should set this to true if an error with the local
|
||||||
|
conversion occurred. */
|
||||||
|
int locale_err;
|
||||||
|
|
||||||
|
/* The frontend should set this to a gpg-error so that commands are
|
||||||
|
able to return specific error codes. This is an ugly hack due to
|
||||||
|
the fact that pinentry_cmd_handler_t returns the length of the
|
||||||
|
passphrase or a negative error code. */
|
||||||
|
int specific_err;
|
||||||
|
|
||||||
|
/* The frontend may store a string with the error location here. */
|
||||||
|
const char *specific_err_loc;
|
||||||
|
|
||||||
|
/* The frontend may store a malloced string here to emit an ERROR
|
||||||
|
* status code with this extra info along with SPECIFIC_ERR. */
|
||||||
|
char *specific_err_info;
|
||||||
|
|
||||||
|
/* The frontend should set this to true if the window close button
|
||||||
|
has been used. This flag is used in addition to a regular return
|
||||||
|
value. */
|
||||||
|
int close_button;
|
||||||
|
|
||||||
|
/* The caller should set this to true if only one button is
|
||||||
|
required. This is useful for notification dialogs where only a
|
||||||
|
dismiss button is required. */
|
||||||
|
int one_button;
|
||||||
|
|
||||||
|
/* Whether this is a CONFIRM pinentry. */
|
||||||
|
int confirm;
|
||||||
|
|
||||||
|
/* If true a second prompt for the passphrase is shown and the user
|
||||||
|
is expected to enter the same passphrase again. Pinentry checks
|
||||||
|
that both match. (Assuan: "SETREPEAT".) */
|
||||||
|
char *repeat_passphrase;
|
||||||
|
|
||||||
|
/* The string to show if a repeated passphrase does not match.
|
||||||
|
(Assuan: "SETREPEATERROR ERROR".) */
|
||||||
|
char *repeat_error_string;
|
||||||
|
|
||||||
|
/* The string to show if a repeated passphrase does match.
|
||||||
|
(Assuan: "SETREPEATOK STRING".) */
|
||||||
|
char *repeat_ok_string;
|
||||||
|
|
||||||
|
/* Set to true if the passphrase has been entered a second time and
|
||||||
|
matches the first passphrase. */
|
||||||
|
int repeat_okay;
|
||||||
|
|
||||||
|
/* If this is not NULL, a passphrase quality indicator is shown.
|
||||||
|
There will also be an inquiry back to the caller to get an
|
||||||
|
indication of the quality for the passphrase entered so far. The
|
||||||
|
string is used as a label for the quality bar. (Assuan:
|
||||||
|
"SETQUALITYBAR LABEL".) */
|
||||||
|
char *quality_bar;
|
||||||
|
|
||||||
|
/* The tooltip to be shown for the qualitybar. Malloced or NULL.
|
||||||
|
(Assuan: "SETQUALITYBAR_TT TOOLTIP".) */
|
||||||
|
char *quality_bar_tt;
|
||||||
|
|
||||||
|
/* If this is not NULL, a generate action should be shown.
|
||||||
|
There will be an inquiry back to the caller to get such a
|
||||||
|
PIN. generate action. Malloced or NULL.
|
||||||
|
(Assuan: "SETGENPIN LABEL" .) */
|
||||||
|
char *genpin_label;
|
||||||
|
|
||||||
|
/* The tooltip to be shown for the generate action. Malloced or NULL.
|
||||||
|
(Assuan: "SETGENPIN_TT TOOLTIP".) */
|
||||||
|
char *genpin_tt;
|
||||||
|
|
||||||
|
/* Specifies whether passphrase formatting should be enabled.
|
||||||
|
(Assuan: "OPTION formatted-passphrase") */
|
||||||
|
int formatted_passphrase;
|
||||||
|
|
||||||
|
/* A hint to be shown near the passphrase input field if passphrase
|
||||||
|
formatting is enabled. Malloced or NULL.
|
||||||
|
(Assuan: "OPTION formatted-passphrase-hint=HINT".) */
|
||||||
|
char *formatted_passphrase_hint;
|
||||||
|
|
||||||
|
/* For the curses pinentry, the color of error messages. */
|
||||||
|
pinentry_color_t color_fg;
|
||||||
|
int color_fg_bright;
|
||||||
|
pinentry_color_t color_bg;
|
||||||
|
pinentry_color_t color_so;
|
||||||
|
int color_so_bright;
|
||||||
|
pinentry_color_t color_ok;
|
||||||
|
int color_ok_bright;
|
||||||
|
pinentry_color_t color_qualitybar;
|
||||||
|
int color_qualitybar_bright;
|
||||||
|
|
||||||
|
/* Malloced and i18ned default strings or NULL. These strings may
|
||||||
|
include an underscore character to indicate an accelerator key.
|
||||||
|
A double underscore represents a plain one. */
|
||||||
|
/* (Assuan: "OPTION default-ok OK"). */
|
||||||
|
char *default_ok;
|
||||||
|
/* (Assuan: "OPTION default-cancel CANCEL"). */
|
||||||
|
char *default_cancel;
|
||||||
|
/* (Assuan: "OPTION default-prompt PROMPT"). */
|
||||||
|
char *default_prompt;
|
||||||
|
/* (Assuan: "OPTION default-pwmngr
|
||||||
|
SAVE_PASSWORD_WITH_PASSWORD_MANAGER?"). */
|
||||||
|
char *default_pwmngr;
|
||||||
|
/* (Assuan: "OPTION default-cf-visi
|
||||||
|
Do you really want to make your passphrase visible?"). */
|
||||||
|
char *default_cf_visi;
|
||||||
|
/* (Assuan: "OPTION default-tt-visi
|
||||||
|
Make passphrase visible?"). */
|
||||||
|
char *default_tt_visi;
|
||||||
|
/* (Assuan: "OPTION default-tt-hide
|
||||||
|
Hide passphrase"). */
|
||||||
|
char *default_tt_hide;
|
||||||
|
/* (Assuan: "OPTION default-capshint
|
||||||
|
Caps Lock is on"). */
|
||||||
|
char *default_capshint;
|
||||||
|
|
||||||
|
/* Whether we are allowed to read the password from an external
|
||||||
|
cache. (Assuan: "OPTION allow-external-password-cache") */
|
||||||
|
int allow_external_password_cache;
|
||||||
|
|
||||||
|
/* We only try the cache once. */
|
||||||
|
int tried_password_cache;
|
||||||
|
|
||||||
|
/* A stable identifier for the key. (Assuan: "SETKEYINFO
|
||||||
|
KEYINFO".) */
|
||||||
|
char *keyinfo;
|
||||||
|
|
||||||
|
/* Whether we may cache the password (according to the user). */
|
||||||
|
int may_cache_password;
|
||||||
|
|
||||||
|
/* NOTE: If you add any additional fields to this structure, be sure
|
||||||
|
to update the initializer in pinentry/pinentry.c!!! */
|
||||||
|
|
||||||
|
/* For the quality indicator and genpin we need to do an inquiry.
|
||||||
|
Thus we need to save the assuan ctx. */
|
||||||
|
void *ctx_assuan;
|
||||||
|
|
||||||
|
/* An UTF-8 string with an invisible character used to override the
|
||||||
|
default in some pinentries. Only the first character is
|
||||||
|
used. */
|
||||||
|
char *invisible_char;
|
||||||
|
|
||||||
|
/* Whether the passphrase constraints are enforced by gpg-agent.
|
||||||
|
(Assuan: "OPTION constraints-enforce") */
|
||||||
|
int constraints_enforce;
|
||||||
|
|
||||||
|
/* A short translated hint for the user with the constraints for new
|
||||||
|
passphrases to be displayed near the passphrase input field.
|
||||||
|
Malloced or NULL.
|
||||||
|
(Assuan: "OPTION constraints-hint-short=At least 8 characters".) */
|
||||||
|
char *constraints_hint_short;
|
||||||
|
|
||||||
|
/* A longer translated hint for the user with the constraints for new
|
||||||
|
passphrases to be displayed for example as tooltip. Malloced or NULL.
|
||||||
|
(Assuan: "OPTION constraints-hint-long=The passphrase must ...".) */
|
||||||
|
char *constraints_hint_long;
|
||||||
|
|
||||||
|
/* A short translated title for an error dialog informing the user about
|
||||||
|
unsatisfied passphrase constraints. Malloced or NULL.
|
||||||
|
(Assuan: "OPTION constraints-error-title=Passphrase Not Allowed".) */
|
||||||
|
char *constraints_error_title;
|
||||||
|
};
|
||||||
|
typedef struct pinentry *pinentry_t;
|
||||||
|
|
||||||
|
/* The pinentry command handler type processes the pinentry request
|
||||||
|
PIN. If PIN->pin is zero, request a confirmation, otherwise a PIN
|
||||||
|
entry. On confirmation, the function should return TRUE if
|
||||||
|
confirmed, and FALSE otherwise. On PIN entry, the function should
|
||||||
|
return -1 if an error occurred or the user cancelled the operation
|
||||||
|
and 1 otherwise. */
|
||||||
|
typedef int (*pinentry_cmd_handler_t)(pinentry_t pin);
|
||||||
|
|
||||||
|
const char *pinentry_get_pgmname(void);
|
||||||
|
|
||||||
|
char *pinentry_get_title(pinentry_t pe);
|
||||||
|
|
||||||
|
/* Run a quality inquiry for PASSPHRASE of LENGTH. */
|
||||||
|
int pinentry_inq_quality(const QString &passphrase);
|
||||||
|
|
||||||
|
/* Run a genpin iquriry. Returns a malloced string or NULL */
|
||||||
|
char *pinentry_inq_genpin(pinentry_t pin);
|
||||||
|
|
||||||
|
/* Try to make room for at least LEN bytes for the pin in the pinentry
|
||||||
|
PIN. Returns new buffer on success and 0 on failure. */
|
||||||
|
char *pinentry_setbufferlen(pinentry_t pin, int len);
|
||||||
|
|
||||||
|
/* Return true if either DISPLAY is set or ARGV contains the string
|
||||||
|
"--display". */
|
||||||
|
int pinentry_have_display(int argc, char **argv);
|
||||||
|
|
||||||
|
/* Parse the command line options. May exit the program if only help
|
||||||
|
or version output is requested. */
|
||||||
|
void pinentry_parse_opts(int argc, char *argv[]);
|
||||||
|
|
||||||
|
/* Set the optional flag used with getinfo. */
|
||||||
|
void pinentry_set_flavor_flag(const char *string);
|
||||||
|
|
||||||
|
#ifdef WINDOWS
|
||||||
|
/* Windows declares sleep as obsolete, but provides a definition for
|
||||||
|
_sleep but non for the still existing sleep. */
|
||||||
|
#define sleep(a) _sleep((a))
|
||||||
|
#endif /*WINDOWS*/
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* PINENTRY_H */
|
10
src/m_pinentry/pinentry.qrc
Normal file
10
src/m_pinentry/pinentry.qrc
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<!DOCTYPE RCC>
|
||||||
|
<RCC>
|
||||||
|
<qresource prefix="/icons">
|
||||||
|
<file alias="data-error.svg">icons/data-error.svg</file>
|
||||||
|
<file alias="document-encrypt.png">icons/document-encrypt.png</file>
|
||||||
|
<file alias="hint.svg">icons/hint.svg</file>
|
||||||
|
<file alias="password-generate.svg">icons/password-generate.svg</file>
|
||||||
|
<file alias="visibility.svg">icons/visibility.svg</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
31
src/m_pinentry/pinentry_debug.cpp
Normal file
31
src/m_pinentry/pinentry_debug.cpp
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/* pinentry_debug.h - Logging category for pinentry
|
||||||
|
* Copyright (C) 2021 g10 Code GmbH
|
||||||
|
*
|
||||||
|
* Software engineering by Ingo Klöcker <dev@ingo-kloecker.de>
|
||||||
|
*
|
||||||
|
* This program 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 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 this program; if not, see <https://www.gnu.org/licenses/>.
|
||||||
|
* SPDX-License-Identifier: GPL-2.0+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
# include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "pinentry_debug.h"
|
||||||
|
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)
|
||||||
|
Q_LOGGING_CATEGORY(PINENTRY_LOG, "gpg.pinentry", QtWarningMsg)
|
||||||
|
#else
|
||||||
|
Q_LOGGING_CATEGORY(PINENTRY_LOG, "gpg.pinentry")
|
||||||
|
#endif
|
28
src/m_pinentry/pinentry_debug.h
Normal file
28
src/m_pinentry/pinentry_debug.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/* pinentry_debug.h - Logging category for pinentry
|
||||||
|
* Copyright (C) 2021 g10 Code GmbH
|
||||||
|
*
|
||||||
|
* Software engineering by Ingo Klöcker <dev@ingo-kloecker.de>
|
||||||
|
*
|
||||||
|
* This program 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 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 this program; if not, see <https://www.gnu.org/licenses/>.
|
||||||
|
* SPDX-License-Identifier: GPL-2.0+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __PINENTRY_QT_DEBUG_H__
|
||||||
|
#define __PINENTRY_QT_DEBUG_H__
|
||||||
|
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
|
||||||
|
Q_DECLARE_LOGGING_CATEGORY(PINENTRY_LOG)
|
||||||
|
|
||||||
|
#endif // __PINENTRY_QT_DEBUG_H__
|
123
src/m_pinentry/pinentryconfirm.cpp
Normal file
123
src/m_pinentry/pinentryconfirm.cpp
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
/* pinentryconfirm.cpp - A QMessageBox with a timeout
|
||||||
|
*
|
||||||
|
* Copyright (C) 2011 Ben Kibbey <bjk@luxsci.net>
|
||||||
|
* Copyright (C) 2022 g10 Code GmbH
|
||||||
|
*
|
||||||
|
* Software engineering by Ingo Klöcker <dev@ingo-kloecker.de>
|
||||||
|
*
|
||||||
|
* This program 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 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 this program; if not, see <https://www.gnu.org/licenses/>.
|
||||||
|
* SPDX-License-Identifier: GPL-2.0+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "pinentryconfirm.h"
|
||||||
|
|
||||||
|
#include <QAbstractButton>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QFontMetrics>
|
||||||
|
#include <QGridLayout>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QSpacerItem>
|
||||||
|
|
||||||
|
#include "accessibility.h"
|
||||||
|
#include "pinentrydialog.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
QLabel *messageBoxLabel(QMessageBox *messageBox) {
|
||||||
|
return messageBox->findChild<QLabel *>(QStringLiteral("qt_msgbox_label"));
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
PinentryConfirm::PinentryConfirm(Icon icon, const QString &title,
|
||||||
|
const QString &text, StandardButtons buttons,
|
||||||
|
QWidget *parent, Qt::WindowFlags flags)
|
||||||
|
: QMessageBox{icon, title, text, buttons, parent, flags} {
|
||||||
|
_timer.callOnTimeout(this, &PinentryConfirm::slotTimeout);
|
||||||
|
|
||||||
|
#ifndef QT_NO_ACCESSIBILITY
|
||||||
|
QAccessible::installActivationObserver(this);
|
||||||
|
accessibilityActiveChanged(QAccessible::isActive());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if QT_VERSION >= 0x050000
|
||||||
|
/* This is in line with PinentryDialog ctor to have a maximizing
|
||||||
|
* animation when opening. */
|
||||||
|
if (qApp->platformName() != QLatin1String("wayland")) {
|
||||||
|
setWindowState(Qt::WindowMinimized);
|
||||||
|
QTimer::singleShot(0, this, [this]() { raiseWindow(this); });
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
activateWindow();
|
||||||
|
raise();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
PinentryConfirm::~PinentryConfirm() {
|
||||||
|
#ifndef QT_NO_ACCESSIBILITY
|
||||||
|
QAccessible::removeActivationObserver(this);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void PinentryConfirm::setTimeout(std::chrono::seconds timeout) {
|
||||||
|
_timer.setInterval(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::chrono::seconds PinentryConfirm::timeout() const {
|
||||||
|
return std::chrono::duration_cast<std::chrono::seconds>(
|
||||||
|
_timer.intervalAsDuration());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PinentryConfirm::timedOut() const { return _timed_out; }
|
||||||
|
|
||||||
|
void PinentryConfirm::showEvent(QShowEvent *event) {
|
||||||
|
static bool resized;
|
||||||
|
if (!resized) {
|
||||||
|
QGridLayout *lay = dynamic_cast<QGridLayout *>(layout());
|
||||||
|
if (lay) {
|
||||||
|
QSize textSize = fontMetrics().size(Qt::TextExpandTabs, text(),
|
||||||
|
fontMetrics().maxWidth());
|
||||||
|
QSpacerItem *horizontalSpacer =
|
||||||
|
new QSpacerItem(textSize.width() + iconPixmap().width(), 0,
|
||||||
|
QSizePolicy::Minimum, QSizePolicy::Expanding);
|
||||||
|
lay->addItem(horizontalSpacer, lay->rowCount(), 1, 1,
|
||||||
|
lay->columnCount() - 1);
|
||||||
|
}
|
||||||
|
resized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMessageBox::showEvent(event);
|
||||||
|
|
||||||
|
if (timeout() > std::chrono::milliseconds::zero()) {
|
||||||
|
_timer.setSingleShot(true);
|
||||||
|
_timer.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PinentryConfirm::slotTimeout() {
|
||||||
|
QAbstractButton *b = button(QMessageBox::Cancel);
|
||||||
|
_timed_out = true;
|
||||||
|
|
||||||
|
if (b) {
|
||||||
|
b->animateClick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef QT_NO_ACCESSIBILITY
|
||||||
|
void PinentryConfirm::accessibilityActiveChanged(bool active) {
|
||||||
|
// Allow text label to get focus if accessibility is active
|
||||||
|
const auto focusPolicy = active ? Qt::StrongFocus : Qt::ClickFocus;
|
||||||
|
if (auto label = messageBoxLabel(this)) {
|
||||||
|
label->setFocusPolicy(focusPolicy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
63
src/m_pinentry/pinentryconfirm.h
Normal file
63
src/m_pinentry/pinentryconfirm.h
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/* pinentryconfirm.h - A QMessageBox with a timeout
|
||||||
|
*
|
||||||
|
* Copyright (C) 2011 Ben Kibbey <bjk@luxsci.net>
|
||||||
|
* Copyright (C) 2022 g10 Code GmbH
|
||||||
|
*
|
||||||
|
* Software engineering by Ingo Klöcker <dev@ingo-kloecker.de>
|
||||||
|
*
|
||||||
|
* This program 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 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 this program; if not, see <https://www.gnu.org/licenses/>.
|
||||||
|
* SPDX-License-Identifier: GPL-2.0+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PINENTRYCONFIRM_H
|
||||||
|
#define PINENTRYCONFIRM_H
|
||||||
|
|
||||||
|
#include <QAccessible>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
class PinentryConfirm : public QMessageBox
|
||||||
|
#ifndef QT_NO_ACCESSIBILITY
|
||||||
|
, public QAccessible::ActivationObserver
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
PinentryConfirm(Icon icon, const QString &title, const QString &text,
|
||||||
|
StandardButtons buttons = NoButton, QWidget *parent = nullptr,
|
||||||
|
Qt::WindowFlags flags = Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint);
|
||||||
|
~PinentryConfirm() override;
|
||||||
|
|
||||||
|
void setTimeout(std::chrono::seconds timeout);
|
||||||
|
std::chrono::seconds timeout() const;
|
||||||
|
|
||||||
|
bool timedOut() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void showEvent(QShowEvent *event) override;
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void slotTimeout();
|
||||||
|
|
||||||
|
private:
|
||||||
|
#ifndef QT_NO_ACCESSIBILITY
|
||||||
|
void accessibilityActiveChanged(bool active) override;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
QTimer _timer;
|
||||||
|
bool _timed_out = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
635
src/m_pinentry/pinentrydialog.cpp
Normal file
635
src/m_pinentry/pinentrydialog.cpp
Normal file
@ -0,0 +1,635 @@
|
|||||||
|
/* pinentrydialog.cpp - A (not yet) secure Qt 4 dialog for PIN entry.
|
||||||
|
* Copyright (C) 2002, 2008 Klarälvdalens Datakonsult AB (KDAB)
|
||||||
|
* Copyright 2007 Ingo Klöcker
|
||||||
|
* Copyright 2016 Intevation GmbH
|
||||||
|
* Copyright (C) 2021, 2022 g10 Code GmbH
|
||||||
|
*
|
||||||
|
* Written by Steffen Hansen <steffen@klaralvdalens-datakonsult.se>.
|
||||||
|
* Modified by Andre Heinecke <aheinecke@intevation.de>
|
||||||
|
* Software engineering by Ingo Klöcker <dev@ingo-kloecker.de>
|
||||||
|
*
|
||||||
|
* This program 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 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 this program; if not, see <https://www.gnu.org/licenses/>.
|
||||||
|
* SPDX-License-Identifier: GPL-2.0+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "pinentrydialog.h"
|
||||||
|
|
||||||
|
#include <qnamespace.h>
|
||||||
|
|
||||||
|
#include <QAccessible>
|
||||||
|
#include <QAction>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QCheckBox>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QDialogButtonBox>
|
||||||
|
#include <QFontMetrics>
|
||||||
|
#include <QGridLayout>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QKeyEvent>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QLineEdit>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QPalette>
|
||||||
|
#include <QProgressBar>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QStyle>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
|
#include "GFModuleCommonUtils.hpp"
|
||||||
|
#include "accessibility.h"
|
||||||
|
#include "capslock/capslock.h"
|
||||||
|
#include "pinentry.h"
|
||||||
|
#include "pinlineedit.h"
|
||||||
|
|
||||||
|
void raiseWindow(QWidget *w) {
|
||||||
|
w->setWindowState((w->windowState() & ~Qt::WindowMinimized) |
|
||||||
|
Qt::WindowActive);
|
||||||
|
w->activateWindow();
|
||||||
|
w->raise();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto applicationIconPixmap(const QIcon &overlayIcon) -> QPixmap {
|
||||||
|
QPixmap pm = qApp->windowIcon().pixmap(48, 48);
|
||||||
|
|
||||||
|
if (!overlayIcon.isNull()) {
|
||||||
|
QPainter painter(&pm);
|
||||||
|
const int emblem_size = 22;
|
||||||
|
painter.drawPixmap(pm.width() - emblem_size, 0,
|
||||||
|
overlayIcon.pixmap(emblem_size, emblem_size));
|
||||||
|
}
|
||||||
|
|
||||||
|
return pm;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PinEntryDialog::slotTimeout() {
|
||||||
|
_timed_out = true;
|
||||||
|
reject();
|
||||||
|
}
|
||||||
|
|
||||||
|
PinEntryDialog::PinEntryDialog(QWidget *parent, const char *name, int timeout,
|
||||||
|
bool modal, bool enable_quality_bar,
|
||||||
|
const QString &repeatString,
|
||||||
|
const QString &visibilityTT,
|
||||||
|
const QString &hideTT)
|
||||||
|
: QDialog{parent},
|
||||||
|
_have_quality_bar{enable_quality_bar},
|
||||||
|
mVisibilityTT{visibilityTT},
|
||||||
|
mHideTT{hideTT} {
|
||||||
|
Q_UNUSED(name)
|
||||||
|
|
||||||
|
if (modal) {
|
||||||
|
setWindowModality(Qt::ApplicationModal);
|
||||||
|
setModal(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPalette red_text_palette;
|
||||||
|
red_text_palette.setColor(QPalette::WindowText, Qt::red);
|
||||||
|
|
||||||
|
auto *const main_layout = new QVBoxLayout{this};
|
||||||
|
|
||||||
|
auto *const hbox = new QHBoxLayout;
|
||||||
|
|
||||||
|
_icon = new QLabel(this);
|
||||||
|
_icon->setPixmap(applicationIconPixmap());
|
||||||
|
hbox->addWidget(_icon, 0, Qt::AlignVCenter | Qt::AlignLeft);
|
||||||
|
|
||||||
|
auto *const grid = new QGridLayout;
|
||||||
|
int row = 1;
|
||||||
|
|
||||||
|
_error = new QLabel{this};
|
||||||
|
_error->setTextFormat(Qt::PlainText);
|
||||||
|
_error->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||||
|
_error->setPalette(red_text_palette);
|
||||||
|
_error->hide();
|
||||||
|
grid->addWidget(_error, row, 1, 1, 2);
|
||||||
|
|
||||||
|
row++;
|
||||||
|
_desc = new QLabel{this};
|
||||||
|
_desc->setTextFormat(Qt::PlainText);
|
||||||
|
_desc->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||||
|
_desc->hide();
|
||||||
|
grid->addWidget(_desc, row, 1, 1, 2);
|
||||||
|
|
||||||
|
row++;
|
||||||
|
mCapsLockHint = new QLabel{this};
|
||||||
|
mCapsLockHint->setTextFormat(Qt::PlainText);
|
||||||
|
mCapsLockHint->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||||
|
mCapsLockHint->setPalette(red_text_palette);
|
||||||
|
mCapsLockHint->setAlignment(Qt::AlignCenter);
|
||||||
|
mCapsLockHint->setVisible(false);
|
||||||
|
grid->addWidget(mCapsLockHint, row, 1, 1, 2);
|
||||||
|
|
||||||
|
row++;
|
||||||
|
{
|
||||||
|
_prompt = new QLabel(this);
|
||||||
|
_prompt->setTextFormat(Qt::PlainText);
|
||||||
|
_prompt->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||||
|
_prompt->hide();
|
||||||
|
grid->addWidget(_prompt, row, 1);
|
||||||
|
|
||||||
|
auto *const l = new QHBoxLayout;
|
||||||
|
_edit = new PinLineEdit(this);
|
||||||
|
_edit->setMaxLength(256);
|
||||||
|
_edit->setMinimumWidth(_edit->fontMetrics().averageCharWidth() * 20 + 48);
|
||||||
|
_edit->setEchoMode(QLineEdit::Password);
|
||||||
|
_prompt->setBuddy(_edit);
|
||||||
|
l->addWidget(_edit, 1);
|
||||||
|
|
||||||
|
if (!repeatString.isNull()) {
|
||||||
|
mGenerateButton = new QPushButton{this};
|
||||||
|
mGenerateButton->setIcon(
|
||||||
|
QIcon(QLatin1String(":/icons/password-generate.svg")));
|
||||||
|
mGenerateButton->setVisible(false);
|
||||||
|
l->addWidget(mGenerateButton);
|
||||||
|
}
|
||||||
|
grid->addLayout(l, row, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set up the show password action */
|
||||||
|
const QIcon visibility_icon = QIcon(QLatin1String(":/icons/visibility.svg"));
|
||||||
|
const QIcon hide_icon = QIcon(QLatin1String(":/icons/hint.svg"));
|
||||||
|
#if QT_VERSION >= 0x050200
|
||||||
|
if (!visibility_icon.isNull() && !hide_icon.isNull()) {
|
||||||
|
mVisiActionEdit =
|
||||||
|
_edit->addAction(visibility_icon, QLineEdit::TrailingPosition);
|
||||||
|
mVisiActionEdit->setVisible(false);
|
||||||
|
mVisiActionEdit->setToolTip(mVisibilityTT);
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
if (!mVisibilityTT.isNull()) {
|
||||||
|
row++;
|
||||||
|
mVisiCB = new QCheckBox{mVisibilityTT, this};
|
||||||
|
grid->addWidget(mVisiCB, row, 1, 1, 2, Qt::AlignLeft);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
row++;
|
||||||
|
mConstraintsHint = new QLabel{this};
|
||||||
|
mConstraintsHint->setTextFormat(Qt::PlainText);
|
||||||
|
mConstraintsHint->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||||
|
mConstraintsHint->setVisible(false);
|
||||||
|
grid->addWidget(mConstraintsHint, row, 2);
|
||||||
|
|
||||||
|
row++;
|
||||||
|
mFormattedPassphraseHintSpacer = new QLabel{this};
|
||||||
|
mFormattedPassphraseHintSpacer->setVisible(false);
|
||||||
|
mFormattedPassphraseHint = new QLabel{this};
|
||||||
|
mFormattedPassphraseHint->setTextFormat(Qt::PlainText);
|
||||||
|
mFormattedPassphraseHint->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||||
|
mFormattedPassphraseHint->setVisible(false);
|
||||||
|
grid->addWidget(mFormattedPassphraseHintSpacer, row, 1);
|
||||||
|
grid->addWidget(mFormattedPassphraseHint, row, 2);
|
||||||
|
|
||||||
|
if (!repeatString.isNull()) {
|
||||||
|
row++;
|
||||||
|
auto *repeat_label = new QLabel{this};
|
||||||
|
repeat_label->setTextFormat(Qt::PlainText);
|
||||||
|
repeat_label->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||||
|
repeat_label->setText(repeatString);
|
||||||
|
grid->addWidget(repeat_label, row, 1);
|
||||||
|
|
||||||
|
mRepeat = new PinLineEdit(this);
|
||||||
|
mRepeat->setMaxLength(256);
|
||||||
|
mRepeat->setEchoMode(QLineEdit::Password);
|
||||||
|
repeat_label->setBuddy(mRepeat);
|
||||||
|
grid->addWidget(mRepeat, row, 2);
|
||||||
|
|
||||||
|
row++;
|
||||||
|
mRepeatError = new QLabel{this};
|
||||||
|
mRepeatError->setTextFormat(Qt::PlainText);
|
||||||
|
mRepeatError->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||||
|
mRepeatError->setPalette(red_text_palette);
|
||||||
|
mRepeatError->hide();
|
||||||
|
grid->addWidget(mRepeatError, row, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enable_quality_bar) {
|
||||||
|
row++;
|
||||||
|
_quality_bar_label = new QLabel(this);
|
||||||
|
_quality_bar_label->setTextFormat(Qt::PlainText);
|
||||||
|
_quality_bar_label->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||||
|
_quality_bar_label->setAlignment(Qt::AlignVCenter);
|
||||||
|
grid->addWidget(_quality_bar_label, row, 1);
|
||||||
|
|
||||||
|
_quality_bar = new QProgressBar(this);
|
||||||
|
_quality_bar->setAlignment(Qt::AlignCenter);
|
||||||
|
_quality_bar_label->setBuddy(_quality_bar);
|
||||||
|
grid->addWidget(_quality_bar, row, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
hbox->addLayout(grid, 1);
|
||||||
|
main_layout->addLayout(hbox);
|
||||||
|
|
||||||
|
auto *const buttons = new QDialogButtonBox(this);
|
||||||
|
buttons->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||||
|
_ok = buttons->button(QDialogButtonBox::Ok);
|
||||||
|
_cancel = buttons->button(QDialogButtonBox::Cancel);
|
||||||
|
|
||||||
|
if (style()->styleHint(QStyle::SH_DialogButtonBox_ButtonsHaveIcons)) {
|
||||||
|
_ok->setIcon(style()->standardIcon(QStyle::SP_DialogOkButton));
|
||||||
|
_cancel->setIcon(style()->standardIcon(QStyle::SP_DialogCancelButton));
|
||||||
|
}
|
||||||
|
|
||||||
|
main_layout->addStretch(1);
|
||||||
|
main_layout->addWidget(buttons);
|
||||||
|
main_layout->setSizeConstraint(QLayout::SetFixedSize);
|
||||||
|
|
||||||
|
if (timeout > 0) {
|
||||||
|
_timer = new QTimer(this);
|
||||||
|
connect(_timer, &QTimer::timeout, this, &PinEntryDialog::slotTimeout);
|
||||||
|
_timer->start(timeout * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(buttons, &QDialogButtonBox::accepted, this,
|
||||||
|
&PinEntryDialog::onAccept);
|
||||||
|
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||||
|
connect(_edit, &QLineEdit::textChanged, this, &PinEntryDialog::updateQuality);
|
||||||
|
connect(_edit, &QLineEdit::textChanged, this, &PinEntryDialog::textChanged);
|
||||||
|
connect(_edit, &PinLineEdit::backspacePressed, this,
|
||||||
|
&PinEntryDialog::onBackspace);
|
||||||
|
if (mGenerateButton != nullptr) {
|
||||||
|
connect(mGenerateButton, &QPushButton::clicked, this,
|
||||||
|
&PinEntryDialog::generatePin);
|
||||||
|
}
|
||||||
|
if (mVisiActionEdit != nullptr) {
|
||||||
|
connect(mVisiActionEdit, &QAction::triggered, this,
|
||||||
|
&PinEntryDialog::toggleVisibility);
|
||||||
|
}
|
||||||
|
if (mVisiCB != nullptr) {
|
||||||
|
connect(mVisiCB, &QCheckBox::toggled, this,
|
||||||
|
&PinEntryDialog::toggleVisibility);
|
||||||
|
}
|
||||||
|
if (mRepeat != nullptr) {
|
||||||
|
connect(mRepeat, &QLineEdit::textChanged, this,
|
||||||
|
&PinEntryDialog::textChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto *caps_lock_watcher = new CapsLockWatcher{this};
|
||||||
|
connect(caps_lock_watcher, &CapsLockWatcher::stateChanged, this,
|
||||||
|
[this](bool locked) { mCapsLockHint->setVisible(locked); });
|
||||||
|
|
||||||
|
connect(qApp, &QApplication::focusChanged, this,
|
||||||
|
&PinEntryDialog::focusChanged);
|
||||||
|
connect(qApp, &QApplication::applicationStateChanged, this,
|
||||||
|
&PinEntryDialog::checkCapsLock);
|
||||||
|
checkCapsLock();
|
||||||
|
|
||||||
|
setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
|
||||||
|
|
||||||
|
/* This is mostly an issue on Windows where this results
|
||||||
|
in the pinentry popping up nicely with an animation and
|
||||||
|
comes to front. It is not ifdefed for Windows only since
|
||||||
|
window managers on Linux like KWin can also have this
|
||||||
|
result in an animation when the pinentry is shown and
|
||||||
|
not just popping it up.
|
||||||
|
*/
|
||||||
|
if (qApp->platformName() != QLatin1String("wayland")) {
|
||||||
|
setWindowState(Qt::WindowMinimized);
|
||||||
|
QTimer::singleShot(0, this, [this]() { raiseWindow(this); });
|
||||||
|
} else {
|
||||||
|
raiseWindow(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PinEntryDialog::keyPressEvent(QKeyEvent *e) {
|
||||||
|
const auto return_pressed =
|
||||||
|
(!e->modifiers() &&
|
||||||
|
(e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return)) ||
|
||||||
|
(e->modifiers() & Qt::KeypadModifier && e->key() == Qt::Key_Enter);
|
||||||
|
if (return_pressed && _edit->hasFocus() && (mRepeat != nullptr)) {
|
||||||
|
// if the user pressed Return in the first input field, then move the
|
||||||
|
// focus to the repeat input field and prevent further event processing
|
||||||
|
// by QDialog (which would trigger the default button)
|
||||||
|
mRepeat->setFocus();
|
||||||
|
e->ignore();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDialog::keyPressEvent(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PinEntryDialog::keyReleaseEvent(QKeyEvent *event) {
|
||||||
|
QDialog::keyReleaseEvent(event);
|
||||||
|
checkCapsLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PinEntryDialog::showEvent(QShowEvent *event) {
|
||||||
|
QDialog::showEvent(event);
|
||||||
|
_edit->setFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PinEntryDialog::setDescription(const QString &txt) {
|
||||||
|
_desc->setVisible(!txt.isEmpty());
|
||||||
|
_desc->setText(txt);
|
||||||
|
_icon->setPixmap(applicationIconPixmap());
|
||||||
|
setError(QString());
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PinEntryDialog::description() const { return _desc->text(); }
|
||||||
|
|
||||||
|
void PinEntryDialog::setError(const QString &txt) {
|
||||||
|
if (!txt.isNull()) {
|
||||||
|
_icon->setPixmap(
|
||||||
|
applicationIconPixmap(QIcon{QStringLiteral(":/icons/data-error.svg")}));
|
||||||
|
}
|
||||||
|
_error->setText(txt);
|
||||||
|
_error->setVisible(!txt.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PinEntryDialog::error() const { return _error->text(); }
|
||||||
|
|
||||||
|
void PinEntryDialog::setPin(const QString &txt) { _edit->setPin(txt); }
|
||||||
|
|
||||||
|
QString PinEntryDialog::pin() const { return _edit->pin(); }
|
||||||
|
|
||||||
|
void PinEntryDialog::setPrompt(const QString &txt) {
|
||||||
|
_prompt->setText(txt);
|
||||||
|
_prompt->setVisible(!txt.isEmpty());
|
||||||
|
if (txt.contains("PIN")) _disable_echo_allowed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PinEntryDialog::prompt() const { return _prompt->text(); }
|
||||||
|
|
||||||
|
void PinEntryDialog::setOkText(const QString &txt) {
|
||||||
|
_ok->setText(txt);
|
||||||
|
_ok->setVisible(!txt.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
void PinEntryDialog::setCancelText(const QString &txt) {
|
||||||
|
_cancel->setText(txt);
|
||||||
|
_cancel->setVisible(!txt.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
void PinEntryDialog::setQualityBar(const QString &txt) {
|
||||||
|
if (_have_quality_bar) {
|
||||||
|
_quality_bar_label->setText(txt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PinEntryDialog::setQualityBarTT(const QString &txt) {
|
||||||
|
if (_have_quality_bar) {
|
||||||
|
_quality_bar->setToolTip(txt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PinEntryDialog::setGenpinLabel(const QString &txt) {
|
||||||
|
if (mGenerateButton == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mGenerateButton->setVisible(!txt.isEmpty());
|
||||||
|
if (!txt.isEmpty()) {
|
||||||
|
Accessibility::setName(mGenerateButton, txt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PinEntryDialog::setGenpinTT(const QString &txt) {
|
||||||
|
if (mGenerateButton != nullptr) {
|
||||||
|
mGenerateButton->setToolTip(txt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PinEntryDialog::setCapsLockHint(const QString &txt) {
|
||||||
|
mCapsLockHint->setText(txt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PinEntryDialog::setFormattedPassphrase(
|
||||||
|
const PinEntryDialog::FormattedPassphraseOptions &options) {
|
||||||
|
mFormatPassphrase = options.formatPassphrase;
|
||||||
|
mFormattedPassphraseHint->setTextFormat(Qt::RichText);
|
||||||
|
mFormattedPassphraseHint->setText(QLatin1String("<html>") +
|
||||||
|
options.hint.toHtmlEscaped() +
|
||||||
|
QLatin1String("</html>"));
|
||||||
|
Accessibility::setName(mFormattedPassphraseHint, options.hint);
|
||||||
|
// toggleFormattedPassphrase();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PinEntryDialog::setConstraintsOptions(const ConstraintsOptions &options) {
|
||||||
|
mEnforceConstraints = options.enforce;
|
||||||
|
mConstraintsHint->setText(options.shortHint);
|
||||||
|
if (!options.longHint.isEmpty()) {
|
||||||
|
mConstraintsHint->setToolTip(
|
||||||
|
QLatin1String("<html>") +
|
||||||
|
options.longHint.toHtmlEscaped().replace(QLatin1String("\n\n"),
|
||||||
|
QLatin1String("<br>")) +
|
||||||
|
QLatin1String("</html>"));
|
||||||
|
Accessibility::setDescription(mConstraintsHint, options.longHint);
|
||||||
|
}
|
||||||
|
mConstraintsErrorTitle = options.errorTitle;
|
||||||
|
|
||||||
|
mConstraintsHint->setVisible(mEnforceConstraints &&
|
||||||
|
!options.shortHint.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
void PinEntryDialog::toggleFormattedPassphrase() {
|
||||||
|
const bool enable_formatting =
|
||||||
|
mFormatPassphrase && _edit->echoMode() == QLineEdit::Normal;
|
||||||
|
_edit->setFormattedPassphrase(enable_formatting);
|
||||||
|
if (mRepeat != nullptr) {
|
||||||
|
mRepeat->setFormattedPassphrase(enable_formatting);
|
||||||
|
const bool hint_about_to_be_hidden =
|
||||||
|
mFormattedPassphraseHint->isVisible() && !enable_formatting;
|
||||||
|
if (hint_about_to_be_hidden) {
|
||||||
|
// set hint spacer to current height of hint label before hiding the hint
|
||||||
|
mFormattedPassphraseHintSpacer->setMinimumHeight(
|
||||||
|
mFormattedPassphraseHint->height());
|
||||||
|
mFormattedPassphraseHintSpacer->setVisible(true);
|
||||||
|
} else if (enable_formatting) {
|
||||||
|
mFormattedPassphraseHintSpacer->setVisible(false);
|
||||||
|
}
|
||||||
|
mFormattedPassphraseHint->setVisible(enable_formatting);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PinEntryDialog::onBackspace() {
|
||||||
|
cancelTimeout();
|
||||||
|
|
||||||
|
if (_disable_echo_allowed) {
|
||||||
|
_edit->setEchoMode(QLineEdit::NoEcho);
|
||||||
|
if (mRepeat != nullptr) {
|
||||||
|
mRepeat->setEchoMode(QLineEdit::NoEcho);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PinEntryDialog::updateQuality(const QString &txt) {
|
||||||
|
int length;
|
||||||
|
int percent;
|
||||||
|
QPalette pal;
|
||||||
|
|
||||||
|
_disable_echo_allowed = false;
|
||||||
|
|
||||||
|
if (!_have_quality_bar) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
length = txt.length();
|
||||||
|
percent = length != 0 ? pinentry_inq_quality(txt) : 0;
|
||||||
|
if (length == 0) {
|
||||||
|
_quality_bar->reset();
|
||||||
|
} else {
|
||||||
|
pal = _quality_bar->palette();
|
||||||
|
if (percent < 0) {
|
||||||
|
pal.setColor(QPalette::Highlight, QColor("red"));
|
||||||
|
percent = -percent;
|
||||||
|
} else {
|
||||||
|
pal.setColor(QPalette::Highlight, QColor("green"));
|
||||||
|
}
|
||||||
|
_quality_bar->setPalette(pal);
|
||||||
|
_quality_bar->setValue(percent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PinEntryDialog::setPinentryInfo(struct pinentry peinfo) {
|
||||||
|
_pinentry_info = SecureCreateQSharedObject<struct pinentry>(peinfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PinEntryDialog::focusChanged(QWidget *old, QWidget *now) {
|
||||||
|
// Grab keyboard. It might be a little weird to do it here, but it works!
|
||||||
|
// Previously this code was in showEvent, but that did not work in Qt4.
|
||||||
|
if (!_pinentry_info || (_pinentry_info->grab != 0)) {
|
||||||
|
if (_grabbed && (old != nullptr) && (old == _edit || old == mRepeat)) {
|
||||||
|
old->releaseKeyboard();
|
||||||
|
_grabbed = false;
|
||||||
|
}
|
||||||
|
if (!_grabbed && (now != nullptr) && (now == _edit || now == mRepeat)) {
|
||||||
|
now->grabKeyboard();
|
||||||
|
_grabbed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PinEntryDialog::textChanged(const QString &text) {
|
||||||
|
Q_UNUSED(text);
|
||||||
|
|
||||||
|
cancelTimeout();
|
||||||
|
|
||||||
|
if ((mVisiActionEdit != nullptr) && sender() == _edit) {
|
||||||
|
mVisiActionEdit->setVisible(!_edit->pin().isEmpty());
|
||||||
|
}
|
||||||
|
if (mGenerateButton != nullptr) {
|
||||||
|
mGenerateButton->setVisible(_edit->pin().isEmpty()
|
||||||
|
#ifndef QT_NO_ACCESSIBILITY
|
||||||
|
&& !mGenerateButton->accessibleName().isEmpty()
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PinEntryDialog::generatePin() {
|
||||||
|
// std::unique_ptr<char> pin{pinentry_inq_genpin(_pinentry_info.get())};
|
||||||
|
// if (pin) {
|
||||||
|
// if (_edit->echoMode() == QLineEdit::Password) {
|
||||||
|
// if (mVisiActionEdit != nullptr) {
|
||||||
|
// mVisiActionEdit->trigger();
|
||||||
|
// }
|
||||||
|
// if (mVisiCB != nullptr) {
|
||||||
|
// mVisiCB->setChecked(true);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// const auto pin_str = QString::fromUtf8(pin.get());
|
||||||
|
// _edit->setPin(pin_str);
|
||||||
|
// mRepeat->setPin(pin_str);
|
||||||
|
// // explicitly focus the first input field and select the generated
|
||||||
|
// password _edit->setFocus(); _edit->selectAll();
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
void PinEntryDialog::toggleVisibility() {
|
||||||
|
if (sender() != mVisiCB) {
|
||||||
|
if (_edit->echoMode() == QLineEdit::Password) {
|
||||||
|
if (mVisiActionEdit != nullptr) {
|
||||||
|
mVisiActionEdit->setIcon(QIcon(QLatin1String(":/icons/hint.svg")));
|
||||||
|
mVisiActionEdit->setToolTip(mHideTT);
|
||||||
|
}
|
||||||
|
_edit->setEchoMode(QLineEdit::Normal);
|
||||||
|
if (mRepeat != nullptr) {
|
||||||
|
mRepeat->setEchoMode(QLineEdit::Normal);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (mVisiActionEdit != nullptr) {
|
||||||
|
mVisiActionEdit->setIcon(
|
||||||
|
QIcon(QLatin1String(":/icons/visibility.svg")));
|
||||||
|
mVisiActionEdit->setToolTip(mVisibilityTT);
|
||||||
|
}
|
||||||
|
_edit->setEchoMode(QLineEdit::Password);
|
||||||
|
if (mRepeat != nullptr) {
|
||||||
|
mRepeat->setEchoMode(QLineEdit::Password);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (mVisiCB->isChecked()) {
|
||||||
|
if (mRepeat != nullptr) {
|
||||||
|
mRepeat->setEchoMode(QLineEdit::Normal);
|
||||||
|
}
|
||||||
|
_edit->setEchoMode(QLineEdit::Normal);
|
||||||
|
} else {
|
||||||
|
if (mRepeat != nullptr) {
|
||||||
|
mRepeat->setEchoMode(QLineEdit::Password);
|
||||||
|
}
|
||||||
|
_edit->setEchoMode(QLineEdit::Password);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
toggleFormattedPassphrase();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PinEntryDialog::repeatedPin() const {
|
||||||
|
if (mRepeat != nullptr) {
|
||||||
|
return mRepeat->pin();
|
||||||
|
}
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PinEntryDialog::timedOut() const { return _timed_out; }
|
||||||
|
|
||||||
|
void PinEntryDialog::setRepeatErrorText(const QString &err) {
|
||||||
|
if (mRepeatError != nullptr) {
|
||||||
|
mRepeatError->setText(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PinEntryDialog::cancelTimeout() {
|
||||||
|
if (_timer != nullptr) {
|
||||||
|
_timer->stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PinEntryDialog::checkCapsLock() {
|
||||||
|
const auto state = capsLockState();
|
||||||
|
if (state != LockState::Unknown) {
|
||||||
|
mCapsLockHint->setVisible(state == LockState::On);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PinEntryDialog::onAccept() {
|
||||||
|
cancelTimeout();
|
||||||
|
|
||||||
|
if ((mRepeat != nullptr) && mRepeat->pin() != _edit->pin()) {
|
||||||
|
#ifndef QT_NO_ACCESSIBILITY
|
||||||
|
if (QAccessible::isActive()) {
|
||||||
|
QMessageBox::information(this, mRepeatError->text(),
|
||||||
|
mRepeatError->text());
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
mRepeatError->setVisible(true);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
accept();
|
||||||
|
}
|
169
src/m_pinentry/pinentrydialog.h
Normal file
169
src/m_pinentry/pinentrydialog.h
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
/* pinentrydialog.h - A (not yet) secure Qt 4 dialog for PIN entry.
|
||||||
|
* Copyright (C) 2002, 2008 Klarälvdalens Datakonsult AB (KDAB)
|
||||||
|
* Copyright 2007 Ingo Klöcker
|
||||||
|
* Copyright 2016 Intevation GmbH
|
||||||
|
* Copyright (C) 2021, 2022 g10 Code GmbH
|
||||||
|
*
|
||||||
|
* Written by Steffen Hansen <steffen@klaralvdalens-datakonsult.se>.
|
||||||
|
* Modified by Andre Heinecke <aheinecke@intevation.de>
|
||||||
|
* Software engineering by Ingo Klöcker <dev@ingo-kloecker.de>
|
||||||
|
*
|
||||||
|
* This program 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 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 this program; if not, see <https://www.gnu.org/licenses/>.
|
||||||
|
* SPDX-License-Identifier: GPL-2.0+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __PINENTRYDIALOG_H__
|
||||||
|
#define __PINENTRYDIALOG_H__
|
||||||
|
|
||||||
|
#include <QAccessible>
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QStyle>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include "pinentry.h"
|
||||||
|
|
||||||
|
class QIcon;
|
||||||
|
class QLabel;
|
||||||
|
class QPushButton;
|
||||||
|
class QLineEdit;
|
||||||
|
class PinLineEdit;
|
||||||
|
class QString;
|
||||||
|
class QProgressBar;
|
||||||
|
class QCheckBox;
|
||||||
|
class QAction;
|
||||||
|
|
||||||
|
QPixmap applicationIconPixmap(const QIcon &overlayIcon = {});
|
||||||
|
|
||||||
|
void raiseWindow(QWidget *w);
|
||||||
|
|
||||||
|
class PinEntryDialog : public QDialog {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(QString description READ description WRITE setDescription)
|
||||||
|
Q_PROPERTY(QString error READ error WRITE setError)
|
||||||
|
Q_PROPERTY(QString pin READ pin WRITE setPin)
|
||||||
|
Q_PROPERTY(QString prompt READ prompt WRITE setPrompt)
|
||||||
|
public:
|
||||||
|
struct FormattedPassphraseOptions {
|
||||||
|
bool formatPassphrase;
|
||||||
|
QString hint;
|
||||||
|
};
|
||||||
|
struct ConstraintsOptions {
|
||||||
|
bool enforce;
|
||||||
|
QString shortHint;
|
||||||
|
QString longHint;
|
||||||
|
QString errorTitle;
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit PinEntryDialog(QWidget *parent = 0, const char *name = 0,
|
||||||
|
int timeout = 0, bool modal = false,
|
||||||
|
bool enable_quality_bar = false,
|
||||||
|
const QString &repeatString = QString(),
|
||||||
|
const QString &visibiltyTT = QString(),
|
||||||
|
const QString &hideTT = QString());
|
||||||
|
|
||||||
|
void setDescription(const QString &);
|
||||||
|
QString description() const;
|
||||||
|
|
||||||
|
void setError(const QString &);
|
||||||
|
QString error() const;
|
||||||
|
|
||||||
|
void setPin(const QString &);
|
||||||
|
QString pin() const;
|
||||||
|
|
||||||
|
QString repeatedPin() const;
|
||||||
|
void setRepeatErrorText(const QString &);
|
||||||
|
|
||||||
|
void setPrompt(const QString &);
|
||||||
|
QString prompt() const;
|
||||||
|
|
||||||
|
void setOkText(const QString &);
|
||||||
|
void setCancelText(const QString &);
|
||||||
|
|
||||||
|
void setQualityBar(const QString &);
|
||||||
|
void setQualityBarTT(const QString &);
|
||||||
|
|
||||||
|
void setGenpinLabel(const QString &);
|
||||||
|
void setGenpinTT(const QString &);
|
||||||
|
|
||||||
|
void setCapsLockHint(const QString &);
|
||||||
|
|
||||||
|
void setFormattedPassphrase(const FormattedPassphraseOptions &options);
|
||||||
|
|
||||||
|
void setConstraintsOptions(const ConstraintsOptions &options);
|
||||||
|
|
||||||
|
void setPinentryInfo(struct pinentry);
|
||||||
|
|
||||||
|
bool timedOut() const;
|
||||||
|
|
||||||
|
protected Q_SLOTS:
|
||||||
|
void updateQuality(const QString &);
|
||||||
|
void slotTimeout();
|
||||||
|
void textChanged(const QString &);
|
||||||
|
void focusChanged(QWidget *old, QWidget *now);
|
||||||
|
void toggleVisibility();
|
||||||
|
void onBackspace();
|
||||||
|
void generatePin();
|
||||||
|
void toggleFormattedPassphrase();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void keyPressEvent(QKeyEvent *event) override;
|
||||||
|
void keyReleaseEvent(QKeyEvent *event) override;
|
||||||
|
void showEvent(QShowEvent *event) override;
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void cancelTimeout();
|
||||||
|
void checkCapsLock();
|
||||||
|
void onAccept();
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum PassphraseCheckResult {
|
||||||
|
PassphraseNotChecked = -1,
|
||||||
|
PassphraseNotOk = 0,
|
||||||
|
PassphraseOk
|
||||||
|
};
|
||||||
|
|
||||||
|
QLabel *_icon = nullptr;
|
||||||
|
QLabel *_desc = nullptr;
|
||||||
|
QLabel *_error = nullptr;
|
||||||
|
QLabel *_prompt = nullptr;
|
||||||
|
QLabel *_quality_bar_label = nullptr;
|
||||||
|
QProgressBar *_quality_bar = nullptr;
|
||||||
|
PinLineEdit *_edit = nullptr;
|
||||||
|
PinLineEdit *mRepeat = nullptr;
|
||||||
|
QLabel *mRepeatError = nullptr;
|
||||||
|
QPushButton *_ok = nullptr;
|
||||||
|
QPushButton *_cancel = nullptr;
|
||||||
|
bool _grabbed = false;
|
||||||
|
bool _have_quality_bar = false;
|
||||||
|
bool _timed_out = false;
|
||||||
|
bool _disable_echo_allowed = true;
|
||||||
|
bool mEnforceConstraints = false;
|
||||||
|
bool mFormatPassphrase = false;
|
||||||
|
|
||||||
|
QSharedPointer<struct pinentry> _pinentry_info = nullptr;
|
||||||
|
QTimer *_timer = nullptr;
|
||||||
|
QString mVisibilityTT;
|
||||||
|
QString mHideTT;
|
||||||
|
QAction *mVisiActionEdit = nullptr;
|
||||||
|
QPushButton *mGenerateButton = nullptr;
|
||||||
|
QCheckBox *mVisiCB = nullptr;
|
||||||
|
QLabel *mFormattedPassphraseHint = nullptr;
|
||||||
|
QLabel *mFormattedPassphraseHintSpacer = nullptr;
|
||||||
|
QLabel *mCapsLockHint = nullptr;
|
||||||
|
QLabel *mConstraintsHint = nullptr;
|
||||||
|
QString mConstraintsErrorTitle;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __PINENTRYDIALOG_H__
|
204
src/m_pinentry/pinlineedit.cpp
Normal file
204
src/m_pinentry/pinlineedit.cpp
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
/* pinlineedit.cpp - Modified QLineEdit widget.
|
||||||
|
* Copyright (C) 2018 Damien Goutte-Gattat
|
||||||
|
* Copyright (C) 2021 g10 Code GmbH
|
||||||
|
*
|
||||||
|
* Software engineering by Ingo Klöcker <dev@ingo-kloecker.de>
|
||||||
|
*
|
||||||
|
* This program 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 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 this program; if not, see <https://www.gnu.org/licenses/>.
|
||||||
|
* SPDX-License-Identifier: GPL-2.0+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "pinlineedit.h"
|
||||||
|
|
||||||
|
#include <QClipboard>
|
||||||
|
#include <QGuiApplication>
|
||||||
|
#include <QKeyEvent>
|
||||||
|
|
||||||
|
static const int FormattedPassphraseGroupSize = 5;
|
||||||
|
static const QChar FormattedPassphraseSeparator = QChar::Nbsp;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
struct Selection {
|
||||||
|
bool empty() const { return start < 0 || start >= end; }
|
||||||
|
int length() const { return empty() ? 0 : end - start; }
|
||||||
|
|
||||||
|
int start;
|
||||||
|
int end;
|
||||||
|
};
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
class PinLineEdit::Private {
|
||||||
|
PinLineEdit *const q;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Private(PinLineEdit *q) : q{q} {}
|
||||||
|
|
||||||
|
QString formatted(QString text) const {
|
||||||
|
const int dashCount = text.size() / FormattedPassphraseGroupSize;
|
||||||
|
text.reserve(text.size() + dashCount);
|
||||||
|
for (int i = FormattedPassphraseGroupSize; i < text.size();
|
||||||
|
i += FormattedPassphraseGroupSize + 1) {
|
||||||
|
text.insert(i, FormattedPassphraseSeparator);
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
Selection formattedSelection(Selection selection) const {
|
||||||
|
if (selection.empty()) {
|
||||||
|
return selection;
|
||||||
|
}
|
||||||
|
return {selection.start + selection.start / FormattedPassphraseGroupSize,
|
||||||
|
selection.end + (selection.end - 1) / FormattedPassphraseGroupSize};
|
||||||
|
}
|
||||||
|
|
||||||
|
QString unformatted(QString text) const {
|
||||||
|
for (int i = FormattedPassphraseGroupSize; i < text.size();
|
||||||
|
i += FormattedPassphraseGroupSize) {
|
||||||
|
text.remove(i, 1);
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
Selection unformattedSelection(Selection selection) const {
|
||||||
|
if (selection.empty()) {
|
||||||
|
return selection;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
selection.start - selection.start / (FormattedPassphraseGroupSize + 1),
|
||||||
|
selection.end - selection.end / (FormattedPassphraseGroupSize + 1)};
|
||||||
|
}
|
||||||
|
|
||||||
|
void copyToClipboard() {
|
||||||
|
if (q->echoMode() != QLineEdit::Normal) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString text = q->selectedText();
|
||||||
|
if (mFormattedPassphrase) {
|
||||||
|
text.remove(FormattedPassphraseSeparator);
|
||||||
|
}
|
||||||
|
if (!text.isEmpty()) {
|
||||||
|
QGuiApplication::clipboard()->setText(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int selectionEnd() {
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||||
|
return q->selectionEnd();
|
||||||
|
#else
|
||||||
|
return q->selectionStart() + q->selectedText().size();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool mFormattedPassphrase = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
PinLineEdit::PinLineEdit(QWidget *parent)
|
||||||
|
: QLineEdit(parent), d{new Private{this}} {
|
||||||
|
connect(this, SIGNAL(textEdited(QString)), this, SLOT(textEdited()));
|
||||||
|
}
|
||||||
|
|
||||||
|
PinLineEdit::~PinLineEdit() = default;
|
||||||
|
|
||||||
|
void PinLineEdit::setFormattedPassphrase(bool on) {
|
||||||
|
if (on == d->mFormattedPassphrase) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
d->mFormattedPassphrase = on;
|
||||||
|
Selection selection{selectionStart(), d->selectionEnd()};
|
||||||
|
if (d->mFormattedPassphrase) {
|
||||||
|
setText(d->formatted(text()));
|
||||||
|
selection = d->formattedSelection(selection);
|
||||||
|
} else {
|
||||||
|
setText(d->unformatted(text()));
|
||||||
|
selection = d->unformattedSelection(selection);
|
||||||
|
}
|
||||||
|
if (!selection.empty()) {
|
||||||
|
setSelection(selection.start, selection.length());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PinLineEdit::copy() const { d->copyToClipboard(); }
|
||||||
|
|
||||||
|
void PinLineEdit::cut() {
|
||||||
|
if (hasSelectedText()) {
|
||||||
|
copy();
|
||||||
|
del();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PinLineEdit::setPin(const QString &pin) {
|
||||||
|
setText(d->mFormattedPassphrase ? d->formatted(pin) : pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PinLineEdit::pin() const {
|
||||||
|
if (d->mFormattedPassphrase) {
|
||||||
|
return d->unformatted(text());
|
||||||
|
} else {
|
||||||
|
return text();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PinLineEdit::keyPressEvent(QKeyEvent *e) {
|
||||||
|
if (e == QKeySequence::Copy) {
|
||||||
|
copy();
|
||||||
|
return;
|
||||||
|
} else if (e == QKeySequence::Cut) {
|
||||||
|
if (!isReadOnly() && hasSelectedText()) {
|
||||||
|
copy();
|
||||||
|
del();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else if (e == QKeySequence::DeleteEndOfLine) {
|
||||||
|
if (!isReadOnly()) {
|
||||||
|
setSelection(cursorPosition(), text().size());
|
||||||
|
copy();
|
||||||
|
del();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else if (e == QKeySequence::DeleteCompleteLine) {
|
||||||
|
if (!isReadOnly()) {
|
||||||
|
setSelection(0, text().size());
|
||||||
|
copy();
|
||||||
|
del();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QLineEdit::keyPressEvent(e);
|
||||||
|
|
||||||
|
if (e->key() == Qt::Key::Key_Backspace) {
|
||||||
|
emit backspacePressed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PinLineEdit::textEdited() {
|
||||||
|
if (!d->mFormattedPassphrase) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto currentText = text();
|
||||||
|
// first calculate the cursor position in the reformatted text; the cursor
|
||||||
|
// is put left of the separators, so that backspace works as expected
|
||||||
|
auto cursorPos = cursorPosition();
|
||||||
|
cursorPos -= QStringView{currentText}.left(cursorPos).count(
|
||||||
|
FormattedPassphraseSeparator);
|
||||||
|
cursorPos += std::max(cursorPos - 1, 0) / FormattedPassphraseGroupSize;
|
||||||
|
// then reformat the text
|
||||||
|
currentText.remove(FormattedPassphraseSeparator);
|
||||||
|
currentText = d->formatted(currentText);
|
||||||
|
// finally, set reformatted text and updated cursor position
|
||||||
|
setText(currentText);
|
||||||
|
setCursorPosition(cursorPos);
|
||||||
|
}
|
60
src/m_pinentry/pinlineedit.h
Normal file
60
src/m_pinentry/pinlineedit.h
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/* pinlineedit.h - Modified QLineEdit widget.
|
||||||
|
* Copyright (C) 2018 Damien Goutte-Gattat
|
||||||
|
* Copyright (C) 2021 g10 Code GmbH
|
||||||
|
*
|
||||||
|
* Software engineering by Ingo Klöcker <dev@ingo-kloecker.de>
|
||||||
|
*
|
||||||
|
* This program 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 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 this program; if not, see <https://www.gnu.org/licenses/>.
|
||||||
|
* SPDX-License-Identifier: GPL-2.0+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _PINLINEEDIT_H_
|
||||||
|
#define _PINLINEEDIT_H_
|
||||||
|
|
||||||
|
#include <QLineEdit>
|
||||||
|
|
||||||
|
class PinLineEdit : public QLineEdit {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit PinLineEdit(QWidget *parent = nullptr);
|
||||||
|
~PinLineEdit() override;
|
||||||
|
|
||||||
|
void setPin(const QString &pin);
|
||||||
|
QString pin() const;
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
void setFormattedPassphrase(bool on);
|
||||||
|
void copy() const;
|
||||||
|
void cut();
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void backspacePressed();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void keyPressEvent(QKeyEvent *) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
using QLineEdit::setText;
|
||||||
|
using QLineEdit::text;
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void textEdited();
|
||||||
|
|
||||||
|
private:
|
||||||
|
class Private;
|
||||||
|
std::unique_ptr<Private> d;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _PINLINEEDIT_H_
|
93
src/m_pinentry/qti18n.cpp
Normal file
93
src/m_pinentry/qti18n.cpp
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
/* qti18n.cpp - Load qt translations for pinentry.
|
||||||
|
* Copyright 2021 g10 Code GmbH
|
||||||
|
* SPDX-FileCopyrightText: 2015 Lukáš Tinkl <ltinkl@redhat.com>
|
||||||
|
* SPDX-FileCopyrightText: 2021 Ingo Klöcker <kloecker@kde.org>
|
||||||
|
*
|
||||||
|
* Copied from k18n under the terms of LGPLv2 or later.
|
||||||
|
*
|
||||||
|
* This program 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 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 this program; if not, see <https://www.gnu.org/licenses/>.
|
||||||
|
* SPDX-License-Identifier: GPL-2.0+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QLibraryInfo>
|
||||||
|
#include <QLocale>
|
||||||
|
#include <QTranslator>
|
||||||
|
|
||||||
|
static bool loadCatalog(const QString &catalog, const QLocale &locale) {
|
||||||
|
auto translator = new QTranslator(QCoreApplication::instance());
|
||||||
|
|
||||||
|
if (!translator->load(locale, catalog, QString(),
|
||||||
|
QLatin1String(":/i18n_qt"))) {
|
||||||
|
qDebug() << "Loading the" << catalog << "catalog failed for locale"
|
||||||
|
<< locale;
|
||||||
|
delete translator;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
QCoreApplication::instance()->installTranslator(translator);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool loadCatalog(const QString &catalog, const QLocale &locale,
|
||||||
|
const QLocale &fallbackLocale) {
|
||||||
|
// try to load the catalog for locale
|
||||||
|
if (loadCatalog(catalog, locale)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// if this fails, then try the fallback locale (if it's different from locale)
|
||||||
|
if (fallbackLocale != locale) {
|
||||||
|
return loadCatalog(catalog, fallbackLocale);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// load global Qt translation, needed in KDE e.g. by lots of builtin dialogs
|
||||||
|
// (QColorDialog, QFontDialog) that we use
|
||||||
|
static void loadTranslation(const QString &localeName,
|
||||||
|
const QString &fallbackLocaleName) {
|
||||||
|
const QLocale locale{localeName};
|
||||||
|
const QLocale fallbackLocale{fallbackLocaleName};
|
||||||
|
// first, try to load the qt_ meta catalog
|
||||||
|
if (loadCatalog(QStringLiteral("qt_"), locale, fallbackLocale)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// if loading the meta catalog failed, then try loading the four catalogs
|
||||||
|
// it depends on, i.e. qtbase, qtscript, qtmultimedia, qtxmlpatterns,
|
||||||
|
// separately
|
||||||
|
const auto catalogs = {
|
||||||
|
QStringLiteral("qtbase_"),
|
||||||
|
/* QStringLiteral("qtscript_"),
|
||||||
|
QStringLiteral("qtmultimedia_"),
|
||||||
|
QStringLiteral("qtxmlpatterns_"), */
|
||||||
|
};
|
||||||
|
for (const auto &catalog : catalogs) {
|
||||||
|
loadCatalog(catalog, locale, fallbackLocale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void load() {
|
||||||
|
// The way Qt translation system handles plural forms makes it necessary to
|
||||||
|
// have a translation file which contains only plural forms for `en`. That's
|
||||||
|
// why we load the `en` translation unconditionally, then load the
|
||||||
|
// translation for the current locale to overload it.
|
||||||
|
loadCatalog(QStringLiteral("qt_"), QLocale{QStringLiteral("en")});
|
||||||
|
|
||||||
|
const QLocale locale = QLocale::system();
|
||||||
|
if (locale.name() != QStringLiteral("en")) {
|
||||||
|
loadTranslation(locale.name(), locale.bcp47Name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_COREAPP_STARTUP_FUNCTION(load)
|
91
src/m_pinentry/secmem++.h
Normal file
91
src/m_pinentry/secmem++.h
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
/* STL allocator for secmem
|
||||||
|
* Copyright (C) 2008 Marc Mutz <marc@kdab.com>
|
||||||
|
*
|
||||||
|
* This program 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 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 this program; if not, see <https://www.gnu.org/licenses/>.
|
||||||
|
* SPDX-License-Identifier: GPL-2.0+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SECMEM_SECMEMPP_H__
|
||||||
|
#define __SECMEM_SECMEMPP_H__
|
||||||
|
|
||||||
|
#include "../secmem/secmem.h"
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
namespace secmem {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class alloc {
|
||||||
|
public:
|
||||||
|
// type definitions:
|
||||||
|
typedef size_t size_type;
|
||||||
|
typedef ptrdiff_t difference_type;
|
||||||
|
typedef T* pointer;
|
||||||
|
typedef const T* const_pointer;
|
||||||
|
typedef T& reference;
|
||||||
|
typedef const T& const_reference;
|
||||||
|
typedef T value_type;
|
||||||
|
|
||||||
|
// rebind
|
||||||
|
template <typename U>
|
||||||
|
struct rebind {
|
||||||
|
typedef alloc<U> other;
|
||||||
|
};
|
||||||
|
|
||||||
|
// address
|
||||||
|
pointer address( reference value ) const {
|
||||||
|
return &value;
|
||||||
|
}
|
||||||
|
const_pointer address( const_reference value ) const {
|
||||||
|
return &value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// (trivial) ctors and dtors
|
||||||
|
alloc() {}
|
||||||
|
alloc( const alloc & ) {}
|
||||||
|
template <typename U> alloc( const alloc<U> & ) {}
|
||||||
|
// copy ctor is ok
|
||||||
|
~alloc() {}
|
||||||
|
|
||||||
|
// de/allocation
|
||||||
|
size_type max_size() const {
|
||||||
|
return secmem_get_max_size();
|
||||||
|
}
|
||||||
|
|
||||||
|
pointer allocate( size_type n, void * =0 ) {
|
||||||
|
return static_cast<pointer>( secmem_malloc( n * sizeof(T) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void deallocate( pointer p, size_type ) {
|
||||||
|
secmem_free( p );
|
||||||
|
}
|
||||||
|
|
||||||
|
// de/construct
|
||||||
|
void construct( pointer p, const T & value ) {
|
||||||
|
void * loc = p;
|
||||||
|
new (loc)T(value);
|
||||||
|
}
|
||||||
|
void destruct( pointer p ) {
|
||||||
|
p->~T();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// equality comparison
|
||||||
|
template <typename T1,typename T2>
|
||||||
|
bool operator==( const alloc<T1> &, const alloc<T2> & ) { return true; }
|
||||||
|
template <typename T1, typename T2>
|
||||||
|
bool operator!=( const alloc<T1> &, const alloc<T2> & ) { return false; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* __SECMEM_SECMEMPP_H__ */
|
@ -100,12 +100,9 @@ auto GFExecuteModule(GFModuleEvent* event) -> int {
|
|||||||
auto* task = new VersionCheckTask();
|
auto* task = new VersionCheckTask();
|
||||||
QObject::connect(task, &VersionCheckTask::SignalUpgradeVersion,
|
QObject::connect(task, &VersionCheckTask::SignalUpgradeVersion,
|
||||||
QThread::currentThread(), [event](const SoftwareVersion&) {
|
QThread::currentThread(), [event](const SoftwareVersion&) {
|
||||||
char** event_argv = static_cast<char**>(
|
GFModuleTriggerModuleEventCallback(
|
||||||
GFAllocateMemory(sizeof(char**) * 1));
|
event, GFGetModuleID(), 1,
|
||||||
event_argv[0] = DUP("0");
|
ConvertMapToParams({{"ret", "0"}}));
|
||||||
|
|
||||||
GFModuleTriggerModuleEventCallback(event, GFGetModuleID(),
|
|
||||||
1, event_argv);
|
|
||||||
});
|
});
|
||||||
QObject::connect(task, &VersionCheckTask::SignalUpgradeVersion, task,
|
QObject::connect(task, &VersionCheckTask::SignalUpgradeVersion, task,
|
||||||
&QObject::deleteLater);
|
&QObject::deleteLater);
|
||||||
|
Loading…
Reference in New Issue
Block a user