qt: Add job to add existing subkeys to other keys
* lang/qt/src/addexistingsubkeyjob.h, lang/qt/src/qgpgmeaddexistingsubkeyjob.cpp, lang/qt/src/qgpgmeaddexistingsubkeyjob.h: New. * lang/qt/src/protocol.h (class Protocol): Add pure virtual member function addExistingSubkeyJob. * lang/qt/src/protocol_p.h (Protocol::addExistingSubkeyJob): Implement. * lang/qt/src/job.cpp, lang/qt/src/Makefile.am: Update accordingly. * lang/qt/tests/Makefile.am (the_tests, moc_files, noinst_PROGRAMS): Add new test. (t_addexistingsubkey_SOURCES): New. * lang/qt/tests/t-addexistingsubkey.cpp: New. * lang/qt/tests/t-support.h (VERIFY_OR_RETURN_VALUE, COMPARE_OR_RETURN_VALUE, VERIFY_OR_OBJECT, COMPARE_OR_OBJECT, VERIFY_OR_FALSE, COMPARE_OR_FALSE): New. * lang/qt/tests/t-support.h, lang/qt/tests/t-support.cpp (class QQGpgMETest): New member function importSecretKeys. -- The new job allows adding existing subkeys to other keys as with the "addkey" edit-key command of gpg. The added subkey will have the same expiration date (+/- 1 second) as the original subkey. GnuPG-bug-id: 5770
This commit is contained in:
parent
d308910cdf
commit
4d913a8aa5
4
NEWS
4
NEWS
@ -12,7 +12,7 @@ Noteworthy changes in version 1.16.1 (unreleased)
|
||||
* cpp,qt: Add support for export of secret keys and secret subkeys.
|
||||
[#5757]
|
||||
|
||||
* cpp: Support for adding existing subkeys to other keys. [#5770]
|
||||
* cpp,qt: Support for adding existing subkeys to other keys. [#5770]
|
||||
|
||||
* qt: Extend ChangeExpiryJob to change expiration of primary key
|
||||
and of subkeys at the same time. [#4717]
|
||||
@ -35,6 +35,8 @@ Noteworthy changes in version 1.16.1 (unreleased)
|
||||
qt: ChangeExpiryJob::Options NEW.
|
||||
qt: ChangeExpiryJob::setOptions NEW.
|
||||
qt: ChangeExpiryJob::options NEW.
|
||||
qt: AddExistingSubkeyJob NEW.
|
||||
qt: Protocol::addExistingSubkeyJob NEW.
|
||||
|
||||
Noteworthy changes in version 1.16.0 (2021-06-24)
|
||||
-------------------------------------------------
|
||||
|
@ -27,6 +27,7 @@ qgpgme_sources = \
|
||||
dataprovider.cpp \
|
||||
debug.cpp \
|
||||
job.cpp multideletejob.cpp qgpgmeadduseridjob.cpp \
|
||||
qgpgmeaddexistingsubkeyjob.cpp \
|
||||
qgpgmebackend.cpp qgpgmechangeexpiryjob.cpp qgpgmechangeownertrustjob.cpp \
|
||||
qgpgmechangepasswdjob.cpp qgpgmedecryptjob.cpp \
|
||||
qgpgmedecryptverifyjob.cpp qgpgmedeletejob.cpp qgpgmedownloadjob.cpp \
|
||||
@ -46,6 +47,7 @@ qgpgme_sources = \
|
||||
# If you add one here make sure that you also add one in camelcase
|
||||
qgpgme_headers= \
|
||||
abstractimportjob.h \
|
||||
addexistingsubkeyjob.h \
|
||||
adduseridjob.h \
|
||||
changeexpiryjob.h \
|
||||
changeownertrustjob.h \
|
||||
@ -88,6 +90,7 @@ qgpgme_headers= \
|
||||
dn.h
|
||||
|
||||
camelcase_headers= \
|
||||
AddExistingSubkeyJob \
|
||||
AddUserIDJob \
|
||||
AbstractImportJob \
|
||||
ChangeExpiryJob \
|
||||
@ -133,6 +136,7 @@ private_qgpgme_headers = \
|
||||
qgpgme_export.h \
|
||||
protocol_p.h \
|
||||
job_p.h \
|
||||
qgpgmeaddexistingsubkeyjob.h \
|
||||
qgpgmeadduseridjob.h \
|
||||
qgpgmebackend.h \
|
||||
qgpgmechangeexpiryjob.h \
|
||||
@ -165,6 +169,7 @@ private_qgpgme_headers = \
|
||||
|
||||
qgpgme_moc_sources = \
|
||||
abstractimportjob.moc \
|
||||
addexistingsubkeyjob.moc \
|
||||
adduseridjob.moc \
|
||||
changeexpiryjob.moc \
|
||||
changeownertrustjob.moc \
|
||||
@ -183,6 +188,7 @@ qgpgme_moc_sources = \
|
||||
keylistjob.moc \
|
||||
listallkeysjob.moc \
|
||||
multideletejob.moc \
|
||||
qgpgmeaddexistingsubkeyjob.moc \
|
||||
qgpgmeadduseridjob.moc \
|
||||
qgpgmechangeexpiryjob.moc \
|
||||
qgpgmechangeownertrustjob.moc \
|
||||
|
79
lang/qt/src/addexistingsubkeyjob.h
Normal file
79
lang/qt/src/addexistingsubkeyjob.h
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
addexistingsubkeyjob.h
|
||||
|
||||
This file is part of qgpgme, the Qt API binding for gpgme
|
||||
Copyright (c) 2022 g10 Code GmbH
|
||||
Software engineering by Ingo Klöcker <dev@ingo-kloecker.de>
|
||||
|
||||
QGpgME 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.
|
||||
|
||||
QGpgME 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
|
||||
|
||||
In addition, as a special exception, the copyright holders give
|
||||
permission to link the code of this program with any edition of
|
||||
the Qt library by Trolltech AS, Norway (or with modified versions
|
||||
of Qt that use the same license as Qt), and distribute linked
|
||||
combinations including the two. You must obey the GNU General
|
||||
Public License in all respects for all of the code used other than
|
||||
Qt. If you modify this file, you may extend this exception to
|
||||
your version of the file, but you are not obligated to do so. If
|
||||
you do not wish to do so, delete this exception statement from
|
||||
your version.
|
||||
*/
|
||||
|
||||
#ifndef __QGPGME_ADDEXISTINGSUBKEYJOB_H__
|
||||
#define __QGPGME_ADDEXISTINGSUBKEYJOB_H__
|
||||
|
||||
#include "job.h"
|
||||
#include "qgpgme_export.h"
|
||||
|
||||
class QString;
|
||||
|
||||
namespace GpgME
|
||||
{
|
||||
class Error;
|
||||
class Key;
|
||||
class Subkey;
|
||||
}
|
||||
|
||||
namespace QGpgME
|
||||
{
|
||||
|
||||
class QGPGME_EXPORT AddExistingSubkeyJob : public Job
|
||||
{
|
||||
Q_OBJECT
|
||||
protected:
|
||||
explicit AddExistingSubkeyJob(QObject *parent);
|
||||
|
||||
public:
|
||||
~AddExistingSubkeyJob();
|
||||
|
||||
/**
|
||||
Starts the operation. \a key is the key to add the subkey \a subkey to.
|
||||
|
||||
The job deletes itself after it has completed the operation.
|
||||
*/
|
||||
virtual GpgME::Error start(const GpgME::Key &key, const GpgME::Subkey &subkey) = 0;
|
||||
|
||||
/**
|
||||
Runs the operation. \a key is the key to add the subkey \a subkey to.
|
||||
*/
|
||||
virtual GpgME::Error exec(const GpgME::Key &key, const GpgME::Subkey &subkey) = 0;
|
||||
|
||||
Q_SIGNALS:
|
||||
void result(const GpgME::Error &result, const QString &auditLogAsHtml = {}, const GpgME::Error &auditLogError = {});
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __QGPGME_ADDEXISTINGSUBKEYJOB_H__
|
@ -61,6 +61,7 @@
|
||||
#include "downloadjob.h"
|
||||
#include "deletejob.h"
|
||||
#include "refreshkeysjob.h"
|
||||
#include "addexistingsubkeyjob.h"
|
||||
#include "adduseridjob.h"
|
||||
#include "specialjob.h"
|
||||
#include "keyformailboxjob.h"
|
||||
@ -160,6 +161,7 @@ make_job_subclass(ChangePasswdJob)
|
||||
make_job_subclass(DownloadJob)
|
||||
make_job_subclass(DeleteJob)
|
||||
make_job_subclass(RefreshKeysJob)
|
||||
make_job_subclass(AddExistingSubkeyJob)
|
||||
make_job_subclass(AddUserIDJob)
|
||||
make_job_subclass(SpecialJob)
|
||||
make_job_subclass(KeyForMailboxJob)
|
||||
@ -194,6 +196,7 @@ make_job_subclass(GpgCardJob)
|
||||
#include "downloadjob.moc"
|
||||
#include "deletejob.moc"
|
||||
#include "refreshkeysjob.moc"
|
||||
#include "addexistingsubkeyjob.moc"
|
||||
#include "adduseridjob.moc"
|
||||
#include "specialjob.moc"
|
||||
#include "keyformailboxjob.moc"
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include "qgpgme_export.h"
|
||||
|
||||
namespace QGpgME {
|
||||
class AddExistingSubkeyJob;
|
||||
class CryptoConfig;
|
||||
class KeyListJob;
|
||||
class ListAllKeysJob;
|
||||
@ -138,6 +139,7 @@ public:
|
||||
virtual SignKeyJob *signKeyJob() const = 0;
|
||||
virtual ChangePasswdJob *changePasswdJob() const = 0;
|
||||
virtual ChangeOwnerTrustJob *changeOwnerTrustJob() const = 0;
|
||||
virtual AddExistingSubkeyJob *addExistingSubkeyJob() const = 0;
|
||||
virtual AddUserIDJob *addUserIDJob() const = 0;
|
||||
virtual SpecialJob *specialJob(const char *type, const QMap<QString, QVariant> &args) const = 0;
|
||||
|
||||
|
@ -57,6 +57,7 @@
|
||||
#include "qgpgmechangeexpiryjob.h"
|
||||
#include "qgpgmechangeownertrustjob.h"
|
||||
#include "qgpgmechangepasswdjob.h"
|
||||
#include "qgpgmeaddexistingsubkeyjob.h"
|
||||
#include "qgpgmeadduseridjob.h"
|
||||
#include "qgpgmekeyformailboxjob.h"
|
||||
#include "qgpgmewkdlookupjob.h"
|
||||
@ -371,6 +372,19 @@ public:
|
||||
return new QGpgME::QGpgMEChangeOwnerTrustJob(context);
|
||||
}
|
||||
|
||||
QGpgME:: AddExistingSubkeyJob *addExistingSubkeyJob() const override
|
||||
{
|
||||
if (mProtocol != GpgME::OpenPGP) {
|
||||
return nullptr; // only supported by gpg
|
||||
}
|
||||
|
||||
GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol);
|
||||
if (!context) {
|
||||
return nullptr;
|
||||
}
|
||||
return new QGpgME::QGpgMEAddExistingSubkeyJob{context};
|
||||
}
|
||||
|
||||
QGpgME::AddUserIDJob *addUserIDJob() const Q_DECL_OVERRIDE
|
||||
{
|
||||
if (mProtocol != GpgME::OpenPGP) {
|
||||
|
96
lang/qt/src/qgpgmeaddexistingsubkeyjob.cpp
Normal file
96
lang/qt/src/qgpgmeaddexistingsubkeyjob.cpp
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
qgpgmeaddexistingsubkeyjob.cpp
|
||||
|
||||
This file is part of qgpgme, the Qt API binding for gpgme
|
||||
Copyright (c) 2022 g10 Code GmbH
|
||||
Software engineering by Ingo Klöcker <dev@ingo-kloecker.de>
|
||||
|
||||
QGpgME 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.
|
||||
|
||||
QGpgME 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
|
||||
|
||||
In addition, as a special exception, the copyright holders give
|
||||
permission to link the code of this program with any edition of
|
||||
the Qt library by Trolltech AS, Norway (or with modified versions
|
||||
of Qt that use the same license as Qt), and distribute linked
|
||||
combinations including the two. You must obey the GNU General
|
||||
Public License in all respects for all of the code used other than
|
||||
Qt. If you modify this file, you may extend this exception to
|
||||
your version of the file, but you are not obligated to do so. If
|
||||
you do not wish to do so, delete this exception statement from
|
||||
your version.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "qgpgmeaddexistingsubkeyjob.h"
|
||||
|
||||
#include "dataprovider.h"
|
||||
|
||||
#include <QDateTime>
|
||||
|
||||
#include "context.h"
|
||||
#include "data.h"
|
||||
#include "gpgaddexistingsubkeyeditinteractor.h"
|
||||
#include "key.h"
|
||||
|
||||
#include <gpg-error.h>
|
||||
|
||||
using namespace QGpgME;
|
||||
using namespace GpgME;
|
||||
|
||||
QGpgMEAddExistingSubkeyJob::QGpgMEAddExistingSubkeyJob(Context *context)
|
||||
: mixin_type{context}
|
||||
{
|
||||
lateInitialization();
|
||||
}
|
||||
|
||||
QGpgMEAddExistingSubkeyJob::~QGpgMEAddExistingSubkeyJob() = default;
|
||||
|
||||
static QGpgMEAddExistingSubkeyJob::result_type add_subkey(Context *ctx, const Key &key, const Subkey &subkey)
|
||||
{
|
||||
std::unique_ptr<GpgAddExistingSubkeyEditInteractor> interactor{new GpgAddExistingSubkeyEditInteractor{subkey.keyGrip()}};
|
||||
|
||||
if (!subkey.neverExpires()) {
|
||||
const auto expiry = QDateTime::fromSecsSinceEpoch(subkey.expirationTime(), Qt::UTC).toString(u"yyyyMMdd'T'hhmmss").toStdString();
|
||||
interactor->setExpiry(expiry);
|
||||
}
|
||||
|
||||
QGpgME::QByteArrayDataProvider dp;
|
||||
Data data(&dp);
|
||||
assert(!data.isNull());
|
||||
|
||||
ctx->setFlag("extended-edit", "1");
|
||||
|
||||
const Error err = ctx->edit(key, std::unique_ptr<EditInteractor>(interactor.release()), data);
|
||||
Error ae;
|
||||
const QString log = _detail::audit_log_as_html(ctx, ae);
|
||||
return std::make_tuple(err, log, ae);
|
||||
}
|
||||
|
||||
Error QGpgMEAddExistingSubkeyJob::start(const GpgME::Key &key, const GpgME::Subkey &subkey)
|
||||
{
|
||||
run(std::bind(&add_subkey, std::placeholders::_1, key, subkey));
|
||||
return {};
|
||||
}
|
||||
|
||||
Error QGpgMEAddExistingSubkeyJob::exec(const GpgME::Key &key, const GpgME::Subkey &subkey)
|
||||
{
|
||||
const result_type r = add_subkey(context(), key, subkey);
|
||||
resultHook(r);
|
||||
return std::get<0>(r);
|
||||
}
|
||||
|
||||
#include "qgpgmeaddexistingsubkeyjob.moc"
|
68
lang/qt/src/qgpgmeaddexistingsubkeyjob.h
Normal file
68
lang/qt/src/qgpgmeaddexistingsubkeyjob.h
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
qgpgmeaddexistingsubkeyjob.h
|
||||
|
||||
This file is part of qgpgme, the Qt API binding for gpgme
|
||||
Copyright (c) 2022 g10 Code GmbH
|
||||
Software engineering by Ingo Klöcker <dev@ingo-kloecker.de>
|
||||
|
||||
QGpgME 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.
|
||||
|
||||
QGpgME 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
|
||||
|
||||
In addition, as a special exception, the copyright holders give
|
||||
permission to link the code of this program with any edition of
|
||||
the Qt library by Trolltech AS, Norway (or with modified versions
|
||||
of Qt that use the same license as Qt), and distribute linked
|
||||
combinations including the two. You must obey the GNU General
|
||||
Public License in all respects for all of the code used other than
|
||||
Qt. If you modify this file, you may extend this exception to
|
||||
your version of the file, but you are not obligated to do so. If
|
||||
you do not wish to do so, delete this exception statement from
|
||||
your version.
|
||||
*/
|
||||
|
||||
#ifndef __QGPGME_QGPGMEADDEXISTINGSUBKEYJOB_H__
|
||||
#define __QGPGME_QGPGMEADDEXISTINGSUBKEYJOB_H__
|
||||
|
||||
#include "threadedjobmixin.h"
|
||||
#include "addexistingsubkeyjob.h"
|
||||
|
||||
namespace QGpgME
|
||||
{
|
||||
|
||||
class QGpgMEAddExistingSubkeyJob
|
||||
#ifdef Q_MOC_RUN
|
||||
: public AddExistingSubkeyJob
|
||||
#else
|
||||
: public _detail::ThreadedJobMixin<AddExistingSubkeyJob>
|
||||
#endif
|
||||
{
|
||||
Q_OBJECT
|
||||
#ifdef Q_MOC_RUN
|
||||
public Q_SLOTS:
|
||||
void slotFinished();
|
||||
#endif
|
||||
public:
|
||||
explicit QGpgMEAddExistingSubkeyJob(GpgME::Context *context);
|
||||
~QGpgMEAddExistingSubkeyJob();
|
||||
|
||||
/* from AddExistingSubkeyJob */
|
||||
GpgME::Error start(const GpgME::Key &key, const GpgME::Subkey &subkey) override;
|
||||
|
||||
/* from AddExistingSubkeyJob */
|
||||
GpgME::Error exec(const GpgME::Key &key, const GpgME::Subkey &subkey) override;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __QGPGME_QGPGMEADDEXISTINGSUBKEYJOB_H__
|
@ -27,16 +27,19 @@ TESTS_ENVIRONMENT = GNUPGHOME=$(GNUPGHOME)
|
||||
EXTRA_DIST = initial.test final.test
|
||||
|
||||
the_tests = \
|
||||
t-addexistingsubkey \
|
||||
t-keylist t-keylocate t-ownertrust t-tofuinfo \
|
||||
t-encrypt t-verify t-various t-config t-remarks t-trustsignatures \
|
||||
t-changeexpiryjob t-wkdlookup t-import
|
||||
|
||||
TESTS = initial.test $(the_tests) final.test
|
||||
|
||||
moc_files = t-keylist.moc t-keylocate.moc t-ownertrust.moc t-tofuinfo.moc \
|
||||
t-encrypt.moc t-support.hmoc t-wkspublish.moc t-verify.moc \
|
||||
t-various.moc t-config.moc t-remarks.moc t-trustsignatures.moc \
|
||||
t-changeexpiryjob.moc t-wkdlookup.moc t-import.moc
|
||||
moc_files = \
|
||||
t-addexistingsubkey.moc \
|
||||
t-keylist.moc t-keylocate.moc t-ownertrust.moc t-tofuinfo.moc \
|
||||
t-encrypt.moc t-support.hmoc t-wkspublish.moc t-verify.moc \
|
||||
t-various.moc t-config.moc t-remarks.moc t-trustsignatures.moc \
|
||||
t-changeexpiryjob.moc t-wkdlookup.moc t-import.moc
|
||||
|
||||
AM_LDFLAGS = -no-install
|
||||
|
||||
@ -52,6 +55,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/lang/cpp/src -I$(top_builddir)/src \
|
||||
|
||||
support_src = t-support.h t-support.cpp
|
||||
|
||||
t_addexistingsubkey_SOURCES = t-addexistingsubkey.cpp $(support_src)
|
||||
t_keylist_SOURCES = t-keylist.cpp $(support_src)
|
||||
t_keylocate_SOURCES = t-keylocate.cpp $(support_src)
|
||||
t_ownertrust_SOURCES = t-ownertrust.cpp $(support_src)
|
||||
@ -74,10 +78,12 @@ nodist_t_keylist_SOURCES = $(moc_files)
|
||||
|
||||
BUILT_SOURCES = $(moc_files) pubring-stamp
|
||||
|
||||
noinst_PROGRAMS = t-keylist t-keylocate t-ownertrust t-tofuinfo t-encrypt \
|
||||
run-keyformailboxjob t-wkspublish t-verify t-various t-config t-remarks \
|
||||
t-trustsignatures t-changeexpiryjob t-wkdlookup t-import run-importjob \
|
||||
run-exportjob
|
||||
noinst_PROGRAMS = \
|
||||
t-addexistingsubkey \
|
||||
t-keylist t-keylocate t-ownertrust t-tofuinfo t-encrypt \
|
||||
run-keyformailboxjob t-wkspublish t-verify t-various t-config t-remarks \
|
||||
t-trustsignatures t-changeexpiryjob t-wkdlookup t-import run-importjob \
|
||||
run-exportjob
|
||||
|
||||
CLEANFILES = secring.gpg pubring.gpg pubring.kbx trustdb.gpg dirmngr.conf \
|
||||
gpg-agent.conf pubring.kbx~ S.gpg-agent gpg.conf pubring.gpg~ \
|
||||
|
260
lang/qt/tests/t-addexistingsubkey.cpp
Normal file
260
lang/qt/tests/t-addexistingsubkey.cpp
Normal file
@ -0,0 +1,260 @@
|
||||
/* t-addexistingsubkey.cpp
|
||||
|
||||
This file is part of qgpgme, the Qt API binding for gpgme
|
||||
Copyright (c) 2022 g10 Code GmbH
|
||||
Software engineering by Ingo Klöcker <dev@ingo-kloecker.de>
|
||||
|
||||
QGpgME 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.
|
||||
|
||||
QGpgME 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
|
||||
|
||||
In addition, as a special exception, the copyright holders give
|
||||
permission to link the code of this program with any edition of
|
||||
the Qt library by Trolltech AS, Norway (or with modified versions
|
||||
of Qt that use the same license as Qt), and distribute linked
|
||||
combinations including the two. You must obey the GNU General
|
||||
Public License in all respects for all of the code used other than
|
||||
Qt. If you modify this file, you may extend this exception to
|
||||
your version of the file, but you are not obligated to do so. If
|
||||
you do not wish to do so, delete this exception statement from
|
||||
your version.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "t-support.h"
|
||||
|
||||
#include "addexistingsubkeyjob.h"
|
||||
#include "protocol.h"
|
||||
|
||||
#include <QSignalSpy>
|
||||
#include <QTest>
|
||||
|
||||
#include "context.h"
|
||||
#include "data.h"
|
||||
#include "engineinfo.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using namespace QGpgME;
|
||||
using namespace GpgME;
|
||||
|
||||
static const char *requiredVersion = "2.3.5";
|
||||
|
||||
/* Test keys
|
||||
sec# ed25519 2022-01-13 [SC]
|
||||
1CB8C6A0317AA83F44FE009932392C82B814C8E0
|
||||
uid [ unknown] source-key@example.net
|
||||
ssb cv25519 2022-01-13 [E]
|
||||
ssb cv25519 2022-01-13 [E] [expires: 2100-01-01]
|
||||
|
||||
sec ed25519 2022-01-13 [SC]
|
||||
C3C87F0A3920B01F9E4450EA2B79F21D4DD10BFC
|
||||
uid [ unknown] target-key@example.net
|
||||
ssb cv25519 2022-01-13 [E]
|
||||
* generated with
|
||||
export GNUPGHOME=$(mktemp -d)
|
||||
gpg -K
|
||||
gpg --batch --pinentry-mode loopback --passphrase abc --quick-gen-key source-key@example.net default default never
|
||||
fpr=$(gpg -k --with-colons source-key@example.net | grep ^fpr | head -1 | cut -d ':' -f 10)
|
||||
gpg --batch --pinentry-mode loopback --passphrase abc --quick-add-key ${fpr} default default 21000101T120000
|
||||
gpg --batch --pinentry-mode loopback --passphrase abc --quick-gen-key target-key@example.net default default never
|
||||
gpg -K
|
||||
gpg --export-secret-subkeys --armor --batch --pinentry-mode loopback --passphrase abc --comment source-key@example.net source-key@example.net | sed 's/\(.*\)/ "\1\\n"/'
|
||||
gpg --export-secret-keys --armor --batch --pinentry-mode loopback --passphrase abc --comment target-key@example.net target-key@example.net | sed 's/\(.*\)/ "\1\\n"/'
|
||||
#rm -rf ${GNUPGHOME}
|
||||
unset GNUPGHOME
|
||||
*/
|
||||
static const char *testKeyData =
|
||||
"-----BEGIN PGP PRIVATE KEY BLOCK-----\n"
|
||||
"Comment: source-key@example.net\n"
|
||||
"\n"
|
||||
"lDsEYd/ujBYJKwYBBAHaRw8BAQdAwiZPINTcrpgmu6ZWSaPZlcRSd4nDuofVMhe7\n"
|
||||
"c2XrFyT/AGUAR05VAbQWc291cmNlLWtleUBleGFtcGxlLm5ldIiUBBMWCgA8FiEE\n"
|
||||
"HLjGoDF6qD9E/gCZMjksgrgUyOAFAmHf7owCGwMFCwkIBwIDIgIBBhUKCQgLAgQW\n"
|
||||
"AgMBAh4HAheAAAoJEDI5LIK4FMjgupIA/Au2YEAT9dYdJd0eJCJerG5YAeoB+uBs\n"
|
||||
"mBkgr6xXE0bIAP43b6u1Jtvf/Wm3BhRbLd5Tg67Ba4CIZ8ZLGng73FBoBpyLBGHf\n"
|
||||
"7owSCisGAQQBl1UBBQEBB0Cpg8Qof/WShxROZZtmPnw24vTk0R8nIAF1CZJ0bG/C\n"
|
||||
"SwMBCAf+BwMCtzxziVxQEor8w/VVzHp4/hVSCUyrpiX7Djf04cIMs2bFPduZLgxb\n"
|
||||
"c1SXhlgiqU0YBNntbGGNdKjTP6FMbYWq1+NwQm6ZXtC76LPG7syM94h4BBgWCgAg\n"
|
||||
"FiEEHLjGoDF6qD9E/gCZMjksgrgUyOAFAmHf7owCGwwACgkQMjksgrgUyOCI0wEA\n"
|
||||
"+f56fkvDDUwMOMw7n4+GKpfJXpWhVL08ttccbBOa/9IA/2HYA/78ZaD8E5EyqAEK\n"
|
||||
"Aj9Au+2oJu9V5qo92QEoqwYHnIsEYd/vgxIKKwYBBAGXVQEFAQEHQBa9FxJkm/9D\n"
|
||||
"xABildkaYMrbJbu8BPk6uv9V8aLmv9FnAwEIB/4HAwIPhcbN8s6OzPz8/g78TrCh\n"
|
||||
"xqQb2kygCEj+OQ4/XXU3lus2b5xS5h44LGt99Wisqx+wVPDXmPDJOaxjhHXDmJxd\n"
|
||||
"/LplIEhykojSm3uUDxERiH4EGBYKACYWIQQcuMagMXqoP0T+AJkyOSyCuBTI4AUC\n"
|
||||
"Yd/vgwIbDAUJkqcQPQAKCRAyOSyCuBTI4IUjAP9BTfOD+jy6lLmzNO9pquRSAxi/\n"
|
||||
"PQuglGtpS0LQEJMEOwD+PFnsMe2EtQ+WVSDBeB7O0m61EXeY+RhpuhNtsNXVuwc=\n"
|
||||
"=wIPU\n"
|
||||
"-----END PGP PRIVATE KEY BLOCK-----\n"
|
||||
"-----BEGIN PGP PRIVATE KEY BLOCK-----\n"
|
||||
"Comment: target-key@example.net\n"
|
||||
"\n"
|
||||
"lIYEYd/v/RYJKwYBBAHaRw8BAQdAKoILWXG3yaLb2EniNKQLUjwsrvy5vgAN299J\n"
|
||||
"W5cFbrz+BwMC/uKbCq3sK5H8QVtEQ/IxGmjWNBpy6c8EDlOG4APi4o4VE+bEYD8w\n"
|
||||
"J3Kk/lzSm6ZT5vC6DDASks797omjXD+J7zZ0vtTPvheYi/nsVz2UebQWdGFyZ2V0\n"
|
||||
"LWtleUBleGFtcGxlLm5ldIiUBBMWCgA8FiEEw8h/CjkgsB+eRFDqK3nyHU3RC/wF\n"
|
||||
"AmHf7/0CGwMFCwkIBwIDIgIBBhUKCQgLAgQWAgMBAh4HAheAAAoJECt58h1N0Qv8\n"
|
||||
"rXcBAPxnkXqpp4IY3iTKV5XAdo7Uys7U/joUD73rj2XEvgI1AQDhKK4PLxPhf3ki\n"
|
||||
"FKU0RA7itxzOH+F8bQ5BdYS49jDPCpyLBGHf7/0SCisGAQQBl1UBBQEBB0Dq9rwA\n"
|
||||
"hAA2UFJShFsLFp7+g4uhWDfuDa3VjeIQRM+9QgMBCAf+BwMCMfCTl0LNqsn836t5\n"
|
||||
"f2ZHBuMcNs4JWYmdLAIVaewEHq7zhOsX3iB+/yxwu9g2mXc4XUJ1iQzXLOYwgGov\n"
|
||||
"8jIovrr01hDkSg4rvM9JKMWdd4h4BBgWCgAgFiEEw8h/CjkgsB+eRFDqK3nyHU3R\n"
|
||||
"C/wFAmHf7/0CGwwACgkQK3nyHU3RC/xyfAEAqnMdSv6FTAwAWrYvJqJtSVoEhjMn\n"
|
||||
"3c2qMsu34Bk86/MBAKHbLFmdyePvHaxKeO8CkQDoJzK8rYzw3RAmq/5JsQkL\n"
|
||||
"=rOVf\n"
|
||||
"-----END PGP PRIVATE KEY BLOCK-----\n";
|
||||
|
||||
class AddExistingSubkeyJobTest : public QGpgMETest
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
|
||||
void initTestCase()
|
||||
{
|
||||
QGpgMETest::initTestCase();
|
||||
|
||||
// set up the test fixture for this test
|
||||
qputenv("GNUPGHOME", mGnupgHomeTestFixture.path().toUtf8());
|
||||
QVERIFY(importSecretKeys(testKeyData, 2));
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < requiredVersion) {
|
||||
QSKIP("gpg does not yet support adding an existing subkey to another key via the command API");
|
||||
}
|
||||
|
||||
// set up a copy of the test fixture for each test function
|
||||
mGnupgHomeTestCopy.reset(new QTemporaryDir{});
|
||||
QVERIFY(copyKeyrings(mGnupgHomeTestFixture.path(), mGnupgHomeTestCopy->path()));
|
||||
qputenv("GNUPGHOME", mGnupgHomeTestCopy->path().toUtf8());
|
||||
}
|
||||
|
||||
void testAddExistingSubkeyAsync()
|
||||
{
|
||||
// Get the key the subkey should be added to
|
||||
auto key = getTestKey("target-key@example.net");
|
||||
QVERIFY(!key.isNull());
|
||||
|
||||
// Get the key with the subkey to add
|
||||
auto sourceKey = getTestKey("source-key@example.net", 3);
|
||||
QVERIFY(!sourceKey.isNull());
|
||||
|
||||
auto job = std::unique_ptr<AddExistingSubkeyJob>{openpgp()->addExistingSubkeyJob()};
|
||||
hookUpPassphraseProvider(job.get());
|
||||
|
||||
Error result;
|
||||
connect(job.get(), &AddExistingSubkeyJob::result,
|
||||
job.get(), [this, &result](const Error &result_) {
|
||||
result = result_;
|
||||
Q_EMIT asyncDone();
|
||||
});
|
||||
QVERIFY(!job->start(key, sourceKey.subkey(1)));
|
||||
job.release(); // after the job has been started it's on its own
|
||||
|
||||
QSignalSpy spy (this, SIGNAL(asyncDone()));
|
||||
QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT));
|
||||
|
||||
QVERIFY(result.code() == GPG_ERR_NO_ERROR);
|
||||
key.update();
|
||||
QCOMPARE(key.numSubkeys(), 3u);
|
||||
}
|
||||
|
||||
void testAddExistingSubkeySync()
|
||||
{
|
||||
// Get the key the subkey should be added to
|
||||
auto key = getTestKey("target-key@example.net");
|
||||
QVERIFY(!key.isNull());
|
||||
|
||||
// Get the key with the subkey to add
|
||||
auto sourceKey = getTestKey("source-key@example.net", 3);
|
||||
QVERIFY(!sourceKey.isNull());
|
||||
auto sourceSubkey = sourceKey.subkey(1);
|
||||
QVERIFY(sourceSubkey.expirationTime() == 0);
|
||||
|
||||
auto job = std::unique_ptr<AddExistingSubkeyJob>{openpgp()->addExistingSubkeyJob()};
|
||||
hookUpPassphraseProvider(job.get());
|
||||
|
||||
const auto result = job->exec(key, sourceSubkey);
|
||||
|
||||
QVERIFY(result.code() == GPG_ERR_NO_ERROR);
|
||||
key.update();
|
||||
QCOMPARE(key.numSubkeys(), 3u);
|
||||
QCOMPARE(key.subkey(2).expirationTime(), 0);
|
||||
}
|
||||
|
||||
void testAddExistingSubkeyWithExpiration()
|
||||
{
|
||||
// Get the key the subkey should be added to
|
||||
auto key = getTestKey("target-key@example.net");
|
||||
QVERIFY(!key.isNull());
|
||||
|
||||
// Get the key with the subkey to add
|
||||
auto sourceKey = getTestKey("source-key@example.net", 3);
|
||||
QVERIFY(!sourceKey.isNull());
|
||||
auto sourceSubkey = sourceKey.subkey(2);
|
||||
QVERIFY(sourceSubkey.expirationTime() != 0);
|
||||
|
||||
auto job = std::unique_ptr<AddExistingSubkeyJob>{openpgp()->addExistingSubkeyJob()};
|
||||
hookUpPassphraseProvider(job.get());
|
||||
|
||||
const auto result = job->exec(key, sourceSubkey);
|
||||
|
||||
QVERIFY(result.code() == GPG_ERR_NO_ERROR);
|
||||
key.update();
|
||||
QCOMPARE(key.numSubkeys(), 3u);
|
||||
|
||||
// allow 1 second different expiration because gpg calculates with
|
||||
// expiration as difference to current time and takes current time
|
||||
// several times
|
||||
const auto allowedDeltaTSeconds = 1;
|
||||
const auto expectedExpirationRange = std::make_pair(
|
||||
sourceSubkey.expirationTime() - allowedDeltaTSeconds,
|
||||
sourceSubkey.expirationTime() + allowedDeltaTSeconds);
|
||||
const auto actualExpiration = key.subkey(2).expirationTime();
|
||||
QVERIFY2(actualExpiration >= expectedExpirationRange.first,
|
||||
("actual: " + std::to_string(actualExpiration) +
|
||||
"; expected: " + std::to_string(expectedExpirationRange.first)).c_str());
|
||||
QVERIFY2(actualExpiration <= expectedExpirationRange.second,
|
||||
("actual: " + std::to_string(actualExpiration) +
|
||||
"; expected: " + std::to_string(expectedExpirationRange.second)).c_str());
|
||||
}
|
||||
|
||||
private:
|
||||
Key getTestKey(const char *pattern, unsigned int expectedSubkeys = 2)
|
||||
{
|
||||
auto ctx = Context::create(OpenPGP);
|
||||
VERIFY_OR_OBJECT(ctx);
|
||||
|
||||
Error err;
|
||||
auto key = ctx->key(pattern, err, /*secret=*/true);
|
||||
VERIFY_OR_OBJECT(!err);
|
||||
VERIFY_OR_OBJECT(!key.isNull());
|
||||
COMPARE_OR_OBJECT(key.numSubkeys(), expectedSubkeys);
|
||||
for (unsigned int i = 0; i < key.numSubkeys(); ++i) {
|
||||
VERIFY_OR_OBJECT(!key.subkey(i).isNull());
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
private:
|
||||
QTemporaryDir mGnupgHomeTestFixture;
|
||||
std::unique_ptr<QTemporaryDir> mGnupgHomeTestCopy;
|
||||
};
|
||||
|
||||
QTEST_MAIN(AddExistingSubkeyJobTest)
|
||||
|
||||
#include "t-addexistingsubkey.moc"
|
@ -36,7 +36,9 @@
|
||||
|
||||
#include "t-support.h"
|
||||
|
||||
#include "importjob.h"
|
||||
#include "job.h"
|
||||
#include "protocol.h"
|
||||
|
||||
#include <QTest>
|
||||
|
||||
@ -44,9 +46,11 @@
|
||||
#include <QCoreApplication>
|
||||
#include <QObject>
|
||||
#include <QDir>
|
||||
#include <QSignalSpy>
|
||||
|
||||
#include "context.h"
|
||||
#include "engineinfo.h"
|
||||
#include "importresult.h"
|
||||
|
||||
using namespace GpgME;
|
||||
using namespace QGpgME;
|
||||
@ -97,6 +101,30 @@ bool QGpgMETest::copyKeyrings(const QString &src, const QString &dest)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QGpgMETest::importSecretKeys(const char *keyData, int expectedKeys)
|
||||
{
|
||||
auto job = std::unique_ptr<ImportJob>{openpgp()->importJob()};
|
||||
VERIFY_OR_FALSE(job);
|
||||
hookUpPassphraseProvider(job.get());
|
||||
|
||||
ImportResult result;
|
||||
connect(job.get(), &ImportJob::result,
|
||||
this, [this, &result](const ImportResult &result_) {
|
||||
result = result_;
|
||||
Q_EMIT asyncDone();
|
||||
});
|
||||
VERIFY_OR_FALSE(!job->start(keyData));
|
||||
job.release(); // after the job has been started it's on its own
|
||||
|
||||
QSignalSpy spy (this, SIGNAL(asyncDone()));
|
||||
VERIFY_OR_FALSE(spy.wait(QSIGNALSPY_TIMEOUT));
|
||||
VERIFY_OR_FALSE(!result.error());
|
||||
VERIFY_OR_FALSE(!result.imports().empty());
|
||||
COMPARE_OR_FALSE(result.numSecretKeysImported(), expectedKeys);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void QGpgMETest::hookUpPassphraseProvider(GpgME::Context *context)
|
||||
{
|
||||
context->setPassphraseProvider(&mPassphraseProvider);
|
||||
|
@ -48,6 +48,32 @@ namespace QGpgME
|
||||
class Job;
|
||||
}
|
||||
|
||||
/// generic variant of QVERIFY returning \a returnValue on failure
|
||||
#define VERIFY_OR_RETURN_VALUE(statement, returnValue) \
|
||||
do {\
|
||||
if (!QTest::qVerify(static_cast<bool>(statement), #statement, "", __FILE__, __LINE__))\
|
||||
return returnValue;\
|
||||
} while (false)
|
||||
|
||||
/// generic variant of QCOMPARE returning \a returnValue on failure
|
||||
#define COMPARE_OR_RETURN_VALUE(actual, expected, returnValue) \
|
||||
do {\
|
||||
if (!QTest::qCompare(actual, expected, #actual, #expected, __FILE__, __LINE__))\
|
||||
return returnValue;\
|
||||
} while (false)
|
||||
|
||||
/// variant of QVERIFY returning a default constructed object on failure
|
||||
#define VERIFY_OR_OBJECT(statement) VERIFY_OR_RETURN_VALUE(statement, {})
|
||||
|
||||
/// variant of QCOMPARE returning a default constructed object on failure
|
||||
#define COMPARE_OR_OBJECT(actual, expected) COMPARE_OR_RETURN_VALUE(actual, expected, {})
|
||||
|
||||
/// variant of QVERIFY returning \c false on failure
|
||||
#define VERIFY_OR_FALSE(statement) VERIFY_OR_RETURN_VALUE(statement, false)
|
||||
|
||||
/// variant of QCOMPARE returning \c false on failure
|
||||
#define COMPARE_OR_FALSE(actual, expected) COMPARE_OR_RETURN_VALUE(actual, expected, false)
|
||||
|
||||
namespace QTest
|
||||
{
|
||||
template <>
|
||||
@ -88,6 +114,8 @@ protected:
|
||||
|
||||
bool copyKeyrings(const QString &from, const QString& to);
|
||||
|
||||
bool importSecretKeys(const char *keyData, int expectedKeys = 1);
|
||||
|
||||
void hookUpPassphraseProvider(GpgME::Context *context);
|
||||
void hookUpPassphraseProvider(QGpgME::Job *job);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user