[git] GPGME - branch, master, updated. gpgme-1.8.0-84-g15fbac9

by Justus Winter cvs at cvs.gnupg.org
Fri Feb 17 17:09:57 CET 2017


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GnuPG Made Easy".

The branch, master has been updated
       via  15fbac9e72a4d1bff9a3b9e9822f9175b09fbcd5 (commit)
       via  48634e651fcd02431c0518d42ada1f3b402feb2c (commit)
      from  de8494b16bc50c60a8438f2cae1f8c88e8949f7a (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 15fbac9e72a4d1bff9a3b9e9822f9175b09fbcd5
Author: Justus Winter <justus at g10code.com>
Date:   Fri Feb 17 17:07:05 2017 +0100

    python: Support manipulating the TOFU policy.
    
    * NEWS: Update.
    * doc/gpgme.texi: Fix typos.
    * lang/python/gpg/constants/__init__.py: Import new files.
    * lang/python/gpg/constants/tofu/__init__.py: New file.
    * lang/python/gpg/constants/tofu/policy.py: New file.
    * lang/python/gpg/core.py (Context.key_tofu_policy): New function.
    * lang/python/gpgme.i: Nice reprs for gpgme_tofu_info_t.
    * lang/python/setup.py.in: Install new package.
    * lang/python/tests/t-quick-key-manipulation.py: Extend test.
    
    Signed-off-by: Justus Winter <justus at g10code.com>

diff --git a/NEWS b/NEWS
index 617d1d3..d1042c6 100644
--- a/NEWS
+++ b/NEWS
@@ -26,6 +26,7 @@ Noteworthy changes in version 1.8.1 (unreleased)
  py: Context.key_add_uid     NEW.
  py: Context.key_revoke_uid  NEW.
  py: Context.key_sign        NEW.
+ py: Context.key_tofu_policy NEW.
  py: core.pubkey_algo_string NEW.
  py: core.addrspec_from_uid  NEW.
 
diff --git a/doc/gpgme.texi b/doc/gpgme.texi
index 78225d5..1e2cde7 100644
--- a/doc/gpgme.texi
+++ b/doc/gpgme.texi
@@ -4489,7 +4489,7 @@ could not be started.
 @cindex validity, TOFU
 
 The OpenPGP engine features a Trust-On-First-Use (TOFU) key validation
-model.  For resolving clonflics it is necessary to declare the policy
+model.  For resolving conflicts it is necessary to declare the policy
 for a key.  See the GnuPG manual for details on the TOFU
 implementation.
 
@@ -4502,7 +4502,7 @@ policy values that are supported by @acronym{GPGME}:
 @item GPGME_TOFU_POLICY_AUTO
 Set the policy to ``auto''.
 @item GPGME_TOFU_POLICY_GOOD
-Set the policy to ``goog''.
+Set the policy to ``good''.
 @item GPGME_TOFU_POLICY_BAD
 Set the policy to ``bad''.
 @item GPGME_TOFU_POLICY_ASK
diff --git a/lang/python/gpg/constants/__init__.py b/lang/python/gpg/constants/__init__.py
index 79d1fbc..484ffd2 100644
--- a/lang/python/gpg/constants/__init__.py
+++ b/lang/python/gpg/constants/__init__.py
@@ -25,7 +25,7 @@ util.process_constants('GPGME_', globals())
 del util
 
 # For convenience, we import the modules here.
-from . import data, keylist, sig # The subdirs.
+from . import data, keylist, sig, tofu # The subdirs.
 from . import create, event, keysign, md, pk, protocol, sigsum, status, validity
 
 # A complication arises because 'import' is a reserved keyword.
@@ -34,7 +34,7 @@ globals()['Import'] = getattr(__import__('', globals(), locals(),
                                          [str('import')], 1), "import")
 
 __all__ = ['data', 'event', 'import', 'keysign', 'keylist', 'md', 'pk',
-           'protocol', 'sig', 'sigsum', 'status', 'validity', 'create']
+           'protocol', 'sig', 'sigsum', 'status', 'tofu', 'validity', 'create']
 
 # GPGME 1.7 replaced gpgme_op_edit with gpgme_op_interact.  We
 # implement gpg.Context.op_edit using gpgme_op_interact, so the
diff --git a/lang/python/gpg/constants/tofu/__init__.py b/lang/python/gpg/constants/tofu/__init__.py
new file mode 100644
index 0000000..819a58b
--- /dev/null
+++ b/lang/python/gpg/constants/tofu/__init__.py
@@ -0,0 +1,24 @@
+# TOFU
+#
+# Copyright (C) 2017 g10 Code GmbH
+#
+# This file is part of GPGME.
+#
+# GPGME is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# GPGME 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 Lesser General
+# Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+from . import policy
+__all__ = ['policy']
diff --git a/lang/python/gpg/constants/tofu/policy.py b/lang/python/gpg/constants/tofu/policy.py
new file mode 100644
index 0000000..5a61f06
--- /dev/null
+++ b/lang/python/gpg/constants/tofu/policy.py
@@ -0,0 +1,25 @@
+# TOFU policies
+#
+# Copyright (C) 2017 g10 Code GmbH
+#
+# This file is part of GPGME.
+#
+# GPGME is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# GPGME 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 Lesser General
+# Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+from gpg import util
+util.process_constants('GPGME_TOFU_POLICY_', globals())
+del util
diff --git a/lang/python/gpg/core.py b/lang/python/gpg/core.py
index cb4ccf7..fe0ba81 100644
--- a/lang/python/gpg/core.py
+++ b/lang/python/gpg/core.py
@@ -716,6 +716,18 @@ class Context(GpgmeWrapper):
 
         self.op_keysign(key, uids, expires_in, flags)
 
+    def key_tofu_policy(self, key, policy):
+        """Set a keys' TOFU policy
+
+        Set the TOFU policy associated with KEY to POLICY.  Calling
+        this function is only valid for the OpenPGP protocol.
+
+        Raises:
+        GPGMEError   -- as signaled by the underlying library
+
+        """
+        self.op_tofu_policy(key, policy)
+
     def assuan_transact(self, command,
                         data_cb=None, inquire_cb=None, status_cb=None):
         """Issue a raw assuan command
diff --git a/lang/python/gpgme.i b/lang/python/gpgme.i
index 3e89bb1..fa9caf6 100644
--- a/lang/python/gpgme.i
+++ b/lang/python/gpgme.i
@@ -687,3 +687,7 @@ _gpg_unwrap_gpgme_ctx_t(PyObject *wrapped)
 %extend _gpgme_user_id {
   genericrepr(UID)
 };
+
+%extend _gpgme_tofu_info {
+  genericrepr(TofuInfo)
+};
diff --git a/lang/python/setup.py.in b/lang/python/setup.py.in
index e32237d..bf4efa3 100755
--- a/lang/python/setup.py.in
+++ b/lang/python/setup.py.in
@@ -172,7 +172,8 @@ setup(name="gpg",
       url='https://www.gnupg.org',
       ext_modules=[swige],
       packages = ['gpg', 'gpg.constants', 'gpg.constants.data',
-                  'gpg.constants.keylist', 'gpg.constants.sig'],
+                  'gpg.constants.keylist', 'gpg.constants.sig',
+                  'gpg.constants.tofu'],
       license="LGPL2.1+ (the library), GPL2+ (tests and examples)",
       classifiers=[
           'Development Status :: 4 - Beta',
diff --git a/lang/python/tests/t-quick-key-manipulation.py b/lang/python/tests/t-quick-key-manipulation.py
index 12c18ce..d7d2bd4 100755
--- a/lang/python/tests/t-quick-key-manipulation.py
+++ b/lang/python/tests/t-quick-key-manipulation.py
@@ -90,3 +90,34 @@ with support.EphemeralContext() as ctx:
         assert False, "Expected an error but got none"
     except gpg.errors.GpgError:
         pass
+
+    # Check setting the TOFU policy.
+    with open("gpg.conf", "a") as handle:
+        handle.write("trust-model tofu+pgp\n")
+
+    for name, policy in [(name, getattr(gpg.constants.tofu.policy, name))
+                         for name in filter(lambda x: not x.startswith('__'),
+                                            dir(gpg.constants.tofu.policy))]:
+        if policy == gpg.constants.tofu.policy.NONE:
+            # We must not set the policy to NONE.
+            continue
+
+        ctx.key_tofu_policy(key, policy)
+
+        keys = list(ctx.keylist(key.uids[0].uid,
+                                mode=(gpg.constants.keylist.mode.LOCAL
+                                      |gpg.constants.keylist.mode.WITH_TOFU)))
+        assert len(keys) == 1
+
+        if policy == gpg.constants.tofu.policy.AUTO:
+            # We cannot check that it is set to AUTO.
+            continue
+
+        for uid in keys[0].uids:
+            if uid.uid == alpha:
+                # TOFU information of revoked UIDs is not updated.
+                # XXX: Is that expected?
+                continue
+            assert uid.tofu[0].policy == policy, \
+                "Expected policy {0} ({1}), got {2}".format(policy, name,
+                                                            uid.tofu[0].policy)

commit 48634e651fcd02431c0518d42ada1f3b402feb2c
Author: Justus Winter <justus at g10code.com>
Date:   Fri Feb 17 15:44:35 2017 +0100

    python: Support quick key signing.
    
    * NEWS: Update.
    * doc/gpgme.texi (gpgme_op_keysign): Fix the description of the
    'expire' argument.
    * lang/python/gpg/constants/__init__.py: Import new file.
    * lang/python/gpg/constants/keysign.py: New file.
    * lang/python/gpg/core.py (Context.key_sign): New function.
    * lang/python/tests/Makefile.am (py_tests): Add new test.
    * lang/python/tests/t-quick-key-signing.py: New test.
    
    Signed-off-by: Justus Winter <justus at g10code.com>

diff --git a/NEWS b/NEWS
index 889a526..617d1d3 100644
--- a/NEWS
+++ b/NEWS
@@ -25,6 +25,7 @@ Noteworthy changes in version 1.8.1 (unreleased)
  py: Context.create_subkey   NEW.
  py: Context.key_add_uid     NEW.
  py: Context.key_revoke_uid  NEW.
+ py: Context.key_sign        NEW.
  py: core.pubkey_algo_string NEW.
  py: core.addrspec_from_uid  NEW.
 
diff --git a/doc/gpgme.texi b/doc/gpgme.texi
index c088cfe..78225d5 100644
--- a/doc/gpgme.texi
+++ b/doc/gpgme.texi
@@ -4044,11 +4044,10 @@ object (@code{gpgme_user_id_t}) is to be used.  To select more than
 one user ID put them all into one string separated by linefeeds
 characters (@code{\n}) and set the flag @code{GPGME_KEYSIGN_LFSEP}.
 
- at var{expires} can be set to the number of seconds since Epoch of the
-desired expiration date in UTC for the new signature.  The common case
-is to use 0 to not set an expiration date.  However, if the
-configuration of the engine defines a default expiration for key
-signatures, that is still used unless the flag
+ at var{expires} specifies the expiration time of the new signature in
+seconds.  The common case is to use 0 to not set an expiration date.
+However, if the configuration of the engine defines a default
+expiration for key signatures, that is still used unless the flag
 @code{GPGME_KEYSIGN_NOEXPIRE} is used.  Note that this parameter takes
 an unsigned long value and not a @code{time_t} to avoid problems on
 systems which use a signed 32 bit @code{time_t}.  Note further that
diff --git a/lang/python/gpg/constants/__init__.py b/lang/python/gpg/constants/__init__.py
index 2bf180e..79d1fbc 100644
--- a/lang/python/gpg/constants/__init__.py
+++ b/lang/python/gpg/constants/__init__.py
@@ -26,14 +26,14 @@ del util
 
 # For convenience, we import the modules here.
 from . import data, keylist, sig # The subdirs.
-from . import create, event, md, pk, protocol, sigsum, status, validity
+from . import create, event, keysign, md, pk, protocol, sigsum, status, validity
 
 # A complication arises because 'import' is a reserved keyword.
 # Import it as 'Import' instead.
 globals()['Import'] = getattr(__import__('', globals(), locals(),
                                          [str('import')], 1), "import")
 
-__all__ = ['data', 'event', 'import', 'keylist', 'md', 'pk',
+__all__ = ['data', 'event', 'import', 'keysign', 'keylist', 'md', 'pk',
            'protocol', 'sig', 'sigsum', 'status', 'validity', 'create']
 
 # GPGME 1.7 replaced gpgme_op_edit with gpgme_op_interact.  We
diff --git a/lang/python/gpg/constants/keysign.py b/lang/python/gpg/constants/keysign.py
new file mode 100644
index 0000000..fccdbc4
--- /dev/null
+++ b/lang/python/gpg/constants/keysign.py
@@ -0,0 +1,25 @@
+# Flags for key signing
+#
+# Copyright (C) 2017 g10 Code GmbH
+#
+# This file is part of GPGME.
+#
+# GPGME is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# GPGME 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 Lesser General
+# Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+from gpg import util
+util.process_constants('GPGME_KEYSIGN_', globals())
+del util
diff --git a/lang/python/gpg/core.py b/lang/python/gpg/core.py
index 28d4629..cb4ccf7 100644
--- a/lang/python/gpg/core.py
+++ b/lang/python/gpg/core.py
@@ -675,6 +675,47 @@ class Context(GpgmeWrapper):
         """
         self.op_revuid(key, uid, 0)
 
+    def key_sign(self, key, uids=None, expires_in=False, local=False):
+        """Sign a key
+
+        Sign a key with the current set of signing keys.  Calling this
+        function is only valid for the OpenPGP protocol.
+
+        If UIDS is None (the default), then all UIDs are signed.  If
+        it is a string, then only the matching UID is signed.  If it
+        is a list of strings, then all matching UIDs are signed.  Note
+        that a case-sensitive exact string comparison is done.
+
+        EXPIRES_IN specifies the expiration time of the signature in
+        seconds.  If EXPIRES_IN is False, the signature does not
+        expire.
+
+        Keyword arguments:
+        uids         -- user ids to sign, see above (default: sign all)
+        expires_in   -- validity period of the signature in seconds
+                                               (default: do not expire)
+        local        -- create a local, non-exportable signature
+                                               (default: False)
+
+        Raises:
+        GPGMEError   -- as signaled by the underlying library
+
+        """
+        flags = 0
+        if uids == None or util.is_a_string(uids):
+            pass#through unchanged
+        else:
+            flags |= constants.keysign.LFSEP
+            uids = "\n".join(uids)
+
+        if not expires_in:
+            flags |= constants.keysign.NOEXPIRE
+
+        if local:
+            flags |= constants.keysign.LOCAL
+
+        self.op_keysign(key, uids, expires_in, flags)
+
     def assuan_transact(self, command,
                         data_cb=None, inquire_cb=None, status_cb=None):
         """Issue a raw assuan command
diff --git a/lang/python/tests/Makefile.am b/lang/python/tests/Makefile.am
index 1d5e1db..7251cd3 100644
--- a/lang/python/tests/Makefile.am
+++ b/lang/python/tests/Makefile.am
@@ -53,7 +53,8 @@ py_tests = t-wrapper.py \
 	t-protocol-assuan.py \
 	t-quick-key-creation.py \
 	t-quick-subkey-creation.py \
-	t-quick-key-manipulation.py
+	t-quick-key-manipulation.py \
+	t-quick-key-signing.py
 
 XTESTS = initial.py $(py_tests) final.py
 EXTRA_DIST = support.py $(XTESTS) encrypt-only.asc sign-only.asc \
diff --git a/lang/python/tests/t-quick-key-signing.py b/lang/python/tests/t-quick-key-signing.py
new file mode 100755
index 0000000..f9778a3
--- /dev/null
+++ b/lang/python/tests/t-quick-key-signing.py
@@ -0,0 +1,120 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2017 g10 Code GmbH
+#
+# This file is part of GPGME.
+#
+# GPGME 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.
+#
+# GPGME 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 Lesser General
+# Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+import gpg
+import itertools
+import time
+
+import support
+
+with support.EphemeralContext() as ctx:
+    uid_counter = 0
+    def make_uid():
+        global uid_counter
+        uid_counter += 1
+        return "user{0}@invalid.example.org".format(uid_counter)
+
+    def make_key():
+        uids = [make_uid() for i in range(3)]
+        res = ctx.create_key(uids[0], certify=True)
+        key = ctx.get_key(res.fpr)
+        for u in uids[1:]:
+            ctx.key_add_uid(key, u)
+        return key, uids
+
+    def check_sigs(key, expected_sigs):
+        keys = list(ctx.keylist(key.fpr, mode=(gpg.constants.keylist.mode.LOCAL
+                                               |gpg.constants.keylist.mode.SIGS)))
+        assert len(keys) == 1
+        key_uids = {uid.uid: [s for s in uid.signatures] for uid in keys[0].uids}
+        expected = list(expected_sigs)
+
+        while key_uids and expected:
+            uid, signing_key, func = expected[0]
+            match = False
+            for i, s in enumerate(key_uids[uid]):
+                if signing_key.fpr.endswith(s.keyid):
+                    if func:
+                        func(s)
+                    match = True
+                    break
+            if match:
+                expected.pop(0)
+                key_uids[uid].pop(i)
+                if not key_uids[uid]:
+                    del key_uids[uid]
+
+        assert not key_uids, "Superfluous signatures: {0}".format(key_uids)
+        assert not expected, "Missing signatures: {0}".format(expected)
+
+    # Simplest case.  Sign without any options.
+    key_a, uids_a = make_key()
+    key_b, uids_b = make_key()
+    ctx.signers = [key_a]
+
+    def exportable_non_expiring(s):
+        assert s.exportable
+        assert s.expires == 0
+
+    check_sigs(key_b, itertools.product(uids_b, [key_b], [exportable_non_expiring]))
+    ctx.key_sign(key_b)
+    check_sigs(key_b, itertools.product(uids_b, [key_b, key_a], [exportable_non_expiring]))
+
+    # Create a non-exportable signature, and explicitly name all uids.
+    key_c, uids_c = make_key()
+    ctx.signers = [key_a, key_b]
+
+    def non_exportable_non_expiring(s):
+        assert s.exportable == 0
+        assert s.expires == 0
+
+    ctx.key_sign(key_c, local=True, uids=uids_c)
+    check_sigs(key_c,
+               list(itertools.product(uids_c, [key_c],
+                                      [exportable_non_expiring]))
+               + list(itertools.product(uids_c, [key_b, key_a],
+                                        [non_exportable_non_expiring])))
+
+    # Create a non-exportable, expiring signature for a single uid.
+    key_d, uids_d = make_key()
+    ctx.signers = [key_c]
+    expires_in = 600
+    slack = 10
+
+    def non_exportable_expiring(s):
+        assert s.exportable == 0
+        assert abs(time.time() + expires_in - s.expires) < slack
+
+    ctx.key_sign(key_d, local=True, expires_in=expires_in, uids=uids_d[0])
+    check_sigs(key_d,
+               list(itertools.product(uids_d, [key_d],
+                                      [exportable_non_expiring]))
+               + list(itertools.product(uids_d[:1], [key_c],
+                                        [non_exportable_expiring])))
+
+    # Now sign the second in the same fashion, but use a singleton list.
+    ctx.key_sign(key_d, local=True, expires_in=expires_in, uids=uids_d[1:2])
+    check_sigs(key_d,
+               list(itertools.product(uids_d, [key_d],
+                                      [exportable_non_expiring]))
+               + list(itertools.product(uids_d[:2], [key_c],
+                                        [non_exportable_expiring])))

-----------------------------------------------------------------------

Summary of changes:
 NEWS                                               |   2 +
 doc/gpgme.texi                                     |  13 ++-
 lang/python/gpg/constants/__init__.py              |   8 +-
 .../python/gpg/constants/{create.py => keysign.py} |   4 +-
 .../gpg/constants/{create.py => tofu/__init__.py}  |   7 +-
 .../gpg/constants/{create.py => tofu/policy.py}    |   4 +-
 lang/python/gpg/core.py                            |  53 +++++++++
 lang/python/gpgme.i                                |   4 +
 lang/python/setup.py.in                            |   3 +-
 lang/python/tests/Makefile.am                      |   3 +-
 lang/python/tests/t-quick-key-manipulation.py      |  31 ++++++
 lang/python/tests/t-quick-key-signing.py           | 120 +++++++++++++++++++++
 12 files changed, 231 insertions(+), 21 deletions(-)
 copy lang/python/gpg/constants/{create.py => keysign.py} (91%)
 copy lang/python/gpg/constants/{create.py => tofu/__init__.py} (88%)
 copy lang/python/gpg/constants/{create.py => tofu/policy.py} (91%)
 create mode 100755 lang/python/tests/t-quick-key-signing.py


hooks/post-receive
-- 
GnuPG Made Easy
http://git.gnupg.org




More information about the Gnupg-commits mailing list