summaryrefslogtreecommitdiff
path: root/lang
diff options
context:
space:
mode:
authorJinWang An <jinwang.an@samsung.com>2021-12-01 16:54:39 +0900
committerJinWang An <jinwang.an@samsung.com>2021-12-01 16:54:39 +0900
commitd19c360948ede5ffe5974de8abc9da44be617ca1 (patch)
treef77dd8640a12e7e960c3d408f90e08e069ece9f1 /lang
parentfc59d1efdca7b2886739948b88a1aa8cd4243146 (diff)
downloadgpgme-d19c360948ede5ffe5974de8abc9da44be617ca1.tar.gz
gpgme-d19c360948ede5ffe5974de8abc9da44be617ca1.tar.bz2
gpgme-d19c360948ede5ffe5974de8abc9da44be617ca1.zip
Imported Upstream version 1.16.0upstream/1.16.0
Diffstat (limited to 'lang')
-rw-r--r--lang/Makefile.in4
-rw-r--r--lang/cl/Makefile.in4
-rw-r--r--lang/cl/gpgme.asd2
-rw-r--r--lang/cpp/Makefile.in4
-rw-r--r--lang/cpp/src/Makefile.in4
-rw-r--r--lang/cpp/src/editinteractor.cpp13
-rw-r--r--lang/cpp/src/gpgsignkeyeditinteractor.cpp50
-rw-r--r--lang/cpp/src/gpgsignkeyeditinteractor.h5
-rw-r--r--lang/cpp/src/key.cpp23
-rw-r--r--lang/cpp/src/key.h11
-rw-r--r--lang/cpp/tests/Makefile.in4
-rw-r--r--lang/js/BrowserTestExtension/Makefile.in4
-rw-r--r--lang/js/DemoExtension/Makefile.in4
-rw-r--r--lang/js/Makefile.in4
-rw-r--r--lang/js/src/Makefile.in4
-rw-r--r--lang/python/Makefile.in4
-rw-r--r--lang/python/doc/Makefile.in4
-rw-r--r--lang/python/examples/Makefile.in4
-rw-r--r--lang/python/src/Makefile.in4
-rw-r--r--lang/python/src/core.py12
-rw-r--r--lang/python/tests/Makefile.in9
-rw-r--r--lang/qt/Makefile.in4
-rw-r--r--lang/qt/doc/Doxyfile.in2
-rw-r--r--lang/qt/doc/Makefile.in4
-rw-r--r--lang/qt/src/Makefile.am2
-rw-r--r--lang/qt/src/Makefile.in11
-rw-r--r--lang/qt/src/cryptoconfig.cpp20
-rw-r--r--lang/qt/src/cryptoconfig.h22
-rw-r--r--lang/qt/src/dataprovider.cpp12
-rw-r--r--lang/qt/src/qgpgme_debug.cpp4
-rw-r--r--lang/qt/src/qgpgme_debug.h7
-rw-r--r--lang/qt/src/qgpgmenewcryptoconfig.cpp87
-rw-r--r--lang/qt/src/qgpgmesignkeyjob.cpp137
-rw-r--r--lang/qt/src/qgpgmesignkeyjob.h23
-rw-r--r--lang/qt/src/signkeyjob.h38
-rw-r--r--lang/qt/tests/Makefile.am8
-rw-r--r--lang/qt/tests/Makefile.in41
-rw-r--r--lang/qt/tests/t-trustsignatures.cpp565
-rw-r--r--lang/qt/tests/t-various.cpp132
39 files changed, 1125 insertions, 171 deletions
diff --git a/lang/Makefile.in b/lang/Makefile.in
index 3e27eae..c080aa4 100644
--- a/lang/Makefile.in
+++ b/lang/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
diff --git a/lang/cl/Makefile.in b/lang/cl/Makefile.in
index d30c361..d9639d4 100644
--- a/lang/cl/Makefile.in
+++ b/lang/cl/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
diff --git a/lang/cl/gpgme.asd b/lang/cl/gpgme.asd
index c22940a..f0a15d8 100644
--- a/lang/cl/gpgme.asd
+++ b/lang/cl/gpgme.asd
@@ -27,7 +27,7 @@
(defsystem gpgme
:description "GnuPG Made Easy."
:author "g10 Code GmbH"
- :version "1.15.1"
+ :version "1.16.0"
:licence "GPL"
:defsystem-depends-on ("cffi-grovel")
:depends-on ("cffi" "gpg-error" "trivial-garbage")
diff --git a/lang/cpp/Makefile.in b/lang/cpp/Makefile.in
index 628f808..14315ac 100644
--- a/lang/cpp/Makefile.in
+++ b/lang/cpp/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
diff --git a/lang/cpp/src/Makefile.in b/lang/cpp/src/Makefile.in
index 958a057..5dc6fcc 100644
--- a/lang/cpp/src/Makefile.in
+++ b/lang/cpp/src/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
diff --git a/lang/cpp/src/editinteractor.cpp b/lang/cpp/src/editinteractor.cpp
index 774903d..e411ada 100644
--- a/lang/cpp/src/editinteractor.cpp
+++ b/lang/cpp/src/editinteractor.cpp
@@ -62,9 +62,10 @@ public:
~Private();
private:
- unsigned int state;
+ unsigned int state = StartState;
Error error;
- std::FILE *debug;
+ std::FILE *debug = nullptr;
+ bool debugNeedsClosing = false;
};
class GpgME::CallbackHelper
@@ -174,10 +175,7 @@ static gpgme_error_t edit_interactor_callback(void *opaque, gpgme_status_code_t
const gpgme_edit_cb_t GpgME::edit_interactor_callback = ::edit_interactor_callback;
EditInteractor::Private::Private(EditInteractor *qq)
- : q(qq),
- state(StartState),
- error(),
- debug(nullptr)
+ : q(qq)
{
const char *debug_env = std::getenv("GPGMEPP_INTERACTOR_DEBUG");
if (!debug_env) {
@@ -189,12 +187,13 @@ EditInteractor::Private::Private(EditInteractor *qq)
debug = stderr;
} else if (debug_env) {
debug = std::fopen(debug_env, "a+");
+ debugNeedsClosing = true;
}
}
EditInteractor::Private::~Private()
{
- if (debug) {
+ if (debug && debugNeedsClosing) {
std::fclose(debug);
}
}
diff --git a/lang/cpp/src/gpgsignkeyeditinteractor.cpp b/lang/cpp/src/gpgsignkeyeditinteractor.cpp
index 33ffbcf..4b5d274 100644
--- a/lang/cpp/src/gpgsignkeyeditinteractor.cpp
+++ b/lang/cpp/src/gpgsignkeyeditinteractor.cpp
@@ -66,6 +66,11 @@ public:
unsigned int checkLevel;
bool dupeOk;
Key key;
+ struct {
+ TrustSignatureTrust trust;
+ std::string depth;
+ std::string scope;
+ } trustSignature;
const char *command() const
{
@@ -129,7 +134,8 @@ GpgSignKeyEditInteractor::Private::Private()
currentId(),
nextId(),
checkLevel(0),
- dupeOk(false)
+ dupeOk(false),
+ trustSignature{TrustSignatureTrust::None, "0", {}}
{
}
@@ -190,26 +196,31 @@ static GpgSignKeyEditInteractor_Private::TransitionMap makeTable()
addEntry(COMMAND, GET_BOOL, "sign_uid.okay", CONFIRM);
addEntry(COMMAND, GET_BOOL, "sign_uid.local_promote_okay", CONFIRM2);
addEntry(COMMAND, GET_BOOL, "sign_uid.dupe_okay", DUPE_OK);
+ addEntry(COMMAND, GET_LINE, "trustsig_prompt.trust_value", SET_TRUST_VALUE);
addEntry(UIDS_ANSWER_SIGN_ALL, GET_BOOL, "sign_uid.okay", CONFIRM);
+ addEntry(UIDS_ANSWER_SIGN_ALL, GET_BOOL, "sign_uid.dupe_okay", DUPE_OK);
addEntry(UIDS_ANSWER_SIGN_ALL, GET_LINE, "sign_uid.expire", SET_EXPIRE);
addEntry(UIDS_ANSWER_SIGN_ALL, GET_LINE, "sign_uid.class", SET_CHECK_LEVEL);
- addEntry(SET_TRUST_VALUE, GET_LINE, "trustsign_prompt.trust_depth", SET_TRUST_DEPTH);
- addEntry(SET_TRUST_DEPTH, GET_LINE, "trustsign_prompt.trust_regexp", SET_TRUST_REGEXP);
- addEntry(SET_TRUST_REGEXP, GET_LINE, "sign_uid.okay", CONFIRM);
+ addEntry(UIDS_ANSWER_SIGN_ALL, GET_LINE, "trustsig_prompt.trust_value", SET_TRUST_VALUE);
+ addEntry(SET_TRUST_VALUE, GET_LINE, "trustsig_prompt.trust_depth", SET_TRUST_DEPTH);
+ addEntry(SET_TRUST_DEPTH, GET_LINE, "trustsig_prompt.trust_regexp", SET_TRUST_REGEXP);
+ addEntry(SET_TRUST_REGEXP, GET_BOOL, "sign_uid.okay", CONFIRM);
addEntry(SET_CHECK_LEVEL, GET_BOOL, "sign_uid.okay", CONFIRM);
addEntry(SET_EXPIRE, GET_BOOL, "sign_uid.class", SET_CHECK_LEVEL);
addEntry(CONFIRM, GET_BOOL, "sign_uid.local_promote_okay", CONFIRM);
addEntry(DUPE_OK, GET_BOOL, "sign_uid.okay", CONFIRM);
addEntry(DUPE_OK2, GET_BOOL, "sign_uid.okay", CONFIRM);
+ addEntry(DUPE_OK, GET_LINE, "trustsig_prompt.trust_value", SET_TRUST_VALUE);
+ addEntry(DUPE_OK2, GET_LINE, "trustsig_prompt.trust_value", SET_TRUST_VALUE);
addEntry(CONFIRM, GET_BOOL, "sign_uid.okay", CONFIRM);
addEntry(CONFIRM2, GET_BOOL, "sign_uid.okay", CONFIRM);
addEntry(CONFIRM, GET_LINE, "keyedit.prompt", COMMAND);
- addEntry(CONFIRM, GET_LINE, "trustsign_prompt.trust_value", SET_TRUST_VALUE);
+ addEntry(CONFIRM, GET_LINE, "trustsig_prompt.trust_value", SET_TRUST_VALUE);
addEntry(CONFIRM, GET_LINE, "sign_uid.expire", SET_EXPIRE);
addEntry(CONFIRM, GET_LINE, "sign_uid.class", SET_CHECK_LEVEL);
addEntry(UIDS_LIST_SEPARATELY_DONE, GET_BOOL, "sign_uid.local_promote_okay", CONFIRM);
addEntry(UIDS_LIST_SEPARATELY_DONE, GET_LINE, "keyedit.prompt", COMMAND);
- addEntry(UIDS_LIST_SEPARATELY_DONE, GET_LINE, "trustsign_prompt.trust_value", SET_TRUST_VALUE);
+ addEntry(UIDS_LIST_SEPARATELY_DONE, GET_LINE, "trustsig_prompt.trust_value", SET_TRUST_VALUE);
addEntry(UIDS_LIST_SEPARATELY_DONE, GET_LINE, "sign_uid.expire", SET_EXPIRE);
addEntry(UIDS_LIST_SEPARATELY_DONE, GET_LINE, "sign_uid.class", SET_CHECK_LEVEL);
addEntry(UIDS_LIST_SEPARATELY_DONE, GET_BOOL, "sign_uid.okay", CONFIRM);
@@ -239,12 +250,11 @@ const char *GpgSignKeyEditInteractor::action(Error &err) const
case SET_EXPIRE:
return answer(true);
case SET_TRUST_VALUE:
- // TODO
+ return d->trustSignature.trust == TrustSignatureTrust::Partial ? "1" : "2";
case SET_TRUST_DEPTH:
- //TODO
+ return d->trustSignature.depth.c_str();
case SET_TRUST_REGEXP:
- //TODO
- return nullptr;
+ return d->trustSignature.scope.c_str();
case SET_CHECK_LEVEL:
return check_level_strings[d->checkLevel];
case DUPE_OK:
@@ -360,3 +370,23 @@ void GpgSignKeyEditInteractor::setDupeOk(bool value)
assert(!d->started);
d->dupeOk = value;
}
+
+void GpgSignKeyEditInteractor::setTrustSignatureTrust(GpgME::TrustSignatureTrust trust)
+{
+ assert(!d->started);
+ assert(trust != TrustSignatureTrust::None);
+ d->trustSignature.trust = trust;
+}
+
+void GpgSignKeyEditInteractor::setTrustSignatureDepth(unsigned short depth)
+{
+ assert(!d->started);
+ assert(depth <= 255);
+ d->trustSignature.depth = std::to_string(depth);
+}
+
+void GpgSignKeyEditInteractor::setTrustSignatureScope(const std::string &scope)
+{
+ assert(!d->started);
+ d->trustSignature.scope = scope;
+}
diff --git a/lang/cpp/src/gpgsignkeyeditinteractor.h b/lang/cpp/src/gpgsignkeyeditinteractor.h
index d459687..889ed56 100644
--- a/lang/cpp/src/gpgsignkeyeditinteractor.h
+++ b/lang/cpp/src/gpgsignkeyeditinteractor.h
@@ -35,6 +35,7 @@ namespace GpgME
class Key;
class UserID;
+enum class TrustSignatureTrust : char;
class GPGMEPP_EXPORT GpgSignKeyEditInteractor : public EditInteractor
{
@@ -58,6 +59,10 @@ public:
* Context::setFlag before calling edit.*/
void setDupeOk(bool value);
+ void setTrustSignatureTrust(TrustSignatureTrust trust);
+ void setTrustSignatureDepth(unsigned short depth);
+ void setTrustSignatureScope(const std::string &scope);
+
private:
const char *action(Error &err) const override;
unsigned int nextState(unsigned int statusCode, const char *args, Error &err) const override;
diff --git a/lang/cpp/src/key.cpp b/lang/cpp/src/key.cpp
index f9cc2b6..b893a7c 100644
--- a/lang/cpp/src/key.cpp
+++ b/lang/cpp/src/key.cpp
@@ -1083,6 +1083,29 @@ const char *UserID::Signature::policyURL() const
return nullptr;
}
+bool UserID::Signature::isTrustSignature() const
+{
+ return sig && sig->trust_depth > 0;
+}
+
+TrustSignatureTrust UserID::Signature::trustValue() const
+{
+ if (!sig || !isTrustSignature()) {
+ return TrustSignatureTrust::None;
+ }
+ return sig->trust_value >= 120 ? TrustSignatureTrust::Complete : TrustSignatureTrust::Partial;
+}
+
+unsigned int UserID::Signature::trustDepth() const
+{
+ return sig ? sig->trust_depth : 0;
+}
+
+const char *UserID::Signature::trustScope() const
+{
+ return sig ? sig->trust_scope : nullptr;
+}
+
std::string UserID::addrSpecFromString(const char *userid)
{
if (!userid) {
diff --git a/lang/cpp/src/key.h b/lang/cpp/src/key.h
index 515bf18..0e6380d 100644
--- a/lang/cpp/src/key.h
+++ b/lang/cpp/src/key.h
@@ -47,6 +47,12 @@ class TofuInfo;
typedef std::shared_ptr< std::remove_pointer<gpgme_key_t>::type > shared_gpgme_key_t;
+enum class TrustSignatureTrust : char {
+ None = 0,
+ Partial,
+ Complete,
+};
+
//
// class Key
//
@@ -514,6 +520,11 @@ public:
GpgME::Notation notation(unsigned int idx) const;
std::vector<GpgME::Notation> notations() const;
+ bool isTrustSignature() const;
+ TrustSignatureTrust trustValue() const;
+ unsigned int trustDepth() const;
+ const char *trustScope() const;
+
private:
shared_gpgme_key_t key;
gpgme_user_id_t uid;
diff --git a/lang/cpp/tests/Makefile.in b/lang/cpp/tests/Makefile.in
index b065f61..a675bf1 100644
--- a/lang/cpp/tests/Makefile.in
+++ b/lang/cpp/tests/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
diff --git a/lang/js/BrowserTestExtension/Makefile.in b/lang/js/BrowserTestExtension/Makefile.in
index 057f9e5..68fa1ac 100644
--- a/lang/js/BrowserTestExtension/Makefile.in
+++ b/lang/js/BrowserTestExtension/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
diff --git a/lang/js/DemoExtension/Makefile.in b/lang/js/DemoExtension/Makefile.in
index 4290886..ce14e1f 100644
--- a/lang/js/DemoExtension/Makefile.in
+++ b/lang/js/DemoExtension/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
diff --git a/lang/js/Makefile.in b/lang/js/Makefile.in
index 15383e2..8810732 100644
--- a/lang/js/Makefile.in
+++ b/lang/js/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
diff --git a/lang/js/src/Makefile.in b/lang/js/src/Makefile.in
index 3fff929..05f1d7a 100644
--- a/lang/js/src/Makefile.in
+++ b/lang/js/src/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
diff --git a/lang/python/Makefile.in b/lang/python/Makefile.in
index 19ed6f3..c0fc091 100644
--- a/lang/python/Makefile.in
+++ b/lang/python/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
diff --git a/lang/python/doc/Makefile.in b/lang/python/doc/Makefile.in
index 36eef56..a53283b 100644
--- a/lang/python/doc/Makefile.in
+++ b/lang/python/doc/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
diff --git a/lang/python/examples/Makefile.in b/lang/python/examples/Makefile.in
index a674e9b..06ea925 100644
--- a/lang/python/examples/Makefile.in
+++ b/lang/python/examples/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
diff --git a/lang/python/src/Makefile.in b/lang/python/src/Makefile.in
index 33d7e0b..f250242 100644
--- a/lang/python/src/Makefile.in
+++ b/lang/python/src/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
diff --git a/lang/python/src/core.py b/lang/python/src/core.py
index 5e57e4a..9618adc 100644
--- a/lang/python/src/core.py
+++ b/lang/python/src/core.py
@@ -342,7 +342,7 @@ class Context(GpgmeWrapper):
return self.__read__(sink, ciphertext), result, sig_result
- def decrypt(self, ciphertext, sink=None, passphrase=None, verify=True):
+ def decrypt(self, ciphertext, sink=None, passphrase=None, verify=True, filter_signatures=True):
"""Decrypt data
Decrypt the given ciphertext and verify any signatures. If
@@ -354,6 +354,10 @@ class Context(GpgmeWrapper):
signatures are required and no MissingSignatures error will be
raised).
+ The filter_signatures argument can be used to force this
+ function to return signatures that are not fully trusted - for
+ example because they were made by unknown keys.
+
If the ciphertext is symmetrically encrypted using a
passphrase, that passphrase can be given as parameter, using a
callback registered at the context, or out-of-band via
@@ -364,6 +368,8 @@ class Context(GpgmeWrapper):
passphrase -- for symmetric decryption
verify -- check signatures (boolean or iterable of keys,
see above) (default True)
+ filter_signatures -- if this function should filter out signatures
+ that are not completely OK (default True)
Returns:
plaintext -- the decrypted data (or None if sink is given)
@@ -437,8 +443,8 @@ class Context(GpgmeWrapper):
results=results)
if do_sig_verification:
- # filter out all invalid signatures
- verify_result.signatures = list(filter(lambda s: s.status == errors.NO_ERROR, verify_result.signatures))
+ if filter_signatures:
+ verify_result.signatures = list(filter(lambda s: s.status == errors.NO_ERROR, verify_result.signatures))
if required_keys is not None:
missing = []
for key in required_keys:
diff --git a/lang/python/tests/Makefile.in b/lang/python/tests/Makefile.in
index 306b904..9e60086 100644
--- a/lang/python/tests/Makefile.in
+++ b/lang/python/tests/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -461,7 +461,8 @@ all-am: Makefile
installdirs:
install: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) install-am
-install-exec: install-exec-am
+install-exec: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) install-exec-am
install-data: install-data-am
uninstall: uninstall-am
@@ -558,7 +559,7 @@ ps-am:
uninstall-am:
-.MAKE: all check install install-am install-strip
+.MAKE: all check install install-am install-exec install-strip
.PHONY: all all-am check check-am clean clean-generic clean-libtool \
clean-local cscopelist-am ctags-am distclean distclean-generic \
diff --git a/lang/qt/Makefile.in b/lang/qt/Makefile.in
index da82af8..d186d88 100644
--- a/lang/qt/Makefile.in
+++ b/lang/qt/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
diff --git a/lang/qt/doc/Doxyfile.in b/lang/qt/doc/Doxyfile.in
index 8ccd11c..ccccbc4 100644
--- a/lang/qt/doc/Doxyfile.in
+++ b/lang/qt/doc/Doxyfile.in
@@ -140,7 +140,7 @@ INLINE_INHERITED_MEMB = NO
# shortest path that makes the file name unique will be used
# The default value is: YES.
-FULL_PATH_NAMES = YES
+FULL_PATH_NAMES = NO
# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
# Stripping is only done if one of the specified strings matches the left-hand
diff --git a/lang/qt/doc/Makefile.in b/lang/qt/doc/Makefile.in
index 9b43d7f..eadc021 100644
--- a/lang/qt/doc/Makefile.in
+++ b/lang/qt/doc/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
diff --git a/lang/qt/src/Makefile.am b/lang/qt/src/Makefile.am
index 94e88f3..683b44c 100644
--- a/lang/qt/src/Makefile.am
+++ b/lang/qt/src/Makefile.am
@@ -233,7 +233,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/lang/cpp/src -I$(top_builddir)/src \
-Wzero-as-null-pointer-constant
libqgpgme_la_LIBADD = ../../cpp/src/libgpgmepp.la ../../../src/libgpgme.la \
- @LIBASSUAN_LIBS@ @GPGME_QT_LIBS@
+ @LIBASSUAN_LIBS@ @GPG_ERROR_LIBS@ @GPGME_QT_LIBS@
libqgpgme_la_LDFLAGS = -no-undefined -version-info \
@LIBQGPGME_LT_CURRENT@:@LIBQGPGME_LT_REVISION@:@LIBQGPGME_LT_AGE@
diff --git a/lang/qt/src/Makefile.in b/lang/qt/src/Makefile.in
index a89b904..8e37198 100644
--- a/lang/qt/src/Makefile.in
+++ b/lang/qt/src/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -728,7 +728,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/lang/cpp/src -I$(top_builddir)/src \
-Wzero-as-null-pointer-constant
libqgpgme_la_LIBADD = ../../cpp/src/libgpgmepp.la ../../../src/libgpgme.la \
- @LIBASSUAN_LIBS@ @GPGME_QT_LIBS@
+ @LIBASSUAN_LIBS@ @GPG_ERROR_LIBS@ @GPGME_QT_LIBS@
libqgpgme_la_LDFLAGS = -no-undefined -version-info \
@LIBQGPGME_LT_CURRENT@:@LIBQGPGME_LT_REVISION@:@LIBQGPGME_LT_AGE@
@@ -1058,7 +1058,8 @@ installdirs:
done
install: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) install-am
-install-exec: install-exec-am
+install-exec: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) install-exec-am
install-data: install-data-am
uninstall: uninstall-am
@@ -1239,7 +1240,7 @@ uninstall-am: uninstall-camelcaseincludeHEADERS \
uninstall-nodist_qgpgmeincludeHEADERS \
uninstall-qgpgmeincludeHEADERS
-.MAKE: all check install install-am install-strip
+.MAKE: all check install install-am install-exec install-strip
.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
clean-generic clean-libLTLIBRARIES clean-libtool cscopelist-am \
diff --git a/lang/qt/src/cryptoconfig.cpp b/lang/qt/src/cryptoconfig.cpp
index 7121220..d52dce7 100644
--- a/lang/qt/src/cryptoconfig.cpp
+++ b/lang/qt/src/cryptoconfig.cpp
@@ -43,3 +43,23 @@ QStringList CryptoConfigEntry::stringValueList() const
}
return entry->stringValueList();
}
+
+QGpgME::CryptoConfigEntry *CryptoConfig::entry(const QString &componentName, const QString &entryName) const
+{
+ const CryptoConfigComponent *comp = component(componentName);
+ const QStringList groupNames = comp->groupList();
+ for (const auto &groupName : groupNames) {
+ const CryptoConfigGroup *group = comp ? comp->group(groupName) : nullptr;
+ if (CryptoConfigEntry *entry = group->entry(entryName)) {
+ return entry;
+ }
+ }
+ return nullptr;
+}
+
+QGpgME::CryptoConfigEntry *CryptoConfig::entry(const QString &componentName, const QString &groupName, const QString &entryName) const
+{
+ const CryptoConfigComponent *comp = component(componentName);
+ const CryptoConfigGroup *group = comp ? comp->group(groupName) : nullptr;
+ return group ? group->entry(entryName) : nullptr;
+}
diff --git a/lang/qt/src/cryptoconfig.h b/lang/qt/src/cryptoconfig.h
index d7aae9d..651e2af 100644
--- a/lang/qt/src/cryptoconfig.h
+++ b/lang/qt/src/cryptoconfig.h
@@ -373,18 +373,26 @@ public:
/**
* Convenience method to get hold of a single configuration entry when
- * its component, group and name are known. This can be used to read
+ * its component and name are known. This can be used to read
* the value and/or to set a value to it.
*
* @return the configuration object for a single configuration entry, 0 if not found.
* The object is owned by CryptoConfig, don't delete it.
*/
- CryptoConfigEntry *entry(const QString &componentName, const QString &groupName, const QString &entryName) const
- {
- const QGpgME::CryptoConfigComponent *comp = component(componentName);
- const QGpgME::CryptoConfigGroup *group = comp ? comp->group(groupName) : nullptr;
- return group ? group->entry(entryName) : nullptr;
- }
+ CryptoConfigEntry *entry(const QString &componentName, const QString &entryName) const;
+
+ /**
+ * This function is obsolete. It is provided to keep old source code working.
+ * We strongly advise against using it in new code.
+ *
+ * This function overloads @ref entry().
+ *
+ * Use the entry overload that does not require a group name instead. The group name
+ * is not needed to identify a configuration entry because it only provides logical
+ * grouping for user interfaces. Sometimes configuration entries are moved to different
+ * groups to improve usability.
+ */
+ QGPGME_DEPRECATED CryptoConfigEntry *entry(const QString &componentName, const QString &groupName, const QString &entryName) const;
/**
* Write back changes
diff --git a/lang/qt/src/dataprovider.cpp b/lang/qt/src/dataprovider.cpp
index a025a03..820ccbb 100644
--- a/lang/qt/src/dataprovider.cpp
+++ b/lang/qt/src/dataprovider.cpp
@@ -248,7 +248,17 @@ ssize_t QIODeviceDataProvider::write(const void *buffer, size_t bufSize)
return -1;
}
- return mIO->write(static_cast<const char *>(buffer), bufSize);
+ ssize_t ret = mIO->write(static_cast<const char *>(buffer), bufSize);
+ if (mHaveQProcess) {
+ /* XXX: With at least Qt 5.12 we have the problem that the acutal write
+ * would be triggered by an event / slot. So as we have moved the io
+ * device to our thread this is never triggered until the job is finished
+ * calling waitForBytesWritten internally triggers a _q_canWrite which will
+ * actually write. This is what we want as we want to stream and not to
+ * buffer endlessly. */
+ qobject_cast<QProcess *>(mIO.get())->waitForBytesWritten(0);
+ }
+ return ret;
}
off_t QIODeviceDataProvider::seek(off_t offset, int whence)
diff --git a/lang/qt/src/qgpgme_debug.cpp b/lang/qt/src/qgpgme_debug.cpp
index 4ed859e..513a9b0 100644
--- a/lang/qt/src/qgpgme_debug.cpp
+++ b/lang/qt/src/qgpgme_debug.cpp
@@ -1,5 +1,3 @@
-// This file is autogenerated by CMake: DO NOT EDIT
-
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
@@ -9,6 +7,8 @@
#if QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)
Q_LOGGING_CATEGORY(QGPGME_LOG, "gpg.qgpgme", QtWarningMsg)
+Q_LOGGING_CATEGORY(QGPGME_CONFIG_LOADING_LOG, "gpg.qgpgme.config_loading", QtInfoMsg)
#else
Q_LOGGING_CATEGORY(QGPGME_LOG, "gpg.qgpgme")
+Q_LOGGING_CATEGORY(QGPGME_CONFIG_LOADING_LOG, "gpg.qgpgme.config_loading")
#endif
diff --git a/lang/qt/src/qgpgme_debug.h b/lang/qt/src/qgpgme_debug.h
index 38c16ad..9cdd1a8 100644
--- a/lang/qt/src/qgpgme_debug.h
+++ b/lang/qt/src/qgpgme_debug.h
@@ -1,11 +1,10 @@
-// This file is autogenerated by CMake: DO NOT EDIT
-
-#ifndef QGPGME_LOG_H
-#define QGPGME_LOG_H
+#ifndef QGPGME_QGPGME_DEBUG_H
+#define QGPGME_QGPGME_DEBUG_H
#include <QLoggingCategory>
Q_DECLARE_LOGGING_CATEGORY(QGPGME_LOG)
+Q_DECLARE_LOGGING_CATEGORY(QGPGME_CONFIG_LOADING_LOG)
#endif
diff --git a/lang/qt/src/qgpgmenewcryptoconfig.cpp b/lang/qt/src/qgpgmenewcryptoconfig.cpp
index fba20fa..b93db76 100644
--- a/lang/qt/src/qgpgmenewcryptoconfig.cpp
+++ b/lang/qt/src/qgpgmenewcryptoconfig.cpp
@@ -46,7 +46,7 @@
#include "global.h"
#include "error.h"
-
+#include "debug.h"
#include <sstream>
#include <string>
@@ -101,7 +101,7 @@ void QGpgMENewCryptoConfig::reloadConfiguration(bool)
<< "components:\n";
std::copy(components.begin(), components.end(),
std::ostream_iterator<Component>(ss, "\n"));
- qCDebug(QGPGME_LOG) << ss.str().c_str();
+ qCDebug(QGPGME_CONFIG_LOADING_LOG) << ss.str().c_str();
}
#endif
#if 0
@@ -468,50 +468,74 @@ static QUrl parseURL(int mRealArgType, const QString &str)
{
if (mRealArgType == 33) { // LDAP server
// The format is HOSTNAME:PORT:USERNAME:PASSWORD:BASE_DN
- QStringList items = str.split(QLatin1Char(':'));
- if (items.count() == 5) {
- QStringList::const_iterator it = items.constBegin();
- QUrl url;
- url.setScheme(QStringLiteral("ldap"));
- url.setHost(urlpart_decode(*it++));
-
- bool ok;
- const int port = (*it++).toInt(&ok);
- if (ok) {
- url.setPort(port);
- } else if (!it->isEmpty()) {
- qCWarning(QGPGME_LOG) << "parseURL: malformed LDAP server port, ignoring: \"" << *it << "\"";
- }
+ // or, since gpg 2.2.18, e.g. for dirmngr/ldapserver: [ldap:]hostname:port:username:password:base_dn:flags[:]
+ const bool isLdapUrl = str.startsWith(QLatin1String("ldap://")) || str.startsWith(QLatin1String("ldaps://"));
+ if (!isLdapUrl) {
+ const bool hasOptionalPrefix = str.startsWith(QLatin1String("ldap:"));
+ const QStringList items = hasOptionalPrefix ? str.mid(5).split(QLatin1Char(':')) : str.split(QLatin1Char(':'));
+ if (items.size() >= 5) {
+ QUrl url;
+ url.setScheme(QStringLiteral("ldap"));
+ url.setHost(urlpart_decode(items[0]), QUrl::DecodedMode);
+
+ const auto portString = items[1];
+ if (!portString.isEmpty()) {
+ bool ok;
+ const int port = portString.toInt(&ok);
+ if (ok) {
+ url.setPort(port);
+ } else {
+ qCWarning(QGPGME_LOG) << "parseURL: malformed LDAP server port, ignoring:" << portString;
+ }
+ }
- const QString userName = urlpart_decode(*it++);
- if (!userName.isEmpty()) {
- url.setUserName(userName);
- }
- const QString passWord = urlpart_decode(*it++);
- if (!passWord.isEmpty()) {
- url.setPassword(passWord);
+ const QString userName = urlpart_decode(items[2]);
+ if (!userName.isEmpty()) {
+ url.setUserName(userName, QUrl::DecodedMode);
+ }
+ const QString passWord = urlpart_decode(items[3]);
+ if (!passWord.isEmpty()) {
+ url.setPassword(passWord, QUrl::DecodedMode);
+ }
+ const auto baseDn = urlpart_decode(items[4]);
+ if (!baseDn.isEmpty()) {
+ url.setQuery(baseDn, QUrl::DecodedMode);
+ }
+ if (items.size() >= 6) {
+ const auto flags = urlpart_decode(items[5]);
+ if (!flags.isEmpty()) {
+ url.setFragment(flags, QUrl::DecodedMode);
+ }
+ }
+ return url;
+ } else {
+ qCWarning(QGPGME_LOG) << "parseURL: malformed LDAP server:" << str;
}
- url.setQuery(urlpart_decode(*it));
- return url;
- } else {
- qCWarning(QGPGME_LOG) << "parseURL: malformed LDAP server:" << str;
}
}
// other URLs : assume wellformed URL syntax.
return QUrl(str);
}
+static QString portToString(int port)
+{
+ // -1 is used for default ports => empty string
+ return port != -1 ? QString::number(port) : QString();
+}
+
// The opposite of parseURL
static QString splitURL(int mRealArgType, const QUrl &url)
{
if (mRealArgType == 33) { // LDAP server
// The format is HOSTNAME:PORT:USERNAME:PASSWORD:BASE_DN
+ // or, since gpg 2.2.18, e.g. for dirmngr/ldapserver: [ldap:]hostname:port:username:password:base_dn:flags[:]
Q_ASSERT(url.scheme() == QLatin1String("ldap"));
return urlpart_encode(url.host()) + QLatin1Char(':') +
- (url.port() != -1 ? QString::number(url.port()) : QString()) + QLatin1Char(':') + // -1 is used for default ports, omit
+ portToString(url.port()) + QLatin1Char(':') +
urlpart_encode(url.userName()) + QLatin1Char(':') +
urlpart_encode(url.password()) + QLatin1Char(':') +
- urlpart_encode(url.query());
+ urlpart_encode(url.query()) + QLatin1Char(':') +
+ urlpart_encode(url.fragment());
}
return url.path();
}
@@ -675,7 +699,10 @@ void QGpgMENewCryptoConfigEntry::setURLValueList(const QList<QUrl> &urls)
} else {
values.push_back(splitURL(type, url).toUtf8().constData());
}
- m_option.setNewValue(m_option.createStringListArgument(values));
+ const auto err = m_option.setNewValue(m_option.createStringListArgument(values));
+ if (err) {
+ qCWarning(QGPGME_LOG) << "setURLValueList: failed to set new value:" << err;
+ }
}
bool QGpgMENewCryptoConfigEntry::isDirty() const
diff --git a/lang/qt/src/qgpgmesignkeyjob.cpp b/lang/qt/src/qgpgmesignkeyjob.cpp
index ee0c537..5036a9b 100644
--- a/lang/qt/src/qgpgmesignkeyjob.cpp
+++ b/lang/qt/src/qgpgmesignkeyjob.cpp
@@ -38,27 +38,51 @@
#include "qgpgmesignkeyjob.h"
+#include <QDate>
+#include <QString>
+
#include "dataprovider.h"
#include "context.h"
#include "data.h"
#include "gpgsignkeyeditinteractor.h"
+#include "qgpgme_debug.h"
+
#include <cassert>
-#include <memory>
using namespace QGpgME;
using namespace GpgME;
+namespace
+{
+struct TrustSignatureProperties {
+ TrustSignatureTrust trust = TrustSignatureTrust::None;
+ unsigned int depth = 0;
+ QString scope;
+};
+}
+
+class QGpgMESignKeyJob::Private
+{
+public:
+ Private() = default;
+
+ std::vector<unsigned int> m_userIDsToSign;
+ GpgME::Key m_signingKey;
+ unsigned int m_checkLevel = 0;
+ bool m_exportable = false;
+ bool m_nonRevocable = false;
+ bool m_started = false;
+ bool m_dupeOk = false;
+ QString m_remark;
+ TrustSignatureProperties m_trustSignature;
+ QDate m_expiration;
+};
+
QGpgMESignKeyJob::QGpgMESignKeyJob(Context *context)
- : mixin_type(context),
- m_userIDsToSign(),
- m_signingKey(),
- m_checkLevel(0),
- m_exportable(false),
- m_nonRevocable(false),
- m_started(false),
- m_dupeOk(false)
+ : mixin_type(context)
+ , d{std::unique_ptr<Private>(new Private())}
{
lateInitialization();
}
@@ -67,7 +91,9 @@ QGpgMESignKeyJob::~QGpgMESignKeyJob() {}
static QGpgMESignKeyJob::result_type sign_key(Context *ctx, const Key &key, const std::vector<unsigned int> &uids,
unsigned int checkLevel, const Key &signer, unsigned int opts,
- bool dupeOk, const QString &remark)
+ bool dupeOk, const QString &remark,
+ const TrustSignatureProperties &trustSignature,
+ const QDate &expirationDate)
{
QGpgME::QByteArrayDataProvider dp;
Data data(&dp);
@@ -87,10 +113,38 @@ static QGpgMESignKeyJob::result_type sign_key(Context *ctx, const Key &key, cons
ctx->addSignatureNotation("rem@gnupg.org", remark.toUtf8().constData());
}
- if (!signer.isNull())
+ if (opts & GpgSignKeyEditInteractor::Trust) {
+ skei->setTrustSignatureTrust(trustSignature.trust);
+ skei->setTrustSignatureDepth(trustSignature.depth);
+ skei->setTrustSignatureScope(trustSignature.scope.toUtf8().toStdString());
+ }
+
+ if (!signer.isNull()) {
if (const Error err = ctx->addSigningKey(signer)) {
return std::make_tuple(err, QString(), Error());
}
+ }
+
+ if (expirationDate.isValid()) {
+ // on 2106-02-07, the Unix time will reach 0xFFFFFFFF; since gpg uses uint32 internally
+ // for the expiration date clip it at 2106-02-06
+ static const QDate maxAllowedDate{2106, 2, 6};
+ const auto clippedExpirationDate = expirationDate <= maxAllowedDate ? expirationDate : maxAllowedDate;
+ if (clippedExpirationDate != expirationDate) {
+ qCWarning(QGPGME_LOG) << "Expiration of certification has been changed to" << clippedExpirationDate;
+ }
+ // use the "days from now" format to specify the expiration date of the certification;
+ // this format is the most appropriate regardless of the local timezone
+ const auto daysFromNow = QDate::currentDate().daysTo(clippedExpirationDate);
+ if (daysFromNow > 0) {
+ const auto certExpire = std::to_string(daysFromNow) + "d";
+ ctx->setFlag("cert-expire", certExpire.c_str());
+ }
+ } else {
+ // explicitly set "cert-expire" to "0" (no expiration) to override default-cert-expire set in gpg.conf
+ ctx->setFlag("cert-expire", "0");
+ }
+
const Error err = ctx->edit(key, std::unique_ptr<EditInteractor> (skei), data);
Error ae;
const QString log = _detail::audit_log_as_html(ctx, ae);
@@ -100,57 +154,80 @@ static QGpgMESignKeyJob::result_type sign_key(Context *ctx, const Key &key, cons
Error QGpgMESignKeyJob::start(const Key &key)
{
unsigned int opts = 0;
- if (m_nonRevocable) {
+ if (d->m_nonRevocable) {
opts |= GpgSignKeyEditInteractor::NonRevocable;
}
- if (m_exportable) {
+ if (d->m_exportable) {
opts |= GpgSignKeyEditInteractor::Exportable;
}
- run(std::bind(&sign_key, std::placeholders::_1, key, m_userIDsToSign, m_checkLevel, m_signingKey, opts,
- m_dupeOk, m_remark));
- m_started = true;
+ switch (d->m_trustSignature.trust) {
+ case TrustSignatureTrust::Partial:
+ case TrustSignatureTrust::Complete:
+ opts |= GpgSignKeyEditInteractor::Trust;
+ break;
+ default:
+ opts &= ~GpgSignKeyEditInteractor::Trust;
+ break;
+ }
+ run(std::bind(&sign_key, std::placeholders::_1, key, d->m_userIDsToSign, d->m_checkLevel, d->m_signingKey,
+ opts, d->m_dupeOk, d->m_remark, d->m_trustSignature, d->m_expiration));
+ d->m_started = true;
return Error();
}
void QGpgMESignKeyJob::setUserIDsToSign(const std::vector<unsigned int> &idsToSign)
{
- assert(!m_started);
- m_userIDsToSign = idsToSign;
+ assert(!d->m_started);
+ d->m_userIDsToSign = idsToSign;
}
void QGpgMESignKeyJob::setCheckLevel(unsigned int checkLevel)
{
- assert(!m_started);
- m_checkLevel = checkLevel;
+ assert(!d->m_started);
+ d->m_checkLevel = checkLevel;
}
void QGpgMESignKeyJob::setExportable(bool exportable)
{
- assert(!m_started);
- m_exportable = exportable;
+ assert(!d->m_started);
+ d->m_exportable = exportable;
}
void QGpgMESignKeyJob::setSigningKey(const Key &key)
{
- assert(!m_started);
- m_signingKey = key;
+ assert(!d->m_started);
+ d->m_signingKey = key;
}
void QGpgMESignKeyJob::setNonRevocable(bool nonRevocable)
{
- assert(!m_started);
- m_nonRevocable = nonRevocable;
+ assert(!d->m_started);
+ d->m_nonRevocable = nonRevocable;
}
void QGpgMESignKeyJob::setRemark(const QString &remark)
{
- assert(!m_started);
- m_remark = remark;
+ assert(!d->m_started);
+ d->m_remark = remark;
}
void QGpgMESignKeyJob::setDupeOk(bool value)
{
- assert(!m_started);
- m_dupeOk = value;
+ assert(!d->m_started);
+ d->m_dupeOk = value;
}
+
+void QGpgMESignKeyJob::setTrustSignature(GpgME::TrustSignatureTrust trust, unsigned short depth, const QString &scope)
+{
+ assert(!d->m_started);
+ assert(depth <= 255);
+ d->m_trustSignature = {trust, depth, scope};
+}
+
+void QGpgMESignKeyJob::setExpirationDate(const QDate &expiration)
+{
+ assert(!d->m_started);
+ d->m_expiration = expiration;
+}
+
#include "qgpgmesignkeyjob.moc"
diff --git a/lang/qt/src/qgpgmesignkeyjob.h b/lang/qt/src/qgpgmesignkeyjob.h
index 9c19c02..2ea9e94 100644
--- a/lang/qt/src/qgpgmesignkeyjob.h
+++ b/lang/qt/src/qgpgmesignkeyjob.h
@@ -39,13 +39,7 @@
#include "threadedjobmixin.h"
-#include <QString>
-
-#ifdef BUILDING_QGPGME
-# include "key.h"
-#else
-#include <gpgme++/key.h>
-#endif
+#include <memory>
namespace QGpgME
{
@@ -90,15 +84,14 @@ public:
/* from SignKeyJob */
void setDupeOk(bool value) Q_DECL_OVERRIDE;
+ /* from SignKeyJob */
+ void setTrustSignature(GpgME::TrustSignatureTrust trust, unsigned short depth, const QString &scope) Q_DECL_OVERRIDE;
+
+ void setExpirationDate(const QDate &expiration) override;
+
private:
- std::vector<unsigned int> m_userIDsToSign;
- GpgME::Key m_signingKey;
- unsigned int m_checkLevel;
- bool m_exportable;
- bool m_nonRevocable;
- bool m_started;
- bool m_dupeOk;
- QString m_remark;
+ class Private;
+ std::unique_ptr<Private> d;
};
}
diff --git a/lang/qt/src/signkeyjob.h b/lang/qt/src/signkeyjob.h
index e3ae75f..666af92 100644
--- a/lang/qt/src/signkeyjob.h
+++ b/lang/qt/src/signkeyjob.h
@@ -43,8 +43,10 @@ namespace GpgME
{
class Error;
class Key;
+enum class TrustSignatureTrust : char;
}
+class QDate;
class QString;
namespace QGpgME
@@ -74,22 +76,19 @@ public:
/**
Starts the key signing operation. \a key is the key to sign.
@param keyToSign the key to be signed
- @param idsToSign the user IDs to sign
- @param signingKey the secret key to use for signing
- @param option the signing mode, either local or exportable
*/
virtual GpgME::Error start(const GpgME::Key &keyToSign) = 0;
/**
* If explicitly specified, only the listed user IDs will be signed. Otherwise all user IDs
* are signed.
- * @param list of user ID indexes (of the key to be signed).
+ * @param idsToSign list of user ID indexes (of the key to be signed).
*/
virtual void setUserIDsToSign(const std::vector<unsigned int> &idsToSign) = 0;
/**
* sets the check level
- * @param the check level, ranges from 0 (no claim) and 3 (extensively checked),
+ * @param checkLevel the check level, ranges from 0 (no claim) and 3 (extensively checked),
* default is 0
*/
virtual void setCheckLevel(unsigned int checkLevel) = 0;
@@ -127,6 +126,35 @@ public:
**/
virtual void setRemark(const QString &) {};
+ /**
+ * If set, then the created signature will be a trust signature. By default,
+ * no trust signatures are created.
+ *
+ * @a trust is the amount of trust to put into the signed key, either
+ * @c TrustSignatureTrust::Partial or @c TrustSignatureTrust::Complete.
+ * @a depth is the level of the trust signature. Values between 0 and 255 are
+ * allowed. Level 0 has the same meaning as an ordinary validity signature.
+ * Level 1 means that the signed key is asserted to be a valid trusted
+ * introducer. Level n >= 2 means that the signed key is asserted to be
+ * trusted to issue level n-1 trust signatures, i.e., that it is a "meta
+ * introducer".
+ * @a scope is a domain name that limits the scope of trust of the signed key
+ * to user IDs with email addresses matching the domain (or a subdomain).
+ *
+ * Not pure virtual for ABI compatibility.
+ **/
+ virtual void setTrustSignature(GpgME::TrustSignatureTrust trust, unsigned short depth, const QString &scope) { Q_UNUSED(trust); Q_UNUSED(depth); Q_UNUSED(scope); };
+
+ /**
+ * Sets the expiration date of the key signature to @a expiration. By default,
+ * key signatures do not expire.
+ *
+ * Note: Expiration dates after 2106-02-06 will be set to 2106-02-06.
+ *
+ * Not pure virtual for ABI compatibility.
+ **/
+ virtual void setExpirationDate(const QDate &expiration) { Q_UNUSED(expiration); }
+
Q_SIGNALS:
void result(const GpgME::Error &result, const QString &auditLogAsHtml = QString(), const GpgME::Error &auditLogError = GpgME::Error());
};
diff --git a/lang/qt/tests/Makefile.am b/lang/qt/tests/Makefile.am
index ace5082..41ea808 100644
--- a/lang/qt/tests/Makefile.am
+++ b/lang/qt/tests/Makefile.am
@@ -27,11 +27,11 @@ TESTS_ENVIRONMENT = GNUPGHOME=$(GNUPGHOME)
EXTRA_DIST = initial.test
TESTS = initial.test t-keylist t-keylocate t-ownertrust t-tofuinfo \
- t-encrypt t-verify t-various t-config t-remarks
+ t-encrypt t-verify t-various t-config t-remarks t-trustsignatures
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-various.moc t-config.moc t-remarks.moc t-trustsignatures.moc
AM_LDFLAGS = -no-install
@@ -57,6 +57,7 @@ t_verify_SOURCES = t-verify.cpp $(support_src)
t_various_SOURCES = t-various.cpp $(support_src)
t_config_SOURCES = t-config.cpp $(support_src)
t_remarks_SOURCES = t-remarks.cpp $(support_src)
+t_trustsignatures_SOURCES = t-trustsignatures.cpp $(support_src)
run_keyformailboxjob_SOURCES = run-keyformailboxjob.cpp
nodist_t_keylist_SOURCES = $(moc_files)
@@ -64,7 +65,8 @@ 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
+ run-keyformailboxjob t-wkspublish t-verify t-various t-config t-remarks \
+ t-trustsignatures
CLEANFILES = secring.gpg pubring.gpg pubring.kbx trustdb.gpg dirmngr.conf \
gpg-agent.conf pubring.kbx~ S.gpg-agent gpg.conf pubring.gpg~ \
diff --git a/lang/qt/tests/Makefile.in b/lang/qt/tests/Makefile.in
index dd7f342..2b0e0ee 100644
--- a/lang/qt/tests/Makefile.in
+++ b/lang/qt/tests/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -110,12 +110,12 @@ host_triplet = @host@
TESTS = initial.test t-keylist$(EXEEXT) t-keylocate$(EXEEXT) \
t-ownertrust$(EXEEXT) t-tofuinfo$(EXEEXT) t-encrypt$(EXEEXT) \
t-verify$(EXEEXT) t-various$(EXEEXT) t-config$(EXEEXT) \
- t-remarks$(EXEEXT)
+ t-remarks$(EXEEXT) t-trustsignatures$(EXEEXT)
noinst_PROGRAMS = t-keylist$(EXEEXT) t-keylocate$(EXEEXT) \
t-ownertrust$(EXEEXT) t-tofuinfo$(EXEEXT) t-encrypt$(EXEEXT) \
run-keyformailboxjob$(EXEEXT) t-wkspublish$(EXEEXT) \
t-verify$(EXEEXT) t-various$(EXEEXT) t-config$(EXEEXT) \
- t-remarks$(EXEEXT)
+ t-remarks$(EXEEXT) t-trustsignatures$(EXEEXT)
subdir = lang/qt/tests
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
@@ -185,6 +185,12 @@ t_tofuinfo_OBJECTS = $(am_t_tofuinfo_OBJECTS)
t_tofuinfo_LDADD = $(LDADD)
t_tofuinfo_DEPENDENCIES = ../../cpp/src/libgpgmepp.la \
../src/libqgpgme.la ../../../src/libgpgme.la
+am_t_trustsignatures_OBJECTS = t-trustsignatures.$(OBJEXT) \
+ $(am__objects_1)
+t_trustsignatures_OBJECTS = $(am_t_trustsignatures_OBJECTS)
+t_trustsignatures_LDADD = $(LDADD)
+t_trustsignatures_DEPENDENCIES = ../../cpp/src/libgpgmepp.la \
+ ../src/libqgpgme.la ../../../src/libgpgme.la
am_t_various_OBJECTS = t-various.$(OBJEXT) $(am__objects_1)
t_various_OBJECTS = $(am_t_various_OBJECTS)
t_various_LDADD = $(LDADD)
@@ -220,8 +226,8 @@ am__depfiles_remade = ./$(DEPDIR)/run-keyformailboxjob.Po \
./$(DEPDIR)/t-keylist.Po ./$(DEPDIR)/t-keylocate.Po \
./$(DEPDIR)/t-ownertrust.Po ./$(DEPDIR)/t-remarks.Po \
./$(DEPDIR)/t-support.Po ./$(DEPDIR)/t-tofuinfo.Po \
- ./$(DEPDIR)/t-various.Po ./$(DEPDIR)/t-verify.Po \
- ./$(DEPDIR)/t-wkspublish.Po
+ ./$(DEPDIR)/t-trustsignatures.Po ./$(DEPDIR)/t-various.Po \
+ ./$(DEPDIR)/t-verify.Po ./$(DEPDIR)/t-wkspublish.Po
am__mv = mv -f
CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
@@ -263,14 +269,15 @@ SOURCES = $(run_keyformailboxjob_SOURCES) $(t_config_SOURCES) \
$(t_encrypt_SOURCES) $(t_keylist_SOURCES) \
$(nodist_t_keylist_SOURCES) $(t_keylocate_SOURCES) \
$(t_ownertrust_SOURCES) $(t_remarks_SOURCES) \
- $(t_tofuinfo_SOURCES) $(t_various_SOURCES) $(t_verify_SOURCES) \
+ $(t_tofuinfo_SOURCES) $(t_trustsignatures_SOURCES) \
+ $(t_various_SOURCES) $(t_verify_SOURCES) \
$(t_wkspublish_SOURCES)
DIST_SOURCES = $(run_keyformailboxjob_SOURCES) $(t_config_SOURCES) \
$(t_encrypt_SOURCES) $(t_keylist_SOURCES) \
$(t_keylocate_SOURCES) $(t_ownertrust_SOURCES) \
$(t_remarks_SOURCES) $(t_tofuinfo_SOURCES) \
- $(t_various_SOURCES) $(t_verify_SOURCES) \
- $(t_wkspublish_SOURCES)
+ $(t_trustsignatures_SOURCES) $(t_various_SOURCES) \
+ $(t_verify_SOURCES) $(t_wkspublish_SOURCES)
am__can_run_installinfo = \
case $$AM_UPDATE_INFO_DIR in \
n|no|NO) false;; \
@@ -525,7 +532,7 @@ TESTS_ENVIRONMENT = GNUPGHOME=$(GNUPGHOME)
EXTRA_DIST = initial.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-various.moc t-config.moc t-remarks.moc t-trustsignatures.moc
AM_LDFLAGS = -no-install
LDADD = ../../cpp/src/libgpgmepp.la ../src/libqgpgme.la \
@@ -549,6 +556,7 @@ t_verify_SOURCES = t-verify.cpp $(support_src)
t_various_SOURCES = t-various.cpp $(support_src)
t_config_SOURCES = t-config.cpp $(support_src)
t_remarks_SOURCES = t-remarks.cpp $(support_src)
+t_trustsignatures_SOURCES = t-trustsignatures.cpp $(support_src)
run_keyformailboxjob_SOURCES = run-keyformailboxjob.cpp
nodist_t_keylist_SOURCES = $(moc_files)
BUILT_SOURCES = $(moc_files) pubring-stamp
@@ -633,6 +641,10 @@ t-tofuinfo$(EXEEXT): $(t_tofuinfo_OBJECTS) $(t_tofuinfo_DEPENDENCIES) $(EXTRA_t_
@rm -f t-tofuinfo$(EXEEXT)
$(AM_V_CXXLD)$(CXXLINK) $(t_tofuinfo_OBJECTS) $(t_tofuinfo_LDADD) $(LIBS)
+t-trustsignatures$(EXEEXT): $(t_trustsignatures_OBJECTS) $(t_trustsignatures_DEPENDENCIES) $(EXTRA_t_trustsignatures_DEPENDENCIES)
+ @rm -f t-trustsignatures$(EXEEXT)
+ $(AM_V_CXXLD)$(CXXLINK) $(t_trustsignatures_OBJECTS) $(t_trustsignatures_LDADD) $(LIBS)
+
t-various$(EXEEXT): $(t_various_OBJECTS) $(t_various_DEPENDENCIES) $(EXTRA_t_various_DEPENDENCIES)
@rm -f t-various$(EXEEXT)
$(AM_V_CXXLD)$(CXXLINK) $(t_various_OBJECTS) $(t_various_LDADD) $(LIBS)
@@ -660,6 +672,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-remarks.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-support.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-tofuinfo.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-trustsignatures.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-various.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-verify.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-wkspublish.Po@am__quote@ # am--include-marker
@@ -883,7 +896,8 @@ all-am: Makefile $(PROGRAMS)
installdirs:
install: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) install-am
-install-exec: install-exec-am
+install-exec: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) install-exec-am
install-data: install-data-am
uninstall: uninstall-am
@@ -929,6 +943,7 @@ distclean: distclean-am
-rm -f ./$(DEPDIR)/t-remarks.Po
-rm -f ./$(DEPDIR)/t-support.Po
-rm -f ./$(DEPDIR)/t-tofuinfo.Po
+ -rm -f ./$(DEPDIR)/t-trustsignatures.Po
-rm -f ./$(DEPDIR)/t-various.Po
-rm -f ./$(DEPDIR)/t-verify.Po
-rm -f ./$(DEPDIR)/t-wkspublish.Po
@@ -986,6 +1001,7 @@ maintainer-clean: maintainer-clean-am
-rm -f ./$(DEPDIR)/t-remarks.Po
-rm -f ./$(DEPDIR)/t-support.Po
-rm -f ./$(DEPDIR)/t-tofuinfo.Po
+ -rm -f ./$(DEPDIR)/t-trustsignatures.Po
-rm -f ./$(DEPDIR)/t-various.Po
-rm -f ./$(DEPDIR)/t-verify.Po
-rm -f ./$(DEPDIR)/t-wkspublish.Po
@@ -1007,7 +1023,8 @@ ps-am:
uninstall-am:
-.MAKE: all check check-am install install-am install-strip
+.MAKE: all check check-am install install-am install-exec \
+ install-strip
.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-TESTS \
check-am clean clean-generic clean-libtool clean-local \
diff --git a/lang/qt/tests/t-trustsignatures.cpp b/lang/qt/tests/t-trustsignatures.cpp
new file mode 100644
index 0000000..d392b2f
--- /dev/null
+++ b/lang/qt/tests/t-trustsignatures.cpp
@@ -0,0 +1,565 @@
+/* t-remarks.cpp
+
+ This file is part of qgpgme, the Qt API binding for gpgme
+ Copyright (c) 2021 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 "context.h"
+#include "engineinfo.h"
+#include "protocol.h"
+#include "signkeyjob.h"
+
+#include <QSignalSpy>
+#include <QTemporaryDir>
+#include <QTest>
+
+using namespace QGpgME;
+using namespace GpgME;
+
+class TestTrustSignatures: public QGpgMETest
+{
+ Q_OBJECT
+
+Q_SIGNALS:
+ void asyncDone();
+
+private Q_SLOTS:
+ void test_tsign_single_uid_key_and_then_tsign_it_again()
+ {
+ Error err;
+
+ if (!loopbackSupported()) {
+ return;
+ }
+
+ auto ctx = Context::create(OpenPGP);
+ QVERIFY(ctx);
+
+ // Get the signing key (alfa@example.net)
+ auto seckey = ctx->key("A0FF4590BB6122EDEF6E3C542D727CC768697734", err, true);
+ QVERIFY(!err);
+ QVERIFY(!seckey.isNull());
+
+ // Get the target key (victor@example.org)
+ auto target = ctx->key("E8143C489C8D41124DC40D0B47AF4B6961F04784", err, false);
+ QVERIFY(!err);
+ QVERIFY(!target.isNull());
+ QVERIFY(target.numUserIDs() > 0);
+
+ // Create first trust signature
+ {
+ // Create the job
+ auto job = std::unique_ptr<SignKeyJob>{openpgp()->signKeyJob()};
+ QVERIFY(job);
+
+ // Hack in the passphrase provider
+ auto jobCtx = Job::context(job.get());
+ TestPassphraseProvider provider;
+ jobCtx->setPassphraseProvider(&provider);
+ jobCtx->setPinentryMode(Context::PinentryLoopback);
+
+ // Setup the job
+ job->setExportable(true);
+ job->setSigningKey(seckey);
+ job->setTrustSignature(TrustSignatureTrust::Complete, 1, QStringLiteral("example.org"));
+
+ connect(job.get(), &SignKeyJob::result,
+ this, [this] (const GpgME::Error &err2, const QString &, const GpgME::Error &) {
+ Q_EMIT asyncDone();
+ if (err2) {
+ if (err2.code() == GPG_ERR_GENERAL) {
+ QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.\n"
+ "Hint: Run with GPGMEPP_INTERACTOR_DEBUG=stderr to debug the edit interaction.").arg(err2.asString())));
+ } else {
+ QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.").arg(err2.asString())));
+ }
+ }
+ });
+
+ job->start(target);
+ QSignalSpy spy (this, SIGNAL(asyncDone()));
+ QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT));
+
+ // At this point the trust signature should have been added.
+ target.update();
+ const auto trustSignature = target.userID(0).signature(target.userID(0).numSignatures() - 1);
+ QVERIFY(trustSignature.isTrustSignature());
+ QCOMPARE(trustSignature.trustDepth(), 1u);
+ QCOMPARE(trustSignature.trustValue(), TrustSignatureTrust::Complete);
+ QVERIFY(trustSignature.trustScope());
+ const auto trustScope = QString::fromUtf8(trustSignature.trustScope());
+ QVERIFY(!trustScope.isEmpty());
+ const QRegExp regex{trustScope};
+ QVERIFY(regex.isValid());
+ QVERIFY(regex.indexIn(QStringLiteral("Foo <foo@example.org>")) != -1);
+ }
+
+ // Create second trust signature
+ {
+ // Create the job
+ auto job = std::unique_ptr<SignKeyJob>{openpgp()->signKeyJob()};
+ QVERIFY(job);
+
+ // Hack in the passphrase provider
+ auto jobCtx = Job::context(job.get());
+ TestPassphraseProvider provider;
+ jobCtx->setPassphraseProvider(&provider);
+ jobCtx->setPinentryMode(Context::PinentryLoopback);
+
+ // Setup the job
+ job->setExportable(true);
+ job->setSigningKey(seckey);
+ job->setDupeOk(true);
+ job->setTrustSignature(TrustSignatureTrust::Partial, 2, QStringLiteral("example.net"));
+
+ connect(job.get(), &SignKeyJob::result,
+ this, [this] (const GpgME::Error &err2, const QString &, const GpgME::Error &) {
+ Q_EMIT asyncDone();
+ if (err2) {
+ if (err2.code() == GPG_ERR_GENERAL) {
+ QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.\n"
+ "Hint: Run with GPGMEPP_INTERACTOR_DEBUG=stderr to debug the edit interaction.").arg(err2.asString())));
+ } else {
+ QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.").arg(err2.asString())));
+ }
+ }
+ });
+
+ err = job->start(target);
+ QVERIFY(!err);
+ QSignalSpy spy (this, SIGNAL(asyncDone()));
+ QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT));
+
+ // At this point the trust signature should have been added.
+ target.update();
+ const auto trustSignature = target.userID(0).signature(target.userID(0).numSignatures() - 1);
+ QVERIFY(trustSignature.isTrustSignature());
+ QCOMPARE(trustSignature.trustDepth(), 2u);
+ QCOMPARE(trustSignature.trustValue(), TrustSignatureTrust::Partial);
+ QVERIFY(trustSignature.trustScope());
+ const auto trustScope = QString::fromUtf8(trustSignature.trustScope());
+ QVERIFY(!trustScope.isEmpty());
+ const QRegExp regex{trustScope};
+ QVERIFY(regex.isValid());
+ QVERIFY(regex.indexIn(QStringLiteral("Foo <foo@example.net>")) != -1);
+ }
+ }
+
+ void test_tsign_multi_uid_key_and_then_tsign_it_again()
+ {
+ Error err;
+
+ if (!loopbackSupported()) {
+ return;
+ }
+
+ auto ctx = Context::create(OpenPGP);
+ QVERIFY(ctx);
+
+ // Get the signing key (alfa@example.net)
+ auto seckey = ctx->key("A0FF4590BB6122EDEF6E3C542D727CC768697734", err, true);
+ QVERIFY(!err);
+ QVERIFY(!seckey.isNull());
+
+ // Get the target key (Bob / Bravo Test)
+ auto target = ctx->key("D695676BDCEDCC2CDD6152BCFE180B1DA9E3B0B2", err, false);
+ QVERIFY(!err);
+ QVERIFY(!target.isNull());
+ QVERIFY(target.numUserIDs() > 0);
+
+ // Create first trust signature
+ {
+ // Create the job
+ auto job = openpgp()->signKeyJob();//std::unique_ptr<SignKeyJob>{openpgp()->signKeyJob()};
+ QVERIFY(job);
+
+ // Hack in the passphrase provider
+ auto jobCtx = Job::context(job);
+ TestPassphraseProvider provider;
+ jobCtx->setPassphraseProvider(&provider);
+ jobCtx->setPinentryMode(Context::PinentryLoopback);
+
+ // Setup the job
+ job->setExportable(true);
+ job->setSigningKey(seckey);
+ job->setTrustSignature(TrustSignatureTrust::Complete, 1, QStringLiteral("example.org"));
+
+ connect(job, &SignKeyJob::result,
+ this, [this] (const GpgME::Error &err2, const QString &, const GpgME::Error &) {
+ Q_EMIT asyncDone();
+ if (err2) {
+ if (err2.code() == GPG_ERR_GENERAL) {
+ QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.\n"
+ "Hint: Run with GPGMEPP_INTERACTOR_DEBUG=stderr to debug the edit interaction.").arg(err2.asString())));
+ } else {
+ QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.").arg(err2.asString())));
+ }
+ }
+ });
+
+ job->start(target);
+ QSignalSpy spy (this, SIGNAL(asyncDone()));
+ QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT));
+
+ // At this point the trust signature should have been added.
+ target.update();
+ const auto trustSignature = target.userID(0).signature(target.userID(0).numSignatures() - 1);
+ QVERIFY(trustSignature.isTrustSignature());
+ QCOMPARE(trustSignature.trustDepth(), 1u);
+ QCOMPARE(trustSignature.trustValue(), TrustSignatureTrust::Complete);
+ QVERIFY(trustSignature.trustScope());
+ const auto trustScope = QString::fromUtf8(trustSignature.trustScope());
+ QVERIFY(!trustScope.isEmpty());
+ const QRegExp regex{trustScope};
+ QVERIFY(regex.isValid());
+ QVERIFY(regex.indexIn(QStringLiteral("Foo <foo@example.org>")) != -1);
+ }
+
+ // Create second trust signature
+ {
+ // Create the job
+ auto job = openpgp()->signKeyJob();//std::unique_ptr<SignKeyJob>{openpgp()->signKeyJob()};
+ QVERIFY(job);
+
+ // Hack in the passphrase provider
+ auto jobCtx = Job::context(job);
+ TestPassphraseProvider provider;
+ jobCtx->setPassphraseProvider(&provider);
+ jobCtx->setPinentryMode(Context::PinentryLoopback);
+
+ // Setup the job
+ job->setExportable(true);
+ job->setSigningKey(seckey);
+ job->setDupeOk(true);
+ job->setTrustSignature(TrustSignatureTrust::Partial, 2, QStringLiteral("example.net"));
+
+ connect(job, &SignKeyJob::result,
+ this, [this] (const GpgME::Error &err2, const QString &, const GpgME::Error &) {
+ Q_EMIT asyncDone();
+ if (err2) {
+ if (err2.code() == GPG_ERR_GENERAL) {
+ QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.\n"
+ "Hint: Run with GPGMEPP_INTERACTOR_DEBUG=stderr to debug the edit interaction.").arg(err2.asString())));
+ } else {
+ QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.").arg(err2.asString())));
+ }
+ }
+ });
+
+ err = job->start(target);
+ QVERIFY(!err);
+ QSignalSpy spy (this, SIGNAL(asyncDone()));
+ QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT));
+
+ // At this point the trust signature should have been added.
+ target.update();
+ const auto trustSignature = target.userID(0).signature(target.userID(0).numSignatures() - 1);
+ QVERIFY(trustSignature.isTrustSignature());
+ QCOMPARE(trustSignature.trustDepth(), 2u);
+ QCOMPARE(trustSignature.trustValue(), TrustSignatureTrust::Partial);
+ QVERIFY(trustSignature.trustScope());
+ const auto trustScope = QString::fromUtf8(trustSignature.trustScope());
+ QVERIFY(!trustScope.isEmpty());
+ const QRegExp regex{trustScope};
+ QVERIFY(regex.isValid());
+ QVERIFY(regex.indexIn(QStringLiteral("Foo <foo@example.net>")) != -1);
+ }
+ }
+
+ void test_tsign_first_uid_and_then_tsign_both_uids()
+ {
+ Error err;
+
+ if (!loopbackSupported()) {
+ return;
+ }
+
+ auto ctx = Context::create(OpenPGP);
+ QVERIFY(ctx);
+
+ // Get the signing key (alfa@example.net)
+ auto seckey = ctx->key("A0FF4590BB6122EDEF6E3C542D727CC768697734", err, true);
+ QVERIFY(!err);
+ QVERIFY(!seckey.isNull());
+
+ // Get the target key (Mallory / Mike Test)
+ auto target = ctx->key("2686AA191A278013992C72EBBE794852BE5CF886", err, false);
+ QVERIFY(!err);
+ QVERIFY(!target.isNull());
+ QVERIFY(target.numUserIDs() > 0);
+
+ // Create first trust signature
+ {
+ // Create the job
+ auto job = openpgp()->signKeyJob();//std::unique_ptr<SignKeyJob>{openpgp()->signKeyJob()};
+ QVERIFY(job);
+
+ // Hack in the passphrase provider
+ auto jobCtx = Job::context(job);
+ TestPassphraseProvider provider;
+ jobCtx->setPassphraseProvider(&provider);
+ jobCtx->setPinentryMode(Context::PinentryLoopback);
+
+ // Setup the job
+ job->setExportable(true);
+ job->setSigningKey(seckey);
+ job->setUserIDsToSign({0});
+ job->setTrustSignature(TrustSignatureTrust::Complete, 1, QStringLiteral("example.org"));
+
+ connect(job, &SignKeyJob::result,
+ this, [this] (const GpgME::Error &err2, const QString &, const GpgME::Error &) {
+ Q_EMIT asyncDone();
+ if (err2) {
+ if (err2.code() == GPG_ERR_GENERAL) {
+ QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.\n"
+ "Hint: Run with GPGMEPP_INTERACTOR_DEBUG=stderr to debug the edit interaction.").arg(err2.asString())));
+ } else {
+ QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.").arg(err2.asString())));
+ }
+ }
+ });
+
+ job->start(target);
+ QSignalSpy spy (this, SIGNAL(asyncDone()));
+ QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT));
+
+ // At this point the trust signature should have been added.
+ target.update();
+ const auto trustSignature = target.userID(0).signature(target.userID(0).numSignatures() - 1);
+ QVERIFY(trustSignature.isTrustSignature());
+ QCOMPARE(trustSignature.trustDepth(), 1u);
+ QCOMPARE(trustSignature.trustValue(), TrustSignatureTrust::Complete);
+ QVERIFY(trustSignature.trustScope());
+ const auto trustScope = QString::fromUtf8(trustSignature.trustScope());
+ QVERIFY(!trustScope.isEmpty());
+ const QRegExp regex{trustScope};
+ QVERIFY(regex.isValid());
+ QVERIFY(regex.indexIn(QStringLiteral("Foo <foo@example.org>")) != -1);
+ }
+
+ // Create second trust signature
+ {
+ // Create the job
+ auto job = openpgp()->signKeyJob();//std::unique_ptr<SignKeyJob>{openpgp()->signKeyJob()};
+ QVERIFY(job);
+
+ // Hack in the passphrase provider
+ auto jobCtx = Job::context(job);
+ TestPassphraseProvider provider;
+ jobCtx->setPassphraseProvider(&provider);
+ jobCtx->setPinentryMode(Context::PinentryLoopback);
+
+ // Setup the job
+ job->setExportable(true);
+ job->setSigningKey(seckey);
+ job->setDupeOk(true);
+ job->setTrustSignature(TrustSignatureTrust::Partial, 2, QStringLiteral("example.net"));
+
+ connect(job, &SignKeyJob::result,
+ this, [this] (const GpgME::Error &err2, const QString &, const GpgME::Error &) {
+ Q_EMIT asyncDone();
+ if (err2) {
+ if (err2.code() == GPG_ERR_GENERAL) {
+ QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.\n"
+ "Hint: Run with GPGMEPP_INTERACTOR_DEBUG=stderr to debug the edit interaction.").arg(err2.asString())));
+ } else {
+ QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.").arg(err2.asString())));
+ }
+ }
+ });
+
+ err = job->start(target);
+ QVERIFY(!err);
+ QSignalSpy spy (this, SIGNAL(asyncDone()));
+ QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT));
+
+ // At this point the trust signature should have been added.
+ target.update();
+ const auto trustSignature = target.userID(0).signature(target.userID(0).numSignatures() - 1);
+ QVERIFY(trustSignature.isTrustSignature());
+ QCOMPARE(trustSignature.trustDepth(), 2u);
+ QCOMPARE(trustSignature.trustValue(), TrustSignatureTrust::Partial);
+ QVERIFY(trustSignature.trustScope());
+ const auto trustScope = QString::fromUtf8(trustSignature.trustScope());
+ QVERIFY(!trustScope.isEmpty());
+ const QRegExp regex{trustScope};
+ QVERIFY(regex.isValid());
+ QVERIFY(regex.indexIn(QStringLiteral("Foo <foo@example.net>")) != -1);
+ }
+ }
+
+ void test_tsign_all_uids_and_then_tsign_first_uid()
+ {
+ Error err;
+
+ if (!loopbackSupported()) {
+ return;
+ }
+
+ auto ctx = Context::create(OpenPGP);
+ QVERIFY(ctx);
+
+ // Get the signing key (alfa@example.net)
+ auto seckey = ctx->key("A0FF4590BB6122EDEF6E3C542D727CC768697734", err, true);
+ QVERIFY(!err);
+ QVERIFY(!seckey.isNull());
+
+ // Get the target key (Echelon / Echo Test / Eve)
+ auto target = ctx->key("3531152DE293E26A07F504BC318C1FAEFAEF6D1B", err, false);
+ QVERIFY(!err);
+ QVERIFY(!target.isNull());
+ QVERIFY(target.numUserIDs() > 0);
+
+ // Create first trust signature
+ {
+ // Create the job
+ auto job = openpgp()->signKeyJob();//std::unique_ptr<SignKeyJob>{openpgp()->signKeyJob()};
+ QVERIFY(job);
+
+ // Hack in the passphrase provider
+ auto jobCtx = Job::context(job);
+ TestPassphraseProvider provider;
+ jobCtx->setPassphraseProvider(&provider);
+ jobCtx->setPinentryMode(Context::PinentryLoopback);
+
+ // Setup the job
+ job->setExportable(true);
+ job->setSigningKey(seckey);
+ job->setTrustSignature(TrustSignatureTrust::Complete, 1, QStringLiteral("example.org"));
+
+ connect(job, &SignKeyJob::result,
+ this, [this] (const GpgME::Error &err2, const QString &, const GpgME::Error &) {
+ Q_EMIT asyncDone();
+ if (err2) {
+ if (err2.code() == GPG_ERR_GENERAL) {
+ QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.\n"
+ "Hint: Run with GPGMEPP_INTERACTOR_DEBUG=stderr to debug the edit interaction.").arg(err2.asString())));
+ } else {
+ QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.").arg(err2.asString())));
+ }
+ }
+ });
+
+ job->start(target);
+ QSignalSpy spy (this, SIGNAL(asyncDone()));
+ QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT));
+
+ // At this point the trust signature should have been added.
+ target.update();
+ const auto trustSignature = target.userID(0).signature(target.userID(0).numSignatures() - 1);
+ QVERIFY(trustSignature.isTrustSignature());
+ QCOMPARE(trustSignature.trustDepth(), 1u);
+ QCOMPARE(trustSignature.trustValue(), TrustSignatureTrust::Complete);
+ QVERIFY(trustSignature.trustScope());
+ const auto trustScope = QString::fromUtf8(trustSignature.trustScope());
+ QVERIFY(!trustScope.isEmpty());
+ const QRegExp regex{trustScope};
+ QVERIFY(regex.isValid());
+ QVERIFY(regex.indexIn(QStringLiteral("Foo <foo@example.org>")) != -1);
+ }
+
+ // Create second trust signature
+ {
+ // Create the job
+ auto job = openpgp()->signKeyJob();//std::unique_ptr<SignKeyJob>{openpgp()->signKeyJob()};
+ QVERIFY(job);
+
+ // Hack in the passphrase provider
+ auto jobCtx = Job::context(job);
+ TestPassphraseProvider provider;
+ jobCtx->setPassphraseProvider(&provider);
+ jobCtx->setPinentryMode(Context::PinentryLoopback);
+
+ // Setup the job
+ job->setExportable(true);
+ job->setSigningKey(seckey);
+ job->setUserIDsToSign({0});
+ job->setDupeOk(true);
+ job->setTrustSignature(TrustSignatureTrust::Partial, 2, QStringLiteral("example.net"));
+
+ connect(job, &SignKeyJob::result,
+ this, [this] (const GpgME::Error &err2, const QString &, const GpgME::Error &) {
+ Q_EMIT asyncDone();
+ if (err2) {
+ if (err2.code() == GPG_ERR_GENERAL) {
+ QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.\n"
+ "Hint: Run with GPGMEPP_INTERACTOR_DEBUG=stderr to debug the edit interaction.").arg(err2.asString())));
+ } else {
+ QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.").arg(err2.asString())));
+ }
+ }
+ });
+
+ err = job->start(target);
+ QVERIFY(!err);
+ QSignalSpy spy (this, SIGNAL(asyncDone()));
+ QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT));
+
+ // At this point the trust signature should have been added.
+ target.update();
+ const auto trustSignature = target.userID(0).signature(target.userID(0).numSignatures() - 1);
+ QVERIFY(trustSignature.isTrustSignature());
+ QCOMPARE(trustSignature.trustDepth(), 2u);
+ QCOMPARE(trustSignature.trustValue(), TrustSignatureTrust::Partial);
+ QVERIFY(trustSignature.trustScope());
+ const auto trustScope = QString::fromUtf8(trustSignature.trustScope());
+ QVERIFY(!trustScope.isEmpty());
+ const QRegExp regex{trustScope};
+ QVERIFY(regex.isValid());
+ QVERIFY(regex.indexIn(QStringLiteral("Foo <foo@example.net>")) != -1);
+ }
+ }
+
+ void initTestCase()
+ {
+ QGpgMETest::initTestCase();
+ const QString gpgHome = qgetenv("GNUPGHOME");
+ QVERIFY(copyKeyrings(gpgHome, mDir.path()));
+ qputenv("GNUPGHOME", mDir.path().toUtf8());
+ QFile conf(mDir.path() + QStringLiteral("/gpg.conf"));
+ QVERIFY(conf.open(QIODevice::WriteOnly));
+ if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() >= "2.2.18") {
+ conf.write("allow-weak-key-signatures");
+ }
+ conf.close();
+ }
+
+private:
+ QTemporaryDir mDir;
+};
+
+QTEST_MAIN(TestTrustSignatures)
+
+#include "t-trustsignatures.moc"
diff --git a/lang/qt/tests/t-various.cpp b/lang/qt/tests/t-various.cpp
index bec0a57..8563b68 100644
--- a/lang/qt/tests/t-various.cpp
+++ b/lang/qt/tests/t-various.cpp
@@ -46,6 +46,7 @@
#include "dn.h"
#include "data.h"
#include "dataprovider.h"
+#include "signkeyjob.h"
#include "t-support.h"
@@ -233,6 +234,131 @@ private Q_SLOTS:
delete ctx;
}
+ void testSignKeyWithoutExpiration()
+ {
+ Error err;
+
+ if (!loopbackSupported()) {
+ return;
+ }
+
+ auto ctx = Context::create(OpenPGP);
+ QVERIFY(ctx);
+
+ // Get the signing key (alfa@example.net)
+ auto seckey = ctx->key("A0FF4590BB6122EDEF6E3C542D727CC768697734", err, true);
+ QVERIFY(!err);
+ QVERIFY(!seckey.isNull());
+
+ // Get the target key (Bob / Bravo Test)
+ auto target = ctx->key("D695676BDCEDCC2CDD6152BCFE180B1DA9E3B0B2", err, false);
+ QVERIFY(!err);
+ QVERIFY(!target.isNull());
+ QVERIFY(target.numUserIDs() > 0);
+
+ // Create the job
+ auto job = std::unique_ptr<SignKeyJob>{openpgp()->signKeyJob()};
+ QVERIFY(job);
+
+ // Hack in the passphrase provider
+ auto jobCtx = Job::context(job.get());
+ TestPassphraseProvider provider;
+ jobCtx->setPassphraseProvider(&provider);
+ jobCtx->setPinentryMode(Context::PinentryLoopback);
+
+ // Setup the job
+ job->setExportable(true);
+ job->setSigningKey(seckey);
+ job->setDupeOk(true);
+
+ connect(job.get(), &SignKeyJob::result,
+ this, [this] (const GpgME::Error &err2, const QString &, const GpgME::Error &) {
+ Q_EMIT asyncDone();
+ if (err2) {
+ if (err2.code() == GPG_ERR_GENERAL) {
+ QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.\n"
+ "Hint: Run with GPGMEPP_INTERACTOR_DEBUG=stderr to debug the edit interaction.").arg(err2.asString())));
+ } else {
+ QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.").arg(err2.asString())));
+ }
+ }
+ });
+
+ job->start(target);
+ QSignalSpy spy{this, &TestVarious::asyncDone};
+ QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT));
+
+ // At this point the signature should have been added.
+ target.update();
+ const auto keySignature = target.userID(0).signature(target.userID(0).numSignatures() - 1);
+ QVERIFY(keySignature.neverExpires());
+ }
+
+ void testSignKeyWithExpiration()
+ {
+ Error err;
+
+ if (!loopbackSupported()) {
+ return;
+ }
+
+ auto ctx = Context::create(OpenPGP);
+ QVERIFY(ctx);
+
+ // Get the signing key (alfa@example.net)
+ auto seckey = ctx->key("A0FF4590BB6122EDEF6E3C542D727CC768697734", err, true);
+ QVERIFY(!err);
+ QVERIFY(!seckey.isNull());
+
+ // Get the target key (Bob / Bravo Test)
+ auto target = ctx->key("D695676BDCEDCC2CDD6152BCFE180B1DA9E3B0B2", err, false);
+ QVERIFY(!err);
+ QVERIFY(!target.isNull());
+ QVERIFY(target.numUserIDs() > 0);
+
+ // Create the job
+ auto job = std::unique_ptr<SignKeyJob>{openpgp()->signKeyJob()};
+ QVERIFY(job);
+
+ // Hack in the passphrase provider
+ auto jobCtx = Job::context(job.get());
+ TestPassphraseProvider provider;
+ jobCtx->setPassphraseProvider(&provider);
+ jobCtx->setPinentryMode(Context::PinentryLoopback);
+
+ // Setup the job
+ job->setExportable(true);
+ job->setSigningKey(seckey);
+ job->setDupeOk(true);
+ job->setExpirationDate(QDate{2222, 2, 22});
+
+ connect(job.get(), &SignKeyJob::result,
+ this, [this] (const GpgME::Error &err2, const QString &, const GpgME::Error &) {
+ Q_EMIT asyncDone();
+ if (err2) {
+ if (err2.code() == GPG_ERR_GENERAL) {
+ QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.\n"
+ "Hint: Run with GPGMEPP_INTERACTOR_DEBUG=stderr to debug the edit interaction.").arg(err2.asString())));
+ } else {
+ QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.").arg(err2.asString())));
+ }
+ }
+ });
+
+ QTest::ignoreMessage(QtWarningMsg, "Expiration of certification has been changed to QDate(\"2106-02-06\")");
+
+ job->start(target);
+ QSignalSpy spy{this, &TestVarious::asyncDone};
+ QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT));
+
+ // At this point the signature should have been added.
+ target.update();
+ const auto keySignature = target.userID(0).signature(target.userID(0).numSignatures() - 1);
+ QVERIFY(!keySignature.neverExpires());
+ const auto expirationDate = QDateTime::fromSecsSinceEpoch(keySignature.expirationTime()).date();
+ QCOMPARE(expirationDate, QDate(2106, 2, 6)); // expiration date is capped at 2106-02-06
+ }
+
void testVersion()
{
QVERIFY(EngineInfo::Version("2.1.0") < EngineInfo::Version("2.1.1"));
@@ -285,6 +411,12 @@ private Q_SLOTS:
const QString gpgHome = qgetenv("GNUPGHOME");
QVERIFY(copyKeyrings(gpgHome, mDir.path()));
qputenv("GNUPGHOME", mDir.path().toUtf8());
+ QFile conf(mDir.path() + QStringLiteral("/gpg.conf"));
+ QVERIFY(conf.open(QIODevice::WriteOnly));
+ if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() >= "2.2.18") {
+ conf.write("allow-weak-key-signatures");
+ }
+ conf.close();
}
private: