diff options
author | JinWang An <jinwang.an@samsung.com> | 2021-12-01 16:54:39 +0900 |
---|---|---|
committer | JinWang An <jinwang.an@samsung.com> | 2021-12-01 16:54:39 +0900 |
commit | d19c360948ede5ffe5974de8abc9da44be617ca1 (patch) | |
tree | f77dd8640a12e7e960c3d408f90e08e069ece9f1 /lang | |
parent | fc59d1efdca7b2886739948b88a1aa8cd4243146 (diff) | |
download | gpgme-d19c360948ede5ffe5974de8abc9da44be617ca1.tar.gz gpgme-d19c360948ede5ffe5974de8abc9da44be617ca1.tar.bz2 gpgme-d19c360948ede5ffe5974de8abc9da44be617ca1.zip |
Imported Upstream version 1.16.0upstream/1.16.0
Diffstat (limited to 'lang')
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: |