1
0
mirror of https://github.com/QRouland/UTPass.git synced 2025-06-26 07:22:28 +00:00

Initial Commit : 0.0.1

This commit is contained in:
2019-09-20 21:29:39 +02:00
commit 2a8c72f8d0
65 changed files with 3486 additions and 0 deletions

View File

@ -0,0 +1,54 @@
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(PLUGIN "Pass")
set(
SRC
plugin.cpp
pass.cpp
gpg.cpp
passkeymodel.h
passphraseprovider.h
)
set(CMAKE_AUTOMOC ON)
execute_process(
COMMAND dpkg-architecture -qDEB_HOST_MULTIARCH
OUTPUT_VARIABLE ARCH_TRIPLET
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(ARCH_TRIPLET STREQUAL "")
set(ARCH_TRIPLET x86_64-linux-gnu)
endif()
add_library(${PLUGIN} MODULE ${SRC})
set_target_properties(${PLUGIN} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PLUGIN})
qt5_use_modules(${PLUGIN} Qml Quick DBus)
set(EXTERNAL_LIBS "${CMAKE_SOURCE_DIR}/build/${ARCH_TRIPLET}/gpg/local/")
INCLUDE_DIRECTORIES(${EXTERNAL_LIBS}/include)
add_library(GpgError STATIC IMPORTED)
set_property(TARGET GpgError PROPERTY IMPORTED_LOCATION "${EXTERNAL_LIBS}/lib/libgpg-error.a")
add_library(GpgAssuan STATIC IMPORTED)
set_property(TARGET GpgAssuan PROPERTY IMPORTED_LOCATION "${EXTERNAL_LIBS}/lib/libassuan.a")
add_library(Gpgme STATIC IMPORTED)
set_property(TARGET Gpgme PROPERTY IMPORTED_LOCATION "${EXTERNAL_LIBS}/lib/libgpgme.a")
add_library(Gpgmepp STATIC IMPORTED)
set_property(TARGET Gpgmepp PROPERTY IMPORTED_LOCATION "${EXTERNAL_LIBS}/lib/libgpgmepp.a")
add_library(QGpgme STATIC IMPORTED)
set_property(TARGET QGpgme PROPERTY IMPORTED_LOCATION "${EXTERNAL_LIBS}/lib/libqgpgme.a")
target_link_libraries(${PLUGIN} QGpgme Gpgmepp Gpgme GpgAssuan GpgError)
set(QT_IMPORTS_DIR "/lib/${ARCH_TRIPLET}")
install(TARGETS ${PLUGIN} DESTINATION ${QT_IMPORTS_DIR}/${PLUGIN}/)
install(FILES qmldir DESTINATION ${QT_IMPORTS_DIR}/${PLUGIN}/)

270
plugins/Pass/gpg.cpp Normal file
View File

@ -0,0 +1,270 @@
#include <memory>
#include <QDebug>
#include <QFile>
#include <QDir>
#include <QtCore/QStandardPaths>
#include <gpgme.h>
#include <gpgme++/data.h>
#include <gpgme++/global.h>
#include <gpgme++/context.h>
#include <gpgme++/engineinfo.h>
#include <gpgme++/keylistresult.h>
#include <gpgme++/importresult.h>
#include <gpgme++/encryptionresult.h>
#include <gpgme++/decryptionresult.h>
#include <qgpgme/importjob.h>
#include <qgpgme/deletejob.h>
#include <qgpgme/decryptjob.h>
#include <qgpgme/encryptjob.h>
#include <qgpgme/protocol.h>
#include <qgpgme/keylistjob.h>
#include <qgpgme/changeownertrustjob.h>
#include "gpg.h"
#include "pass.h"
#include "passphraseprovider.h"
using namespace GpgME;
using namespace QGpgME;
Gpg::Gpg()
{
m_window = nullptr;
initializeLibrary();
Gpg::initGpgConfig();
auto error = checkEngine(OpenPGP);
if (error) {
qDebug() << "Code Error : " << error.code();
qDebug() << "Error str : " << error.asString();
qFatal("GNUPG Engine check Fail");
}
qDebug() << "GNUPG Engine Version is :" << engineInfo(OpenPGP).version();
qDebug() << "GNUPG Executable is :" << engineInfo(OpenPGP).fileName();
qDebug() << "GNUPG Home is :" << engineInfo(OpenPGP).homeDirectory();
}
QString Gpg::initGpgHome()
{
QString path = QStandardPaths::writableLocation(
QStandardPaths::AppDataLocation).append("/.gpghome");
QDir dir(path);
if (!dir.exists()) {
dir.mkpath(".");
}
return path;
}
QString Gpg::initGpgExec()
{
QString path = QDir::currentPath().append("/lib/bin/gpg");
QFileInfo file(path);
if (!file.isFile()) {
qFatal("GNUPGEXEC file not found !");
}
if (!file.isExecutable()) {
qFatal("GNUPGEXEC file not executable !");
}
return path;
}
void Gpg::initGpgConfig()
{
auto home = initGpgHome();
auto exec = initGpgExec();
QFile agentConf(home + QStringLiteral("/gpg-agent.conf"));
agentConf.remove();
agentConf.open(QIODevice::WriteOnly);
agentConf.write("allow-loopback-pinentry");
agentConf.close();
gpgme_set_engine_info (
GPGME_PROTOCOL_OpenPGP,
exec.toLocal8Bit().data(),
home.toLocal8Bit().data()
);
}
QPair<Error, QString> Gpg::decrypt(QByteArray cipherText)
{
auto job = openpgp()->decryptJob();
auto ctx = DecryptJob::context(job);
auto provider = new UTPassphraseProvider;
ctx->setPassphraseProvider(provider);
ctx->setPinentryMode(Context::PinentryLoopback);
QByteArray plain_text;
auto decResult = job->exec(cipherText, plain_text);
delete job;
if (decResult.error()) {
qWarning() << "something gone wrong on decrypt";
qDebug() << "Code Error : " << decResult.error().code();
qDebug() << "Error str : " << decResult.error().asString();
}
return QPair<Error, QString>(decResult.error(), QString::fromUtf8(plain_text));
}
QPair<Error, QString> Gpg::decryptFromFile(QString path)
{
qDebug() << "Decrypt from " << path;
QFile file(path);
if (!file.open(QIODevice::ReadOnly)) {
qWarning() << "Can't open the File";
return QPair<Error, QString>(Error(), QString());;
}
QByteArray cipherText = file.readAll();
file.close();
return decrypt(cipherText);
}
QPair<Error, QByteArray> Gpg::encrypt(QString str, QString uid, bool ascii_armor, bool text_mode)
{
qDebug() << "Encrypt to QByteArray";
auto keys = getKeys(uid);
if (keys.first) {
return QPair<Error, QByteArray>(keys.first, QByteArray());
}
auto job = std::unique_ptr<EncryptJob>(openpgp()->encryptJob(ascii_armor, text_mode));
QByteArray cipherText;
auto result = job->exec(keys.second, str.toUtf8(), Context::AlwaysTrust, cipherText);
qDebug() << "Encrypted to QByteArray";
return QPair<Error, QByteArray>(result.error(), cipherText);
}
Error Gpg::encryptToFile(QString str, QString path, QString uid, bool ascii_armor,
bool text_mode)
{
qDebug() << "Encrypting to file " << path;
QFile file(path);
if (!file.open(QIODevice::WriteOnly)) {
qWarning() << "Can't open the file to write it" ;
return Error();
}
auto encrypt_ret = encrypt(str, uid, ascii_armor, text_mode);
if (encrypt_ret.first) {
file.write(encrypt_ret.second);
}
qDebug() << "Encrypting to file " << path;
return encrypt_ret.first;
}
QPair<Error, std::vector< GpgME::Key >> Gpg::getAllKeys ( bool remote, const bool include_sigs,
bool validate )
{
return getKeys(QString(""), remote, include_sigs, validate);
}
QPair<Error, std::vector<Key>> Gpg::getKeys(QString pattern_uid, bool remote, bool include_sigs,
bool validate)
{
qDebug() << "Getting the keys " << pattern_uid;
auto job = std::unique_ptr<KeyListJob>(openpgp()->keyListJob(remote, include_sigs, validate));
std::vector<Key> keys;
auto result = job->exec(QStringList() << pattern_uid, false, keys);
qDebug() << "Got the keys " << pattern_uid;
return QPair<Error, std::vector< Key >>(result.error(), keys);
}
QPair<Error, Key> Gpg::getKey(QString uid, bool remote, bool include_sigs, bool validate)
{
qDebug() << "Getting the key " << uid;
auto keys = getKeys(uid, remote, include_sigs, validate);
if (keys.first or keys.second.size() != 1) {
qWarning() << "Bad id";
return QPair<Error, Key>(keys.first, Key::null);
}
qDebug() << "Got the key " << uid;
return QPair<Error, Key>(keys.first, keys.second.front());
}
Error Gpg::importKeysFromFile(QString path)
{
qDebug() << "Importing the key file" << path;
qDebug() << "Decrypt from " << path;
QFile file(path);
if (!file.open(QIODevice::ReadOnly)) {
qWarning() << "Can't open the File";
return Error();
}
auto job = openpgp()->importJob();
auto ctx = ImportJob::context(job);
auto provider = new UTPassphraseProvider;
ctx->setPassphraseProvider(provider);
ctx->setPinentryMode(Context::PinentryLoopback);
auto result = job->exec(file.readAll());
qDebug() << "numImported" << result.numImported();
qDebug() << "numSecretKeysImported" << result.numSecretKeysImported();
qDebug() << "numSecretKeysConsidered" << result.numSecretKeysConsidered();
qDebug() << "numSecretKeysUnchanged" << result.numSecretKeysUnchanged();
qDebug() << "numUnchanged" << result.numUnchanged();
file.close();
delete job;
delete provider;
if (result.error()) {
qWarning() << "Import go wrong";
qDebug() << "Code Error : " << result.error().code();
qDebug() << "Error str : " << result.error().asString();
}
qDebug() << "Imported the key file" << path;
return result.error();
}
Error Gpg::deleteKeyId(QString uid)
{
qDebug() << "Deleting key id " << uid;
auto key = getKey(uid);
if (key.first) {
return key.first;
}
auto ctx = std::unique_ptr<Context>(Context::createForProtocol(OpenPGP));
auto err = ctx->deleteKey(key.second, true);
if (err) {
qWarning() << "Delete go wrong";
qDebug() << "Code Error : " << err.code();
qDebug() << "Error str : " << err.asString();
return err;
}
qDebug() << "Deleted key id" << uid;
return err;
}

61
plugins/Pass/gpg.h Normal file
View File

@ -0,0 +1,61 @@
#ifndef GPG_H
#define GPG_H
#include <memory>
#include <QQuickWindow>
#include <gpgme++/context.h>
#include <qgpgme/changeownertrustjob.h>
using namespace GpgME;
class Gpg
{
private:
Gpg();
QObject *m_window;
QString initGpgHome();
QString initGpgExec();
void initGpgConfig();
public:
~Gpg();
static std::shared_ptr<Gpg> instance()
{
static std::shared_ptr<Gpg> s{new Gpg};
return s;
}
Gpg(Gpg const &) = delete;
void operator=(Gpg const &) = delete;
void setWindow(QObject *window)
{
m_window = window;
};
QObject *getWindow()
{
return m_window;
};
QPair< Error, std::vector< Key > > getAllKeys(bool remote = false, bool include_sigs = {}, bool
validate = false);
QPair<Error, std::vector<Key>> getKeys( QString pattern_uid, bool remote = false,
bool include_sigs = false,
bool validate = false);
QPair<Error, Key> getKey( QString uid, bool remote = false, bool include_sigs = false,
bool validate = false);
QPair<Error, QString> decrypt( QByteArray cipherText);
QPair<Error, QString> decryptFromFile( QString path);
QPair<Error, QByteArray> encrypt( QString str, QString uid, bool ascii_armor = true,
bool text_mode = true);
Error encryptToFile( QString str, QString path, QString uid, bool ascii_armor = true,
bool text_mode = true);
Error importKeysFromFile( QString path);
Error deleteKeyId( QString uid);
};
#endif

66
plugins/Pass/pass.cpp Normal file
View File

@ -0,0 +1,66 @@
#include <QUrl>
#include <QtCore/QStandardPaths>
#include <QtCore/QDir>
#include "pass.h"
#include "gpg.h"
#include "passphraseprovider.h"
#include "passkeymodel.h"
Pass::Pass(): m_password_store (QStandardPaths::writableLocation(
QStandardPaths::AppDataLocation).append("/.password-store"))
{}
void Pass::init(QObject *window)
{
if (!window) {
qFatal("window is invalid. Abording.");
}
Gpg::instance()->setWindow(window);
QDir dir(m_password_store);
if (!dir.exists())
dir.mkpath(".");
qDebug() << "Password Store is :" << m_password_store;
}
void Pass::decrypt(QUrl url)
{
qDebug() << "Start decrypting";
auto decrypt_ret = Gpg::instance()->decryptFromFile(url.toLocalFile());
if (decrypt_ret.first) {
qDebug() << "Decrypt Failed";
emit decryptFailed();
} else if (decrypt_ret.second.isNull()) {
qDebug() << "Decrypt Canceled";
emit decryptCanceled();
} else {
qDebug() << "Decrypt OK";
emit decrypted(decrypt_ret.second);
}
}
bool Pass::gpgDeleteKeyId(QString id)
{
qDebug() << "Start deleting Key id " << id;
return !Gpg::instance()->deleteKeyId(id);
}
bool Pass::gpgImportKeyFromFile(QUrl url)
{
qDebug() << "Start importing Key from " << url;
return !Gpg::instance()->importKeysFromFile(url.toLocalFile());
}
QVariant Pass::gpgGetAllKeysModel()
{
return QVariant::fromValue(PassKeyModel::keysToPassKeyQObjectList(
Gpg::instance()->getAllKeys().second));
}
QString Pass::getPasswordStore()
{
return m_password_store;
}

32
plugins/Pass/pass.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef PASS_H
#define PASS_H
#include <QObject>
#include <QUrl>
#include <QVariant>
class Pass : public QObject
{
Q_OBJECT
QString m_password_store;
signals:
void decrypted(QString text);
void decryptCanceled();
void decryptFailed();
public:
Pass();
~Pass() override = default;
Q_INVOKABLE void init(QObject *window);
Q_INVOKABLE QString getPasswordStore();
Q_INVOKABLE void decrypt(QUrl url);
Q_INVOKABLE bool gpgDeleteKeyId(QString id);
Q_INVOKABLE bool gpgImportKeyFromFile(QUrl url);
Q_INVOKABLE QVariant gpgGetAllKeysModel();
};
#endif

View File

@ -0,0 +1,74 @@
#ifndef PASSKEYMODEL_H
#define PASSKEYMODEL_H
#include <QObject>
#include <gpgme++/key.h>
using namespace GpgME;
class PassKeyModel : public QObject
{
Q_OBJECT
Q_PROPERTY(QString uid READ uid WRITE setUid NOTIFY uidChanged MEMBER m_uid)
Q_PROPERTY(bool secret READ secret WRITE setSecret NOTIFY secretChanged MEMBER m_secret)
Q_PROPERTY(bool expired READ expired WRITE setExpired NOTIFY expiredChanged MEMBER m_expired)
QString m_uid;
bool m_secret;
bool m_expired;
public:
PassKeyModel(QString uid, bool secret, bool expired):
m_uid(uid),
m_secret(secret),
m_expired(expired)
{};
PassKeyModel(Key key):
PassKeyModel(QString::fromUtf8(key.keyID()), key.hasSecret(), key.isExpired())
{};
static QList<QObject *> keysToPassKeyQObjectList(std::vector<Key> keys)
{
QList<QObject *> r;
std::for_each(keys.begin(), keys.end(), [&r](Key k) {
r.append(new PassKeyModel(k));
});
return r;
};
QString uid() const
{
return m_uid;
};
bool secret() const
{
return m_secret;
};
bool expired() const
{
return m_expired;
};
void setUid(QString uid)
{
m_uid = uid;
emit uidChanged(uid);
}
void setSecret(bool secret)
{
m_secret = secret;
emit secretChanged(secret);
}
void setExpired(bool expired)
{
m_expired = expired;
emit expiredChanged(expired);
}
signals:
void uidChanged(QString);
void secretChanged(bool);
void expiredChanged(bool);
};
#endif

View File

@ -0,0 +1,88 @@
#ifndef UTPASSPHRASEPROVIDER_H
#define UTPASSPHRASEPROVIDER_H
#include <stdio.h>
#include <QObject>
#include <QQmlProperty>
#include <QEventLoop>
#include <QSemaphore>
#include <gpgme++/interfaces/passphraseprovider.h>
#include "passphraseprovider.h"
#include "gpg.h"
class UTPassphraseProvider : public QObject, public PassphraseProvider
{
Q_OBJECT
private:
std::unique_ptr<QEventLoop> m_loop;
std::unique_ptr<QSemaphore> m_sem;
char *m_passphrase;
bool m_canceled;
public slots:
void handleResponse(bool canceled, QString p)
{
if (!canceled)
gpgrt_asprintf(&m_passphrase, "%s", p.toUtf8().constData());
else
m_canceled = true;
m_loop->quit();
};
public:
UTPassphraseProvider():
m_loop(std::unique_ptr<QEventLoop>(new QEventLoop)),
m_sem(std::unique_ptr<QSemaphore>(new QSemaphore(1))),
m_passphrase(nullptr),
m_canceled(false)
{}
char *getPassphrase( const char *useridHint,
const char *description,
bool previousWasBad,
bool &canceled ) Q_DECL_OVERRIDE {
if (!m_sem->tryAcquire(1, 3000))
{
qWarning() << "Cannot acquire UTPassphraseProvider semaphore.";
canceled = true;
return nullptr;
}
m_passphrase = nullptr;
m_canceled = false;
qDebug() << "Call the QML Dialog Passphrase Provider";
QMetaObject::invokeMethod(
Gpg::instance()->getWindow(), "callPassphraseDialog",
Q_ARG(QVariant, useridHint),
Q_ARG(QVariant, description),
Q_ARG(QVariant, previousWasBad)
);
qDebug() << "Waiting for response";
QObject::connect(
Gpg::instance()->getWindow(), SIGNAL(responsePassphraseDialog(bool, QString)),
this, SLOT(handleResponse(bool, QString))
);
m_loop->exec();
qDebug() << "Prepare Returns";
char *ret;
gpgrt_asprintf(&ret, "%s", m_passphrase);
canceled = m_canceled;
qDebug() << "Clean";
if (m_passphrase)
{
free(m_passphrase);
}
m_canceled = false;
m_sem->release(1);
return ret;
};
};
#endif

10
plugins/Pass/plugin.cpp Normal file
View File

@ -0,0 +1,10 @@
#include <QtQml>
#include "plugin.h"
#include "pass.h"
void PassPlugin::registerTypes(const char *uri)
{
//@uri Pass
qmlRegisterSingletonType<Pass>(uri, 1, 0, "Pass", [](QQmlEngine *, QJSEngine *) -> QObject * { return new Pass; });
}

16
plugins/Pass/plugin.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef PASSPLUGIN_H
#define PASSPLUGIN_H
#include <QQmlExtensionPlugin>
class PassPlugin : public QQmlExtensionPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID
"org.qt-project.Qt.QQmlExtensionInterface")
public:
void registerTypes(const char *uri) override;
};
#endif

2
plugins/Pass/qmldir Normal file
View File

@ -0,0 +1,2 @@
module Pass
plugin Pass