summaryrefslogtreecommitdiff
path: root/lang/python/gpg
diff options
context:
space:
mode:
Diffstat (limited to 'lang/python/gpg')
-rw-r--r--lang/python/gpg/__init__.py121
-rw-r--r--lang/python/gpg/callbacks.py49
-rw-r--r--lang/python/gpg/constants/__init__.py142
-rw-r--r--lang/python/gpg/constants/data/__init__.py6
-rw-r--r--lang/python/gpg/constants/data/encoding.py23
-rw-r--r--lang/python/gpg/constants/event.py23
-rw-r--r--lang/python/gpg/constants/import.py23
-rw-r--r--lang/python/gpg/constants/keylist/__init__.py6
-rw-r--r--lang/python/gpg/constants/keylist/mode.py23
-rw-r--r--lang/python/gpg/constants/md.py23
-rw-r--r--lang/python/gpg/constants/pk.py23
-rw-r--r--lang/python/gpg/constants/protocol.py23
-rw-r--r--lang/python/gpg/constants/sig/__init__.py6
-rw-r--r--lang/python/gpg/constants/sig/mode.py23
-rw-r--r--lang/python/gpg/constants/sig/notation.py25
-rw-r--r--lang/python/gpg/constants/sigsum.py23
-rw-r--r--lang/python/gpg/constants/status.py124
-rw-r--r--lang/python/gpg/constants/validity.py23
-rw-r--r--lang/python/gpg/core.py1164
-rw-r--r--lang/python/gpg/errors.py128
-rw-r--r--lang/python/gpg/gpgme.py126
-rw-r--r--lang/python/gpg/results.py118
-rw-r--r--lang/python/gpg/util.py53
-rw-r--r--lang/python/gpg/version.py68
-rw-r--r--lang/python/gpg/version.py.in68
25 files changed, 2434 insertions, 0 deletions
diff --git a/lang/python/gpg/__init__.py b/lang/python/gpg/__init__.py
new file mode 100644
index 0000000..385b17e
--- /dev/null
+++ b/lang/python/gpg/__init__.py
@@ -0,0 +1,121 @@
+# Copyright (C) 2016 g10 Code GmbH
+# Copyright (C) 2004 Igor Belyi <belyi@users.sourceforge.net>
+# Copyright (C) 2002 John Goerzen <jgoerzen@complete.org>
+#
+# This library 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.
+#
+# This library 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 library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+"""gpg: GnuPG Interface for Python (GPGME bindings)
+
+Welcome to gpg, the GnuPG Interface for Python.
+
+The latest release of this package may be obtained from
+https://www.gnupg.org
+
+FEATURES
+--------
+
+ * Feature-rich, full implementation of the GPGME library. Supports
+ all GPGME features. Callback functions may be written in pure
+ Python. Exceptions raised in callbacks are properly propagated.
+
+ * Ability to sign, encrypt, decrypt, and verify data.
+
+ * Ability to list keys, export and import keys, and manage the keyring.
+
+ * Fully object-oriented with convenient classes and modules.
+
+QUICK EXAMPLE
+-------------
+
+ >>> import gpg
+ >>> with gpg.Context() as c:
+ >>> with gpg.Context() as c:
+ ... cipher, _, _ = c.encrypt("Hello world :)".encode(),
+ ... passphrase="abc")
+ ... c.decrypt(cipher, passphrase="abc")
+ ...
+ (b'Hello world :)',
+ <gpg.results.DecryptResult object at 0x7f5ab8121080>,
+ <gpg.results.VerifyResult object at 0x7f5ab81219b0>)
+
+GENERAL OVERVIEW
+----------------
+
+For those of you familiar with GPGME, you will be right at home here.
+
+The python gpg module is, for the most part, a direct interface to the C GPGME
+library. However, it is re-packaged in a more Pythonic way --
+object-oriented with classes and modules. Take a look at the classes
+defined here -- they correspond directly to certain object types in GPGME
+for C. For instance, the following C code:
+
+gpgme_ctx_t context;
+gpgme_new(&context);
+...
+gpgme_op_encrypt(context, recp, 1, plain, cipher);
+
+Translates into the following Python code:
+
+context = core.Context()
+...
+context.op_encrypt(recp, 1, plain, cipher)
+
+The Python module automatically does error-checking and raises Python
+exception gpg.errors.GPGMEError when GPGME signals an error. getcode()
+and getsource() of this exception return code and source of the error.
+
+IMPORTANT NOTE
+--------------
+This documentation only covers a small subset of available GPGME functions and
+methods. Please consult the documentation for the C library
+for comprehensive coverage.
+
+This library uses Python's reflection to automatically detect the methods
+that are available for each class, and as such, most of those methods
+do not appear explicitly anywhere. You can use dir() python built-in command
+on an object to see what methods and fields it has but their meaning can
+be found only in GPGME documentation.
+
+FOR MORE INFORMATION
+--------------------
+GnuPG homepage: https://www.gnupg.org/
+GPGME documentation: https://www.gnupg.org/documentation/manuals/gpgme/
+
+"""
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+from . import core
+from . import errors
+from . import constants
+from . import util
+from . import callbacks
+from . import version
+from .core import Context
+from .core import Data
+
+# Interface hygiene.
+
+# Drop the low-level gpgme that creeps in for some reason.
+gpgme = None
+del gpgme
+
+# This is a white-list of symbols. Any other will alert pyflakes.
+_ = [Context, Data, core, errors, constants, util, callbacks, version]
+del _
+
+__all__ = ["Context", "Data",
+ "core", "errors", "constants", "util", "callbacks", "version"]
diff --git a/lang/python/gpg/callbacks.py b/lang/python/gpg/callbacks.py
new file mode 100644
index 0000000..b25a9a7
--- /dev/null
+++ b/lang/python/gpg/callbacks.py
@@ -0,0 +1,49 @@
+# Copyright (C) 2004 Igor Belyi <belyi@users.sourceforge.net>
+# Copyright (C) 2002 John Goerzen <jgoerzen@complete.org>
+#
+# This library 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.
+#
+# This library 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 library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+from getpass import getpass
+
+def passphrase_stdin(hint, desc, prev_bad, hook=None):
+ """This is a sample callback that will read a passphrase from
+ the terminal. The hook here, if present, will be used to describe
+ why the passphrase is needed."""
+ why = ''
+ if hook != None:
+ why = ' ' + hook
+ if prev_bad:
+ why += ' (again)'
+ print("Please supply %s' password%s:" % (hint, why))
+ return getpass()
+
+def progress_stdout(what, type, current, total, hook=None):
+ print("PROGRESS UPDATE: what = %s, type = %d, current = %d, total = %d" %\
+ (what, type, current, total))
+
+def readcb_fh(count, hook):
+ """A callback for data. hook should be a Python file-like object."""
+ if count:
+ # Should return '' on EOF
+ return hook.read(count)
+ else:
+ # Wants to rewind.
+ if not hasattr(hook, 'seek'):
+ return None
+ hook.seek(0, 0)
+ return None
diff --git a/lang/python/gpg/constants/__init__.py b/lang/python/gpg/constants/__init__.py
new file mode 100644
index 0000000..4fb3d6f
--- /dev/null
+++ b/lang/python/gpg/constants/__init__.py
@@ -0,0 +1,142 @@
+# Constants.
+#
+# Copyright (C) 2016 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_', globals())
+del util
+
+# For convenience, we import the modules here.
+from . import data, event, keylist, md, pk
+from . import protocol, sig, 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',
+ 'protocol', 'sig', 'sigsum', 'status', 'validity']
+
+# GPGME 1.7 replaced gpgme_op_edit with gpgme_op_interact. We
+# implement gpg.Context.op_edit using gpgme_op_interact, so the
+# callbacks will be called with string keywords instead of numeric
+# status messages. Code that is using these constants will continue
+# to work.
+
+STATUS_ABORT = "ABORT"
+STATUS_ALREADY_SIGNED = "ALREADY_SIGNED"
+STATUS_ATTRIBUTE = "ATTRIBUTE"
+STATUS_BACKUP_KEY_CREATED = "BACKUP_KEY_CREATED"
+STATUS_BAD_PASSPHRASE = "BAD_PASSPHRASE"
+STATUS_BADARMOR = "BADARMOR"
+STATUS_BADMDC = "BADMDC"
+STATUS_BADSIG = "BADSIG"
+STATUS_BEGIN_DECRYPTION = "BEGIN_DECRYPTION"
+STATUS_BEGIN_ENCRYPTION = "BEGIN_ENCRYPTION"
+STATUS_BEGIN_SIGNING = "BEGIN_SIGNING"
+STATUS_BEGIN_STREAM = "BEGIN_STREAM"
+STATUS_CARDCTRL = "CARDCTRL"
+STATUS_DECRYPTION_FAILED = "DECRYPTION_FAILED"
+STATUS_DECRYPTION_INFO = "DECRYPTION_INFO"
+STATUS_DECRYPTION_OKAY = "DECRYPTION_OKAY"
+STATUS_DELETE_PROBLEM = "DELETE_PROBLEM"
+STATUS_ENC_TO = "ENC_TO"
+STATUS_END_DECRYPTION = "END_DECRYPTION"
+STATUS_END_ENCRYPTION = "END_ENCRYPTION"
+STATUS_END_STREAM = "END_STREAM"
+STATUS_ENTER = "ENTER"
+STATUS_ERRMDC = "ERRMDC"
+STATUS_ERROR = "ERROR"
+STATUS_ERRSIG = "ERRSIG"
+STATUS_EXPKEYSIG = "EXPKEYSIG"
+STATUS_EXPSIG = "EXPSIG"
+STATUS_FAILURE = "FAILURE"
+STATUS_FILE_DONE = "FILE_DONE"
+STATUS_FILE_ERROR = "FILE_ERROR"
+STATUS_FILE_START = "FILE_START"
+STATUS_GET_BOOL = "GET_BOOL"
+STATUS_GET_HIDDEN = "GET_HIDDEN"
+STATUS_GET_LINE = "GET_LINE"
+STATUS_GOOD_PASSPHRASE = "GOOD_PASSPHRASE"
+STATUS_GOODMDC = "GOODMDC"
+STATUS_GOODSIG = "GOODSIG"
+STATUS_GOT_IT = "GOT_IT"
+STATUS_IMPORT_OK = "IMPORT_OK"
+STATUS_IMPORT_PROBLEM = "IMPORT_PROBLEM"
+STATUS_IMPORT_RES = "IMPORT_RES"
+STATUS_IMPORTED = "IMPORTED"
+STATUS_INQUIRE_MAXLEN = "INQUIRE_MAXLEN"
+STATUS_INV_RECP = "INV_RECP"
+STATUS_INV_SGNR = "INV_SGNR"
+STATUS_KEY_CONSIDERED = "KEY_CONSIDERED"
+STATUS_KEY_CREATED = "KEY_CREATED"
+STATUS_KEY_NOT_CREATED = "KEY_NOT_CREATED"
+STATUS_KEYEXPIRED = "KEYEXPIRED"
+STATUS_KEYREVOKED = "KEYREVOKED"
+STATUS_LEAVE = "LEAVE"
+STATUS_MISSING_PASSPHRASE = "MISSING_PASSPHRASE"
+STATUS_MOUNTPOINT = "MOUNTPOINT"
+STATUS_NEED_PASSPHRASE = "NEED_PASSPHRASE"
+STATUS_NEED_PASSPHRASE_PIN = "NEED_PASSPHRASE_PIN"
+STATUS_NEED_PASSPHRASE_SYM = "NEED_PASSPHRASE_SYM"
+STATUS_NEWSIG = "NEWSIG"
+STATUS_NO_PUBKEY = "NO_PUBKEY"
+STATUS_NO_RECP = "NO_RECP"
+STATUS_NO_SECKEY = "NO_SECKEY"
+STATUS_NO_SGNR = "NO_SGNR"
+STATUS_NODATA = "NODATA"
+STATUS_NOTATION_DATA = "NOTATION_DATA"
+STATUS_NOTATION_FLAGS = "NOTATION_FLAGS"
+STATUS_NOTATION_NAME = "NOTATION_NAME"
+STATUS_PINENTRY_LAUNCHED = "PINENTRY_LAUNCHED"
+STATUS_PKA_TRUST_BAD = "PKA_TRUST_BAD"
+STATUS_PKA_TRUST_GOOD = "PKA_TRUST_GOOD"
+STATUS_PLAINTEXT = "PLAINTEXT"
+STATUS_PLAINTEXT_LENGTH = "PLAINTEXT_LENGTH"
+STATUS_POLICY_URL = "POLICY_URL"
+STATUS_PROGRESS = "PROGRESS"
+STATUS_REVKEYSIG = "REVKEYSIG"
+STATUS_RSA_OR_IDEA = "RSA_OR_IDEA"
+STATUS_SC_OP_FAILURE = "SC_OP_FAILURE"
+STATUS_SC_OP_SUCCESS = "SC_OP_SUCCESS"
+STATUS_SESSION_KEY = "SESSION_KEY"
+STATUS_SHM_GET = "SHM_GET"
+STATUS_SHM_GET_BOOL = "SHM_GET_BOOL"
+STATUS_SHM_GET_HIDDEN = "SHM_GET_HIDDEN"
+STATUS_SHM_INFO = "SHM_INFO"
+STATUS_SIG_CREATED = "SIG_CREATED"
+STATUS_SIG_ID = "SIG_ID"
+STATUS_SIG_SUBPACKET = "SIG_SUBPACKET"
+STATUS_SIGEXPIRED = "SIGEXPIRED"
+STATUS_SUCCESS = "SUCCESS"
+STATUS_TOFU_STATS = "TOFU_STATS"
+STATUS_TOFU_STATS_LONG = "TOFU_STATS_LONG"
+STATUS_TOFU_USER = "TOFU_USER"
+STATUS_TRUNCATED = "TRUNCATED"
+STATUS_TRUST_FULLY = "TRUST_FULLY"
+STATUS_TRUST_MARGINAL = "TRUST_MARGINAL"
+STATUS_TRUST_NEVER = "TRUST_NEVER"
+STATUS_TRUST_ULTIMATE = "TRUST_ULTIMATE"
+STATUS_TRUST_UNDEFINED = "TRUST_UNDEFINED"
+STATUS_UNEXPECTED = "UNEXPECTED"
+STATUS_USERID_HINT = "USERID_HINT"
+STATUS_VALIDSIG = "VALIDSIG"
diff --git a/lang/python/gpg/constants/data/__init__.py b/lang/python/gpg/constants/data/__init__.py
new file mode 100644
index 0000000..8274ab9
--- /dev/null
+++ b/lang/python/gpg/constants/data/__init__.py
@@ -0,0 +1,6 @@
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+from . import encoding
+__all__ = ['encoding']
diff --git a/lang/python/gpg/constants/data/encoding.py b/lang/python/gpg/constants/data/encoding.py
new file mode 100644
index 0000000..e76a22e
--- /dev/null
+++ b/lang/python/gpg/constants/data/encoding.py
@@ -0,0 +1,23 @@
+# Copyright (C) 2004 Igor Belyi <belyi@users.sourceforge.net>
+# Copyright (C) 2002 John Goerzen <jgoerzen@complete.org>
+#
+# This library 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.
+#
+# This library 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 library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+from gpg import util
+util.process_constants('GPGME_DATA_ENCODING_', globals())
+del util
diff --git a/lang/python/gpg/constants/event.py b/lang/python/gpg/constants/event.py
new file mode 100644
index 0000000..1b14d1d
--- /dev/null
+++ b/lang/python/gpg/constants/event.py
@@ -0,0 +1,23 @@
+# Copyright (C) 2004 Igor Belyi <belyi@users.sourceforge.net>
+# Copyright (C) 2002 John Goerzen <jgoerzen@complete.org>
+#
+# This library 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.
+#
+# This library 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 library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+from gpg import util
+util.process_constants('GPGME_EVENT_', globals())
+del util
diff --git a/lang/python/gpg/constants/import.py b/lang/python/gpg/constants/import.py
new file mode 100644
index 0000000..47c296c
--- /dev/null
+++ b/lang/python/gpg/constants/import.py
@@ -0,0 +1,23 @@
+# Copyright (C) 2004 Igor Belyi <belyi@users.sourceforge.net>
+# Copyright (C) 2002 John Goerzen <jgoerzen@complete.org>
+#
+# This library 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.
+#
+# This library 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 library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+from gpg import util
+util.process_constants('GPGME_IMPORT_', globals())
+del util
diff --git a/lang/python/gpg/constants/keylist/__init__.py b/lang/python/gpg/constants/keylist/__init__.py
new file mode 100644
index 0000000..2ce0edf
--- /dev/null
+++ b/lang/python/gpg/constants/keylist/__init__.py
@@ -0,0 +1,6 @@
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+from . import mode
+__all__ = ['mode']
diff --git a/lang/python/gpg/constants/keylist/mode.py b/lang/python/gpg/constants/keylist/mode.py
new file mode 100644
index 0000000..39e1819
--- /dev/null
+++ b/lang/python/gpg/constants/keylist/mode.py
@@ -0,0 +1,23 @@
+# Copyright (C) 2004 Igor Belyi <belyi@users.sourceforge.net>
+# Copyright (C) 2002 John Goerzen <jgoerzen@complete.org>
+#
+# This library 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.
+#
+# This library 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 library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+from gpg import util
+util.process_constants('GPGME_KEYLIST_MODE_', globals())
+del util
diff --git a/lang/python/gpg/constants/md.py b/lang/python/gpg/constants/md.py
new file mode 100644
index 0000000..f3e8bbd
--- /dev/null
+++ b/lang/python/gpg/constants/md.py
@@ -0,0 +1,23 @@
+# Copyright (C) 2004 Igor Belyi <belyi@users.sourceforge.net>
+# Copyright (C) 2002 John Goerzen <jgoerzen@complete.org>
+#
+# This library 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.
+#
+# This library 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 library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+from gpg import util
+util.process_constants('GPGME_MD_', globals())
+del util
diff --git a/lang/python/gpg/constants/pk.py b/lang/python/gpg/constants/pk.py
new file mode 100644
index 0000000..6bf2a21
--- /dev/null
+++ b/lang/python/gpg/constants/pk.py
@@ -0,0 +1,23 @@
+# Copyright (C) 2004 Igor Belyi <belyi@users.sourceforge.net>
+# Copyright (C) 2002 John Goerzen <jgoerzen@complete.org>
+#
+# This library 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.
+#
+# This library 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 library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+from gpg import util
+util.process_constants('GPGME_PK_', globals())
+del util
diff --git a/lang/python/gpg/constants/protocol.py b/lang/python/gpg/constants/protocol.py
new file mode 100644
index 0000000..d086bbd
--- /dev/null
+++ b/lang/python/gpg/constants/protocol.py
@@ -0,0 +1,23 @@
+# Copyright (C) 2004 Igor Belyi <belyi@users.sourceforge.net>
+# Copyright (C) 2002 John Goerzen <jgoerzen@complete.org>
+#
+# This library 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.
+#
+# This library 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 library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+from gpg import util
+util.process_constants('GPGME_PROTOCOL_', globals())
+del util
diff --git a/lang/python/gpg/constants/sig/__init__.py b/lang/python/gpg/constants/sig/__init__.py
new file mode 100644
index 0000000..39d4e6e
--- /dev/null
+++ b/lang/python/gpg/constants/sig/__init__.py
@@ -0,0 +1,6 @@
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+from . import mode, notation
+__all__ = ['mode', 'notation']
diff --git a/lang/python/gpg/constants/sig/mode.py b/lang/python/gpg/constants/sig/mode.py
new file mode 100644
index 0000000..0f4f0ef
--- /dev/null
+++ b/lang/python/gpg/constants/sig/mode.py
@@ -0,0 +1,23 @@
+# Copyright (C) 2004 Igor Belyi <belyi@users.sourceforge.net>
+# Copyright (C) 2002 John Goerzen <jgoerzen@complete.org>
+#
+# This library 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.
+#
+# This library 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 library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+from gpg import util
+util.process_constants('GPGME_SIG_MODE_', globals())
+del util
diff --git a/lang/python/gpg/constants/sig/notation.py b/lang/python/gpg/constants/sig/notation.py
new file mode 100644
index 0000000..9a79e01
--- /dev/null
+++ b/lang/python/gpg/constants/sig/notation.py
@@ -0,0 +1,25 @@
+# Constants for signature notation data.
+#
+# Copyright (C) 2016 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_SIG_NOTATION_', globals())
+del util
diff --git a/lang/python/gpg/constants/sigsum.py b/lang/python/gpg/constants/sigsum.py
new file mode 100644
index 0000000..09ef9d7
--- /dev/null
+++ b/lang/python/gpg/constants/sigsum.py
@@ -0,0 +1,23 @@
+# Copyright (C) 2004 Igor Belyi <belyi@users.sourceforge.net>
+# Copyright (C) 2002 John Goerzen <jgoerzen@complete.org>
+#
+# This library 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.
+#
+# This library 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 library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+from gpg import util
+util.process_constants('GPGME_SIGSUM_', globals())
+del util
diff --git a/lang/python/gpg/constants/status.py b/lang/python/gpg/constants/status.py
new file mode 100644
index 0000000..a0ad073
--- /dev/null
+++ b/lang/python/gpg/constants/status.py
@@ -0,0 +1,124 @@
+# Copyright (C) 2004 Igor Belyi <belyi@users.sourceforge.net>
+# Copyright (C) 2002 John Goerzen <jgoerzen@complete.org>
+#
+# This library 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.
+#
+# This library 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 library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+# GPGME 1.7 replaced gpgme_op_edit with gpgme_op_interact. We
+# implement gpg.Context.op_edit using gpgme_op_interact, so the
+# callbacks will be called with string keywords instead of numeric
+# status messages. Code that is using these constants will continue
+# to work.
+
+ABORT = "ABORT"
+ALREADY_SIGNED = "ALREADY_SIGNED"
+ATTRIBUTE = "ATTRIBUTE"
+BACKUP_KEY_CREATED = "BACKUP_KEY_CREATED"
+BAD_PASSPHRASE = "BAD_PASSPHRASE"
+BADARMOR = "BADARMOR"
+BADMDC = "BADMDC"
+BADSIG = "BADSIG"
+BEGIN_DECRYPTION = "BEGIN_DECRYPTION"
+BEGIN_ENCRYPTION = "BEGIN_ENCRYPTION"
+BEGIN_SIGNING = "BEGIN_SIGNING"
+BEGIN_STREAM = "BEGIN_STREAM"
+CARDCTRL = "CARDCTRL"
+DECRYPTION_FAILED = "DECRYPTION_FAILED"
+DECRYPTION_INFO = "DECRYPTION_INFO"
+DECRYPTION_OKAY = "DECRYPTION_OKAY"
+DELETE_PROBLEM = "DELETE_PROBLEM"
+ENC_TO = "ENC_TO"
+END_DECRYPTION = "END_DECRYPTION"
+END_ENCRYPTION = "END_ENCRYPTION"
+END_STREAM = "END_STREAM"
+ENTER = "ENTER"
+ERRMDC = "ERRMDC"
+ERROR = "ERROR"
+ERRSIG = "ERRSIG"
+EXPKEYSIG = "EXPKEYSIG"
+EXPSIG = "EXPSIG"
+FAILURE = "FAILURE"
+FILE_DONE = "FILE_DONE"
+FILE_ERROR = "FILE_ERROR"
+FILE_START = "FILE_START"
+GET_BOOL = "GET_BOOL"
+GET_HIDDEN = "GET_HIDDEN"
+GET_LINE = "GET_LINE"
+GOOD_PASSPHRASE = "GOOD_PASSPHRASE"
+GOODMDC = "GOODMDC"
+GOODSIG = "GOODSIG"
+GOT_IT = "GOT_IT"
+IMPORT_OK = "IMPORT_OK"
+IMPORT_PROBLEM = "IMPORT_PROBLEM"
+IMPORT_RES = "IMPORT_RES"
+IMPORTED = "IMPORTED"
+INQUIRE_MAXLEN = "INQUIRE_MAXLEN"
+INV_RECP = "INV_RECP"
+INV_SGNR = "INV_SGNR"
+KEY_CONSIDERED = "KEY_CONSIDERED"
+KEY_CREATED = "KEY_CREATED"
+KEY_NOT_CREATED = "KEY_NOT_CREATED"
+KEYEXPIRED = "KEYEXPIRED"
+KEYREVOKED = "KEYREVOKED"
+LEAVE = "LEAVE"
+MISSING_PASSPHRASE = "MISSING_PASSPHRASE"
+MOUNTPOINT = "MOUNTPOINT"
+NEED_PASSPHRASE = "NEED_PASSPHRASE"
+NEED_PASSPHRASE_PIN = "NEED_PASSPHRASE_PIN"
+NEED_PASSPHRASE_SYM = "NEED_PASSPHRASE_SYM"
+NEWSIG = "NEWSIG"
+NO_PUBKEY = "NO_PUBKEY"
+NO_RECP = "NO_RECP"
+NO_SECKEY = "NO_SECKEY"
+NO_SGNR = "NO_SGNR"
+NODATA = "NODATA"
+NOTATION_DATA = "NOTATION_DATA"
+NOTATION_FLAGS = "NOTATION_FLAGS"
+NOTATION_NAME = "NOTATION_NAME"
+PINENTRY_LAUNCHED = "PINENTRY_LAUNCHED"
+PKA_TRUST_BAD = "PKA_TRUST_BAD"
+PKA_TRUST_GOOD = "PKA_TRUST_GOOD"
+PLAINTEXT = "PLAINTEXT"
+PLAINTEXT_LENGTH = "PLAINTEXT_LENGTH"
+POLICY_URL = "POLICY_URL"
+PROGRESS = "PROGRESS"
+REVKEYSIG = "REVKEYSIG"
+RSA_OR_IDEA = "RSA_OR_IDEA"
+SC_OP_FAILURE = "SC_OP_FAILURE"
+SC_OP_SUCCESS = "SC_OP_SUCCESS"
+SESSION_KEY = "SESSION_KEY"
+SHM_GET = "SHM_GET"
+SHM_GET_BOOL = "SHM_GET_BOOL"
+SHM_GET_HIDDEN = "SHM_GET_HIDDEN"
+SHM_INFO = "SHM_INFO"
+SIG_CREATED = "SIG_CREATED"
+SIG_ID = "SIG_ID"
+SIG_SUBPACKET = "SIG_SUBPACKET"
+SIGEXPIRED = "SIGEXPIRED"
+SUCCESS = "SUCCESS"
+TOFU_STATS = "TOFU_STATS"
+TOFU_STATS_LONG = "TOFU_STATS_LONG"
+TOFU_USER = "TOFU_USER"
+TRUNCATED = "TRUNCATED"
+TRUST_FULLY = "TRUST_FULLY"
+TRUST_MARGINAL = "TRUST_MARGINAL"
+TRUST_NEVER = "TRUST_NEVER"
+TRUST_ULTIMATE = "TRUST_ULTIMATE"
+TRUST_UNDEFINED = "TRUST_UNDEFINED"
+UNEXPECTED = "UNEXPECTED"
+USERID_HINT = "USERID_HINT"
+VALIDSIG = "VALIDSIG"
diff --git a/lang/python/gpg/constants/validity.py b/lang/python/gpg/constants/validity.py
new file mode 100644
index 0000000..d3c5345
--- /dev/null
+++ b/lang/python/gpg/constants/validity.py
@@ -0,0 +1,23 @@
+# Copyright (C) 2004 Igor Belyi <belyi@users.sourceforge.net>
+# Copyright (C) 2002 John Goerzen <jgoerzen@complete.org>
+#
+# This library 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.
+#
+# This library 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 library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+from gpg import util
+util.process_constants('GPGME_VALIDITY_', globals())
+del util
diff --git a/lang/python/gpg/core.py b/lang/python/gpg/core.py
new file mode 100644
index 0000000..748bcbb
--- /dev/null
+++ b/lang/python/gpg/core.py
@@ -0,0 +1,1164 @@
+# Copyright (C) 2016 g10 Code GmbH
+# Copyright (C) 2004,2008 Igor Belyi <belyi@users.sourceforge.net>
+# Copyright (C) 2002 John Goerzen <jgoerzen@complete.org>
+#
+# This library 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.
+#
+# This library 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 library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+"""Core functionality
+
+Core functionality of GPGME wrapped in a object-oriented fashion.
+Provides the 'Context' class for performing cryptographic operations,
+and the 'Data' class describing buffers of data.
+
+"""
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+import re
+import os
+import warnings
+import weakref
+from . import gpgme
+from .errors import errorcheck, GPGMEError
+from . import constants
+from . import errors
+from . import util
+
+class GpgmeWrapper(object):
+ """Base wrapper class
+
+ Not to be instantiated directly.
+
+ """
+
+ def __init__(self, wrapped):
+ self._callback_excinfo = None
+ self.wrapped = wrapped
+
+ def __repr__(self):
+ return '<{}/{!r}>'.format(super(GpgmeWrapper, self).__repr__(),
+ self.wrapped)
+
+ def __str__(self):
+ acc = ['{}.{}'.format(__name__, self.__class__.__name__)]
+ flags = [f for f in self._boolean_properties if getattr(self, f)]
+ if flags:
+ acc.append('({})'.format(' '.join(flags)))
+
+ return '<{}>'.format(' '.join(acc))
+
+ def __hash__(self):
+ return hash(repr(self.wrapped))
+
+ def __eq__(self, other):
+ if other == None:
+ return False
+ else:
+ return repr(self.wrapped) == repr(other.wrapped)
+
+ @property
+ def _ctype(self):
+ """The name of the c type wrapped by this class
+
+ Must be set by child classes.
+
+ """
+ raise NotImplementedError()
+
+ @property
+ def _cprefix(self):
+ """The common prefix of c functions wrapped by this class
+
+ Must be set by child classes.
+
+ """
+ raise NotImplementedError()
+
+ def _errorcheck(self, name):
+ """Must be implemented by child classes.
+
+ This function must return a trueish value for all c functions
+ returning gpgme_error_t."""
+ raise NotImplementedError()
+
+ """The set of all boolean properties"""
+ _boolean_properties = set()
+
+ def __wrap_boolean_property(self, key, do_set=False, value=None):
+ get_func = getattr(gpgme,
+ "{}get_{}".format(self._cprefix, key))
+ set_func = getattr(gpgme,
+ "{}set_{}".format(self._cprefix, key))
+ def get(slf):
+ return bool(get_func(slf.wrapped))
+ def set_(slf, value):
+ set_func(slf.wrapped, bool(value))
+
+ p = property(get, set_, doc="{} flag".format(key))
+ setattr(self.__class__, key, p)
+
+ if do_set:
+ set_(self, bool(value))
+ else:
+ return get(self)
+
+ _munge_docstring = re.compile(r'gpgme_([^(]*)\(([^,]*), (.*\) -> .*)')
+ def __getattr__(self, key):
+ """On-the-fly generation of wrapper methods and properties"""
+ if key[0] == '_' or self._cprefix == None:
+ return None
+
+ if key in self._boolean_properties:
+ return self.__wrap_boolean_property(key)
+
+ name = self._cprefix + key
+ func = getattr(gpgme, name)
+
+ if self._errorcheck(name):
+ def _funcwrap(slf, *args):
+ result = func(slf.wrapped, *args)
+ if slf._callback_excinfo:
+ gpgme.gpg_raise_callback_exception(slf)
+ return errorcheck(result, "Invocation of " + name)
+ else:
+ def _funcwrap(slf, *args):
+ result = func(slf.wrapped, *args)
+ if slf._callback_excinfo:
+ gpgme.gpg_raise_callback_exception(slf)
+ return result
+
+ doc = self._munge_docstring.sub(r'\2.\1(\3', getattr(func, "__doc__"))
+ _funcwrap.__doc__ = doc
+
+ # Monkey-patch the class.
+ setattr(self.__class__, key, _funcwrap)
+
+ # Bind the method to 'self'.
+ def wrapper(*args):
+ return _funcwrap(self, *args)
+ wrapper.__doc__ = doc
+
+ return wrapper
+
+ def __setattr__(self, key, value):
+ """On-the-fly generation of properties"""
+ if key in self._boolean_properties:
+ self.__wrap_boolean_property(key, True, value)
+ else:
+ super(GpgmeWrapper, self).__setattr__(key, value)
+
+class Context(GpgmeWrapper):
+ """Context for cryptographic operations
+
+ All cryptographic operations in GPGME are performed within a
+ context, which contains the internal state of the operation as
+ well as configuration parameters. By using several contexts you
+ can run several cryptographic operations in parallel, with
+ different configuration.
+
+ Access to a context must be synchronized.
+
+ """
+
+ def __init__(self, armor=False, textmode=False, offline=False,
+ signers=[], pinentry_mode=constants.PINENTRY_MODE_DEFAULT,
+ protocol=constants.PROTOCOL_OpenPGP,
+ wrapped=None):
+ """Construct a context object
+
+ Keyword arguments:
+ armor -- enable ASCII armoring (default False)
+ textmode -- enable canonical text mode (default False)
+ offline -- do not contact external key sources (default False)
+ signers -- list of keys used for signing (default [])
+ pinentry_mode -- pinentry mode (default PINENTRY_MODE_DEFAULT)
+ protocol -- protocol to use (default PROTOCOL_OpenPGP)
+
+ """
+ if wrapped:
+ self.own = False
+ else:
+ tmp = gpgme.new_gpgme_ctx_t_p()
+ errorcheck(gpgme.gpgme_new(tmp))
+ wrapped = gpgme.gpgme_ctx_t_p_value(tmp)
+ gpgme.delete_gpgme_ctx_t_p(tmp)
+ self.own = True
+ super(Context, self).__init__(wrapped)
+ self.armor = armor
+ self.textmode = textmode
+ self.offline = offline
+ self.signers = signers
+ self.pinentry_mode = pinentry_mode
+ self.protocol = protocol
+
+ def encrypt(self, plaintext, recipients=[], sign=True, sink=None,
+ passphrase=None, always_trust=False, add_encrypt_to=False,
+ prepare=False, expect_sign=False, compress=True):
+ """Encrypt data
+
+ Encrypt the given plaintext for the given recipients. If the
+ list of recipients is empty, the data is encrypted
+ symmetrically with a passphrase.
+
+ The passphrase can be given as parameter, using a callback
+ registered at the context, or out-of-band via pinentry.
+
+ Keyword arguments:
+ recipients -- list of keys to encrypt to
+ sign -- sign plaintext (default True)
+ sink -- write result to sink instead of returning it
+ passphrase -- for symmetric encryption
+ always_trust -- always trust the keys (default False)
+ add_encrypt_to -- encrypt to configured additional keys (default False)
+ prepare -- (ui) prepare for encryption (default False)
+ expect_sign -- (ui) prepare for signing (default False)
+ compress -- compress plaintext (default True)
+
+ Returns:
+ ciphertext -- the encrypted data (or None if sink is given)
+ result -- additional information about the encryption
+ sign_result -- additional information about the signature(s)
+
+ Raises:
+ InvalidRecipients -- if encryption using a particular key failed
+ InvalidSigners -- if signing using a particular key failed
+ GPGMEError -- as signaled by the underlying library
+
+ """
+ ciphertext = sink if sink else Data()
+ flags = 0
+ flags |= always_trust * constants.ENCRYPT_ALWAYS_TRUST
+ flags |= (not add_encrypt_to) * constants.ENCRYPT_NO_ENCRYPT_TO
+ flags |= prepare * constants.ENCRYPT_PREPARE
+ flags |= expect_sign * constants.ENCRYPT_EXPECT_SIGN
+ flags |= (not compress) * constants.ENCRYPT_NO_COMPRESS
+
+ if passphrase != None:
+ old_pinentry_mode = self.pinentry_mode
+ old_passphrase_cb = getattr(self, '_passphrase_cb', None)
+ self.pinentry_mode = constants.PINENTRY_MODE_LOOPBACK
+ def passphrase_cb(hint, desc, prev_bad, hook=None):
+ return passphrase
+ self.set_passphrase_cb(passphrase_cb)
+
+ try:
+ if sign:
+ self.op_encrypt_sign(recipients, flags, plaintext, ciphertext)
+ else:
+ self.op_encrypt(recipients, flags, plaintext, ciphertext)
+ except errors.GPGMEError as e:
+ if e.getcode() == errors.UNUSABLE_PUBKEY:
+ result = self.op_encrypt_result()
+ if result.invalid_recipients:
+ raise errors.InvalidRecipients(result.invalid_recipients)
+ if e.getcode() == errors.UNUSABLE_SECKEY:
+ sig_result = self.op_sign_result()
+ if sig_result.invalid_signers:
+ raise errors.InvalidSigners(sig_result.invalid_signers)
+ raise
+ finally:
+ if passphrase != None:
+ self.pinentry_mode = old_pinentry_mode
+ if old_passphrase_cb:
+ self.set_passphrase_cb(*old_passphrase_cb[1:])
+
+ result = self.op_encrypt_result()
+ assert not result.invalid_recipients
+ sig_result = self.op_sign_result() if sign else None
+ assert not sig_result or not sig_result.invalid_signers
+
+ cipherbytes = None
+ if not sink:
+ ciphertext.seek(0, os.SEEK_SET)
+ cipherbytes = ciphertext.read()
+ return cipherbytes, result, sig_result
+
+ def decrypt(self, ciphertext, sink=None, passphrase=None, verify=True):
+ """Decrypt data
+
+ Decrypt the given ciphertext and verify any signatures. If
+ VERIFY is an iterable of keys, the ciphertext must be signed
+ by all those keys, otherwise an error is raised.
+
+ 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
+ pinentry.
+
+ Keyword arguments:
+ sink -- write result to sink instead of returning it
+ passphrase -- for symmetric decryption
+ verify -- check signatures (default True)
+
+ Returns:
+ plaintext -- the decrypted data (or None if sink is given)
+ result -- additional information about the decryption
+ verify_result -- additional information about the signature(s)
+
+ Raises:
+ UnsupportedAlgorithm -- if an unsupported algorithm was used
+ BadSignatures -- if a bad signature is encountered
+ MissingSignatures -- if expected signatures are missing or bad
+ GPGMEError -- as signaled by the underlying library
+
+ """
+ plaintext = sink if sink else Data()
+
+ if passphrase != None:
+ old_pinentry_mode = self.pinentry_mode
+ old_passphrase_cb = getattr(self, '_passphrase_cb', None)
+ self.pinentry_mode = constants.PINENTRY_MODE_LOOPBACK
+ def passphrase_cb(hint, desc, prev_bad, hook=None):
+ return passphrase
+ self.set_passphrase_cb(passphrase_cb)
+
+ try:
+ if verify:
+ self.op_decrypt_verify(ciphertext, plaintext)
+ else:
+ self.op_decrypt(ciphertext, plaintext)
+ finally:
+ if passphrase != None:
+ self.pinentry_mode = old_pinentry_mode
+ if old_passphrase_cb:
+ self.set_passphrase_cb(*old_passphrase_cb[1:])
+
+ result = self.op_decrypt_result()
+ verify_result = self.op_verify_result() if verify else None
+ if result.unsupported_algorithm:
+ raise errors.UnsupportedAlgorithm(result.unsupported_algorithm)
+
+ if verify:
+ if any(s.status != errors.NO_ERROR
+ for s in verify_result.signatures):
+ raise errors.BadSignatures(verify_result)
+
+ if verify and verify != True:
+ missing = list()
+ for key in verify:
+ ok = False
+ for subkey in key.subkeys:
+ for sig in verify_result.signatures:
+ if sig.summary & constants.SIGSUM_VALID == 0:
+ continue
+ if subkey.can_sign and subkey.fpr == sig.fpr:
+ ok = True
+ break
+ if ok:
+ break
+ if not ok:
+ missing.append(key)
+ if missing:
+ raise errors.MissingSignatures(verify_result, missing)
+
+ plainbytes = None
+ if not sink:
+ plaintext.seek(0, os.SEEK_SET)
+ plainbytes = plaintext.read()
+ return plainbytes, result, verify_result
+
+ def sign(self, data, sink=None, mode=constants.SIG_MODE_NORMAL):
+ """Sign data
+
+ Sign the given data with either the configured default local
+ key, or the 'signers' keys of this context.
+
+ Keyword arguments:
+ mode -- signature mode (default: normal, see below)
+ sink -- write result to sink instead of returning it
+
+ Returns:
+ either
+ signed_data -- encoded data and signature (normal mode)
+ signature -- only the signature data (detached mode)
+ cleartext -- data and signature as text (cleartext mode)
+ (or None if sink is given)
+ result -- additional information about the signature(s)
+
+ Raises:
+ InvalidSigners -- if signing using a particular key failed
+ GPGMEError -- as signaled by the underlying library
+
+ """
+ signeddata = sink if sink else Data()
+
+ try:
+ self.op_sign(data, signeddata, mode)
+ except errors.GPGMEError as e:
+ if e.getcode() == errors.UNUSABLE_SECKEY:
+ result = self.op_sign_result()
+ if result.invalid_signers:
+ raise errors.InvalidSigners(result.invalid_signers)
+ raise
+
+ result = self.op_sign_result()
+ assert not result.invalid_signers
+
+ signedbytes = None
+ if not sink:
+ signeddata.seek(0, os.SEEK_SET)
+ signedbytes = signeddata.read()
+ return signedbytes, result
+
+ def verify(self, signed_data, signature=None, sink=None, verify=[]):
+ """Verify signatures
+
+ Verify signatures over data. If VERIFY is an iterable of
+ keys, the ciphertext must be signed by all those keys,
+ otherwise an error is raised.
+
+ Keyword arguments:
+ signature -- detached signature data
+ sink -- write result to sink instead of returning it
+
+ Returns:
+ data -- the plain data
+ (or None if sink is given, or we verified a detached signature)
+ result -- additional information about the signature(s)
+
+ Raises:
+ BadSignatures -- if a bad signature is encountered
+ MissingSignatures -- if expected signatures are missing or bad
+ GPGMEError -- as signaled by the underlying library
+
+ """
+ if signature:
+ # Detached signature, we don't return the plain text.
+ data = None
+ else:
+ data = sink if sink else Data()
+
+ if signature:
+ self.op_verify(signature, signed_data, None)
+ else:
+ self.op_verify(signed_data, None, data)
+
+ result = self.op_verify_result()
+ if any(s.status != errors.NO_ERROR for s in result.signatures):
+ raise errors.BadSignatures(result)
+
+ missing = list()
+ for key in verify:
+ ok = False
+ for subkey in key.subkeys:
+ for sig in result.signatures:
+ if sig.summary & constants.SIGSUM_VALID == 0:
+ continue
+ if subkey.can_sign and subkey.fpr == sig.fpr:
+ ok = True
+ break
+ if ok:
+ break
+ if not ok:
+ missing.append(key)
+ if missing:
+ raise errors.MissingSignatures(result, missing)
+
+ plainbytes = None
+ if data and not sink:
+ data.seek(0, os.SEEK_SET)
+ plainbytes = data.read()
+ return plainbytes, result
+
+ def keylist(self, pattern=None, secret=False):
+ """List keys
+
+ Keyword arguments:
+ pattern -- return keys matching pattern (default: all keys)
+ secret -- return only secret keys
+
+ Returns:
+ -- an iterator returning key objects
+
+ Raises:
+ GPGMEError -- as signaled by the underlying library
+ """
+ return self.op_keylist_all(pattern, secret)
+
+ def assuan_transact(self, command,
+ data_cb=None, inquire_cb=None, status_cb=None):
+ """Issue a raw assuan command
+
+ This function can be used to issue a raw assuan command to the
+ engine.
+
+ If command is a string or bytes, it will be used as-is. If it
+ is an iterable of strings, it will be properly escaped and
+ joined into an well-formed assuan command.
+
+ Keyword arguments:
+ data_cb -- a callback receiving data lines
+ inquire_cb -- a callback providing more information
+ status_cb -- a callback receiving status lines
+
+ Returns:
+ result -- the result of command as GPGMEError
+
+ Raises:
+ GPGMEError -- as signaled by the underlying library
+
+ """
+
+ if isinstance(command, (str, bytes)):
+ cmd = command
+ else:
+ cmd = " ".join(util.percent_escape(f) for f in command)
+
+ errptr = gpgme.new_gpgme_error_t_p()
+
+ err = gpgme.gpgme_op_assuan_transact_ext(
+ self.wrapped,
+ cmd,
+ (weakref.ref(self), data_cb) if data_cb else None,
+ (weakref.ref(self), inquire_cb) if inquire_cb else None,
+ (weakref.ref(self), status_cb) if status_cb else None,
+ errptr)
+
+ if self._callback_excinfo:
+ gpgme.gpg_raise_callback_exception(self)
+
+ errorcheck(err)
+
+ status = gpgme.gpgme_error_t_p_value(errptr)
+ gpgme.delete_gpgme_error_t_p(errptr)
+
+ return GPGMEError(status) if status != 0 else None
+
+ def interact(self, key, func, sink=None, flags=0, fnc_value=None):
+ """Interact with the engine
+
+ This method can be used to edit keys and cards interactively.
+ KEY is the key to edit, FUNC is called repeatedly with two
+ unicode arguments, 'keyword' and 'args'. See the GPGME manual
+ for details.
+
+ Keyword arguments:
+ sink -- if given, additional output is written here
+ flags -- use constants.INTERACT_CARD to edit a card
+
+ Raises:
+ GPGMEError -- as signaled by the underlying library
+
+ """
+ if key == None:
+ raise ValueError("First argument cannot be None")
+
+ if sink == None:
+ sink = Data()
+
+ if fnc_value:
+ opaquedata = (weakref.ref(self), func, fnc_value)
+ else:
+ opaquedata = (weakref.ref(self), func)
+
+ result = gpgme.gpgme_op_interact(self.wrapped, key, flags,
+ opaquedata, sink)
+ if self._callback_excinfo:
+ gpgme.gpg_raise_callback_exception(self)
+ errorcheck(result)
+
+ @property
+ def signers(self):
+ """Keys used for signing"""
+ return [self.signers_enum(i) for i in range(self.signers_count())]
+ @signers.setter
+ def signers(self, signers):
+ old = self.signers
+ self.signers_clear()
+ try:
+ for key in signers:
+ self.signers_add(key)
+ except:
+ self.signers = old
+ raise
+
+ @property
+ def pinentry_mode(self):
+ """Pinentry mode"""
+ return self.get_pinentry_mode()
+ @pinentry_mode.setter
+ def pinentry_mode(self, value):
+ self.set_pinentry_mode(value)
+
+ @property
+ def protocol(self):
+ """Protocol to use"""
+ return self.get_protocol()
+ @protocol.setter
+ def protocol(self, value):
+ errorcheck(gpgme.gpgme_engine_check_version(value))
+ self.set_protocol(value)
+
+ _ctype = 'gpgme_ctx_t'
+ _cprefix = 'gpgme_'
+
+ def _errorcheck(self, name):
+ """This function should list all functions returning gpgme_error_t"""
+ return ((name.startswith('gpgme_op_')
+ and not name.endswith('_result'))
+ or name in {
+ 'gpgme_set_ctx_flag',
+ 'gpgme_set_protocol',
+ 'gpgme_set_sub_protocol',
+ 'gpgme_set_keylist_mode',
+ 'gpgme_set_pinentry_mode',
+ 'gpgme_set_locale',
+ 'gpgme_set_engine_info',
+ 'gpgme_signers_add',
+ 'gpgme_get_sig_key',
+ 'gpgme_sig_notation_add',
+ 'gpgme_cancel',
+ 'gpgme_cancel_async',
+ 'gpgme_cancel_get_key',
+ })
+
+ _boolean_properties = {'armor', 'textmode', 'offline'}
+
+ def __del__(self):
+ if not gpgme:
+ # At interpreter shutdown, gpgme is set to NONE.
+ return
+
+ self._free_passcb()
+ self._free_progresscb()
+ self._free_statuscb()
+ if self.own and self.wrapped and gpgme.gpgme_release:
+ gpgme.gpgme_release(self.wrapped)
+ self.wrapped = None
+
+ # Implement the context manager protocol.
+ def __enter__(self):
+ return self
+ def __exit__(self, type, value, tb):
+ self.__del__()
+
+ def op_keylist_all(self, *args, **kwargs):
+ self.op_keylist_start(*args, **kwargs)
+ key = self.op_keylist_next()
+ while key:
+ yield key
+ key = self.op_keylist_next()
+ self.op_keylist_end()
+
+ def op_keylist_next(self):
+ """Returns the next key in the list created
+ by a call to op_keylist_start(). The object returned
+ is of type Key."""
+ ptr = gpgme.new_gpgme_key_t_p()
+ try:
+ errorcheck(gpgme.gpgme_op_keylist_next(self.wrapped, ptr))
+ key = gpgme.gpgme_key_t_p_value(ptr)
+ except errors.GPGMEError as excp:
+ key = None
+ if excp.getcode() != errors.EOF:
+ raise excp
+ gpgme.delete_gpgme_key_t_p(ptr)
+ if key:
+ key.__del__ = lambda self: gpgme.gpgme_key_unref(self)
+ return key
+
+ def get_key(self, fpr, secret=False):
+ """Get a key given a fingerprint
+
+ Keyword arguments:
+ secret -- to request a secret key
+
+ Returns:
+ -- the matching key
+
+ Raises:
+ KeyError -- if the key was not found
+ GPGMEError -- as signaled by the underlying library
+
+ """
+ ptr = gpgme.new_gpgme_key_t_p()
+
+ try:
+ errorcheck(gpgme.gpgme_get_key(self.wrapped, fpr, ptr, secret))
+ except errors.GPGMEError as e:
+ if e.getcode() == errors.EOF:
+ raise errors.KeyNotFound(fpr)
+ raise e
+
+ key = gpgme.gpgme_key_t_p_value(ptr)
+ gpgme.delete_gpgme_key_t_p(ptr)
+ assert key
+ key.__del__ = lambda self: gpgme.gpgme_key_unref(self)
+ return key
+
+ def op_trustlist_all(self, *args, **kwargs):
+ self.op_trustlist_start(*args, **kwargs)
+ trust = self.op_trustlist_next()
+ while trust:
+ yield trust
+ trust = self.op_trustlist_next()
+ self.op_trustlist_end()
+
+ def op_trustlist_next(self):
+ """Returns the next trust item in the list created
+ by a call to op_trustlist_start(). The object returned
+ is of type TrustItem."""
+ ptr = gpgme.new_gpgme_trust_item_t_p()
+ try:
+ errorcheck(gpgme.gpgme_op_trustlist_next(self.wrapped, ptr))
+ trust = gpgme.gpgme_trust_item_t_p_value(ptr)
+ except errors.GPGMEError as excp:
+ trust = None
+ if excp.getcode() != errors.EOF:
+ raise
+ gpgme.delete_gpgme_trust_item_t_p(ptr)
+ return trust
+
+ def set_passphrase_cb(self, func, hook=None):
+ """Sets the passphrase callback to the function specified by func.
+
+ When the system needs a passphrase, it will call func with three args:
+ hint, a string describing the key it needs the passphrase for;
+ desc, a string describing the passphrase it needs;
+ prev_bad, a boolean equal True if this is a call made after
+ unsuccessful previous attempt.
+
+ If hook has a value other than None it will be passed into the func
+ as a forth argument.
+
+ Please see the GPGME manual for more information.
+ """
+ if func == None:
+ hookdata = None
+ else:
+ if hook == None:
+ hookdata = (weakref.ref(self), func)
+ else:
+ hookdata = (weakref.ref(self), func, hook)
+ gpgme.gpg_set_passphrase_cb(self, hookdata)
+
+ def _free_passcb(self):
+ if gpgme.gpg_set_passphrase_cb:
+ self.set_passphrase_cb(None)
+
+ def set_progress_cb(self, func, hook=None):
+ """Sets the progress meter callback to the function specified by FUNC.
+ If FUNC is None, the callback will be cleared.
+
+ This function will be called to provide an interactive update
+ of the system's progress. The function will be called with
+ three arguments, type, total, and current. If HOOK is not
+ None, it will be supplied as fourth argument.
+
+ Please see the GPGME manual for more information.
+
+ """
+ if func == None:
+ hookdata = None
+ else:
+ if hook == None:
+ hookdata = (weakref.ref(self), func)
+ else:
+ hookdata = (weakref.ref(self), func, hook)
+ gpgme.gpg_set_progress_cb(self, hookdata)
+
+ def _free_progresscb(self):
+ if gpgme.gpg_set_progress_cb:
+ self.set_progress_cb(None)
+
+ def set_status_cb(self, func, hook=None):
+ """Sets the status callback to the function specified by FUNC. If
+ FUNC is None, the callback will be cleared.
+
+ The function will be called with two arguments, keyword and
+ args. If HOOK is not None, it will be supplied as third
+ argument.
+
+ Please see the GPGME manual for more information.
+
+ """
+ if func == None:
+ hookdata = None
+ else:
+ if hook == None:
+ hookdata = (weakref.ref(self), func)
+ else:
+ hookdata = (weakref.ref(self), func, hook)
+ gpgme.gpg_set_status_cb(self, hookdata)
+
+ def _free_statuscb(self):
+ if gpgme.gpg_set_status_cb:
+ self.set_status_cb(None)
+
+ @property
+ def engine_info(self):
+ """Configuration of the engine currently in use"""
+ p = self.protocol
+ infos = [i for i in self.get_engine_info() if i.protocol == p]
+ assert len(infos) == 1
+ return infos[0]
+
+ def get_engine_info(self):
+ """Get engine configuration
+
+ Returns information about all configured and installed
+ engines.
+
+ Returns:
+ infos -- a list of engine infos
+
+ """
+ return gpgme.gpgme_ctx_get_engine_info(self.wrapped)
+
+ def set_engine_info(self, proto, file_name=None, home_dir=None):
+ """Change engine configuration
+
+ Changes the configuration of the crypto engine implementing
+ the protocol 'proto' for the context.
+
+ Keyword arguments:
+ file_name -- engine program file name (unchanged if None)
+ home_dir -- configuration directory (unchanged if None)
+
+ """
+ errorcheck(gpgme.gpgme_ctx_set_engine_info(
+ self.wrapped, proto, file_name, home_dir))
+
+ def wait(self, hang):
+ """Wait for asynchronous call to finish. Wait forever if hang is True.
+ Raises an exception on errors.
+
+ Please read the GPGME manual for more information.
+
+ """
+ ptr = gpgme.new_gpgme_error_t_p()
+ gpgme.gpgme_wait(self.wrapped, ptr, hang)
+ status = gpgme.gpgme_error_t_p_value(ptr)
+ gpgme.delete_gpgme_error_t_p(ptr)
+ errorcheck(status)
+
+ def op_edit(self, key, func, fnc_value, out):
+ """Start key editing using supplied callback function
+
+ Note: This interface is deprecated and will be removed with
+ GPGME 1.8. Please use .interact instead. Furthermore, we
+ implement this using gpgme_op_interact, so callbacks will get
+ called with string keywords instead of numeric status
+ messages. Code that is using constants.STATUS_X or
+ constants.status.X will continue to work, whereas code using
+ magic numbers will break as a result.
+
+ """
+ warnings.warn("Call to deprecated method op_edit.",
+ category=DeprecationWarning)
+ return self.interact(key, func, sink=out, fnc_value=fnc_value)
+
+
+class Data(GpgmeWrapper):
+ """Data buffer
+
+ A lot of data has to be exchanged between the user and the crypto
+ engine, like plaintext messages, ciphertext, signatures and
+ information about the keys. The technical details about
+ exchanging the data information are completely abstracted by
+ GPGME. The user provides and receives the data via `gpgme_data_t'
+ objects, regardless of the communication protocol between GPGME
+ and the crypto engine in use.
+
+ This Data class is the implementation of the GpgmeData objects.
+
+ Please see the information about __init__ for instantiation.
+
+ """
+
+ _ctype = 'gpgme_data_t'
+ _cprefix = 'gpgme_data_'
+
+ def _errorcheck(self, name):
+ """This function should list all functions returning gpgme_error_t"""
+ return name not in {
+ 'gpgme_data_release_and_get_mem',
+ 'gpgme_data_get_encoding',
+ 'gpgme_data_seek',
+ 'gpgme_data_get_file_name',
+ }
+
+ def __init__(self, string=None, file=None, offset=None,
+ length=None, cbs=None, copy=True):
+ """Initialize a new gpgme_data_t object.
+
+ If no args are specified, make it an empty object.
+
+ If string alone is specified, initialize it with the data
+ contained there.
+
+ If file, offset, and length are all specified, file must
+ be either a filename or a file-like object, and the object
+ will be initialized by reading the specified chunk from the file.
+
+ If cbs is specified, it MUST be a tuple of the form:
+
+ (read_cb, write_cb, seek_cb, release_cb[, hook])
+
+ where the first four items are functions implementing reading,
+ writing, seeking the data, and releasing any resources once
+ the data object is deallocated. The functions must match the
+ following prototypes:
+
+ def read(amount, hook=None):
+ return <a b"bytes" object>
+
+ def write(data, hook=None):
+ return <the number of bytes written>
+
+ def seek(offset, whence, hook=None):
+ return <the new file position>
+
+ def release(hook=None):
+ <return value and exceptions are ignored>
+
+ The functions may be bound methods. In that case, you can
+ simply use the 'self' reference instead of using a hook.
+
+ If file is specified without any other arguments, then
+ it must be a filename, and the object will be initialized from
+ that file.
+
+ """
+ super(Data, self).__init__(None)
+ self.data_cbs = None
+
+ if cbs != None:
+ self.new_from_cbs(*cbs)
+ elif string != None:
+ self.new_from_mem(string, copy)
+ elif file != None and offset != None and length != None:
+ self.new_from_filepart(file, offset, length)
+ elif file != None:
+ if util.is_a_string(file):
+ self.new_from_file(file, copy)
+ else:
+ self.new_from_fd(file)
+ else:
+ self.new()
+
+ def __del__(self):
+ if not gpgme:
+ # At interpreter shutdown, gpgme is set to NONE.
+ return
+
+ if self.wrapped != None and gpgme.gpgme_data_release:
+ gpgme.gpgme_data_release(self.wrapped)
+ if self._callback_excinfo:
+ gpgme.gpg_raise_callback_exception(self)
+ self.wrapped = None
+ self._free_datacbs()
+
+ # Implement the context manager protocol.
+ def __enter__(self):
+ return self
+ def __exit__(self, type, value, tb):
+ self.__del__()
+
+ def _free_datacbs(self):
+ self._data_cbs = None
+
+ def new(self):
+ tmp = gpgme.new_gpgme_data_t_p()
+ errorcheck(gpgme.gpgme_data_new(tmp))
+ self.wrapped = gpgme.gpgme_data_t_p_value(tmp)
+ gpgme.delete_gpgme_data_t_p(tmp)
+
+ def new_from_mem(self, string, copy=True):
+ tmp = gpgme.new_gpgme_data_t_p()
+ errorcheck(gpgme.gpgme_data_new_from_mem(tmp,string,len(string),copy))
+ self.wrapped = gpgme.gpgme_data_t_p_value(tmp)
+ gpgme.delete_gpgme_data_t_p(tmp)
+
+ def new_from_file(self, filename, copy=True):
+ tmp = gpgme.new_gpgme_data_t_p()
+ try:
+ errorcheck(gpgme.gpgme_data_new_from_file(tmp, filename, copy))
+ except errors.GPGMEError as e:
+ if e.getcode() == errors.INV_VALUE and not copy:
+ raise ValueError("delayed reads are not yet supported")
+ else:
+ raise e
+ self.wrapped = gpgme.gpgme_data_t_p_value(tmp)
+ gpgme.delete_gpgme_data_t_p(tmp)
+
+ def new_from_cbs(self, read_cb, write_cb, seek_cb, release_cb, hook=None):
+ tmp = gpgme.new_gpgme_data_t_p()
+ if hook != None:
+ hookdata = (weakref.ref(self),
+ read_cb, write_cb, seek_cb, release_cb, hook)
+ else:
+ hookdata = (weakref.ref(self),
+ read_cb, write_cb, seek_cb, release_cb)
+ gpgme.gpg_data_new_from_cbs(self, hookdata, tmp)
+ self.wrapped = gpgme.gpgme_data_t_p_value(tmp)
+ gpgme.delete_gpgme_data_t_p(tmp)
+
+ def new_from_filepart(self, file, offset, length):
+ """This wraps the GPGME gpgme_data_new_from_filepart() function.
+ The argument "file" may be:
+
+ * a string specifying a file name, or
+ * a file-like object supporting the fileno() and the mode attribute.
+
+ """
+
+ tmp = gpgme.new_gpgme_data_t_p()
+ filename = None
+ fp = None
+
+ if util.is_a_string(file):
+ filename = file
+ else:
+ fp = gpgme.fdopen(file.fileno(), file.mode)
+ if fp == None:
+ raise ValueError("Failed to open file from %s arg %s" % \
+ (str(type(file)), str(file)))
+
+ errorcheck(gpgme.gpgme_data_new_from_filepart(tmp, filename, fp,
+ offset, length))
+ self.wrapped = gpgme.gpgme_data_t_p_value(tmp)
+ gpgme.delete_gpgme_data_t_p(tmp)
+
+ def new_from_fd(self, file):
+ """This wraps the GPGME gpgme_data_new_from_fd() function. The
+ argument "file" must be a file-like object, supporting the
+ fileno() method.
+
+ """
+ tmp = gpgme.new_gpgme_data_t_p()
+ errorcheck(gpgme.gpgme_data_new_from_fd(tmp, file.fileno()))
+ self.wrapped = gpgme.gpgme_data_t_p_value(tmp)
+ gpgme.delete_gpgme_data_t_p(tmp)
+
+ def new_from_stream(self, file):
+ """This wrap around gpgme_data_new_from_stream is an alias for
+ new_from_fd() method since in python there's not difference
+ between file stream and file descriptor"""
+ self.new_from_fd(file)
+
+ def write(self, buffer):
+ """Write buffer given as string or bytes.
+
+ If a string is given, it is implicitly encoded using UTF-8."""
+ written = gpgme.gpgme_data_write(self.wrapped, buffer)
+ if written < 0:
+ if self._callback_excinfo:
+ gpgme.gpg_raise_callback_exception(self)
+ else:
+ raise GPGMEError.fromSyserror()
+ return written
+
+ def read(self, size = -1):
+ """Read at most size bytes, returned as bytes.
+
+ If the size argument is negative or omitted, read until EOF is reached.
+
+ Returns the data read, or the empty string if there was no data
+ to read before EOF was reached."""
+
+ if size == 0:
+ return ''
+
+ if size > 0:
+ try:
+ result = gpgme.gpgme_data_read(self.wrapped, size)
+ except:
+ if self._callback_excinfo:
+ gpgme.gpg_raise_callback_exception(self)
+ else:
+ raise
+ return result
+ else:
+ chunks = []
+ while True:
+ try:
+ result = gpgme.gpgme_data_read(self.wrapped, 4096)
+ except:
+ if self._callback_excinfo:
+ gpgme.gpg_raise_callback_exception(self)
+ else:
+ raise
+ if len(result) == 0:
+ break
+ chunks.append(result)
+ return b''.join(chunks)
+
+def pubkey_algo_name(algo):
+ return gpgme.gpgme_pubkey_algo_name(algo)
+
+def hash_algo_name(algo):
+ return gpgme.gpgme_hash_algo_name(algo)
+
+def get_protocol_name(proto):
+ return gpgme.gpgme_get_protocol_name(proto)
+
+def check_version(version=None):
+ return gpgme.gpgme_check_version(version)
+
+# check_version also makes sure that several subsystems are properly
+# initialized, and it must be run at least once before invoking any
+# other function. We do it here so that the user does not have to do
+# it unless she really wants to check for a certain version.
+check_version()
+
+def engine_check_version (proto):
+ try:
+ errorcheck(gpgme.gpgme_engine_check_version(proto))
+ return True
+ except errors.GPGMEError:
+ return False
+
+def get_engine_info():
+ ptr = gpgme.new_gpgme_engine_info_t_p()
+ try:
+ errorcheck(gpgme.gpgme_get_engine_info(ptr))
+ info = gpgme.gpgme_engine_info_t_p_value(ptr)
+ except errors.GPGMEError:
+ info = None
+ gpgme.delete_gpgme_engine_info_t_p(ptr)
+ return info
+
+def set_engine_info(proto, file_name, home_dir=None):
+ """Changes the default configuration of the crypto engine implementing
+ the protocol 'proto'. 'file_name' is the file name of
+ the executable program implementing this protocol. 'home_dir' is the
+ directory name of the configuration directory (engine's default is
+ used if omitted)."""
+ errorcheck(gpgme.gpgme_set_engine_info(proto, file_name, home_dir))
+
+def set_locale(category, value):
+ """Sets the default locale used by contexts"""
+ errorcheck(gpgme.gpgme_set_locale(None, category, value))
+
+def wait(hang):
+ """Wait for asynchronous call on any Context to finish.
+ Wait forever if hang is True.
+
+ For finished anynch calls it returns a tuple (status, context):
+ status - status return by asnynchronous call.
+ context - context which caused this call to return.
+
+ Please read the GPGME manual of more information."""
+ ptr = gpgme.new_gpgme_error_t_p()
+ context = gpgme.gpgme_wait(None, ptr, hang)
+ status = gpgme.gpgme_error_t_p_value(ptr)
+ gpgme.delete_gpgme_error_t_p(ptr)
+ if context == None:
+ errorcheck(status)
+ else:
+ context = Context(context)
+ return (status, context)
diff --git a/lang/python/gpg/errors.py b/lang/python/gpg/errors.py
new file mode 100644
index 0000000..1ce139e
--- /dev/null
+++ b/lang/python/gpg/errors.py
@@ -0,0 +1,128 @@
+# Copyright (C) 2004 Igor Belyi <belyi@users.sourceforge.net>
+# Copyright (C) 2002 John Goerzen <jgoerzen@complete.org>
+#
+# This library 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.
+#
+# This library 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 library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+from . import gpgme
+from . import util
+
+# To appease static analysis tools, we define some constants here.
+# They are overwritten with the proper values by process_constants.
+NO_ERROR = None
+EOF = None
+
+util.process_constants('GPG_ERR_', globals())
+del util
+
+class GpgError(Exception):
+ pass
+
+class GPGMEError(GpgError):
+ def __init__(self, error = None, message = None):
+ self.error = error
+ self.message = message
+
+ @classmethod
+ def fromSyserror(cls):
+ return cls(gpgme.gpgme_err_code_from_syserror())
+
+ def getstring(self):
+ message = "%s: %s" % (gpgme.gpgme_strsource(self.error),
+ gpgme.gpgme_strerror(self.error))
+ if self.message != None:
+ message = "%s: %s" % (self.message, message)
+ return message
+
+ def getcode(self):
+ return gpgme.gpgme_err_code(self.error)
+
+ def getsource(self):
+ return gpgme.gpgme_err_source(self.error)
+
+ def __str__(self):
+ return self.getstring()
+
+def errorcheck(retval, extradata = None):
+ if retval:
+ raise GPGMEError(retval, extradata)
+
+class KeyNotFound(GPGMEError, KeyError):
+ """Raised if a key was not found
+
+ GPGME indicates this condition with EOF, which is not very
+ idiomatic. We raise this error that is both a GPGMEError
+ indicating EOF, and a KeyError.
+
+ """
+ def __init__(self, keystr):
+ self.keystr = keystr
+ GPGMEError.__init__(self, EOF)
+ def __str__(self):
+ return self.keystr
+
+# These errors are raised in the idiomatic interface code.
+
+class EncryptionError(GpgError):
+ pass
+
+class InvalidRecipients(EncryptionError):
+ def __init__(self, recipients):
+ self.recipients = recipients
+ def __str__(self):
+ return ", ".join("{}: {}".format(r.fpr,
+ gpgme.gpgme_strerror(r.reason))
+ for r in self.recipients)
+
+class DeryptionError(GpgError):
+ pass
+
+class UnsupportedAlgorithm(DeryptionError):
+ def __init__(self, algorithm):
+ self.algorithm = algorithm
+ def __str__(self):
+ return self.algorithm
+
+class SigningError(GpgError):
+ pass
+
+class InvalidSigners(SigningError):
+ def __init__(self, signers):
+ self.signers = signers
+ def __str__(self):
+ return ", ".join("{}: {}".format(s.fpr,
+ gpgme.gpgme_strerror(s.reason))
+ for s in self.signers)
+
+class VerificationError(GpgError):
+ pass
+
+class BadSignatures(VerificationError):
+ def __init__(self, result):
+ self.result = result
+ def __str__(self):
+ return ", ".join("{}: {}".format(s.fpr,
+ gpgme.gpgme_strerror(s.status))
+ for s in self.result.signatures
+ if s.status != NO_ERROR)
+
+class MissingSignatures(VerificationError):
+ def __init__(self, result, missing):
+ self.result = result
+ self.missing = missing
+ def __str__(self):
+ return ", ".join(k.subkeys[0].fpr for k in self.missing)
diff --git a/lang/python/gpg/gpgme.py b/lang/python/gpg/gpgme.py
new file mode 100644
index 0000000..238359d
--- /dev/null
+++ b/lang/python/gpg/gpgme.py
@@ -0,0 +1,126 @@
+# This file was automatically generated by SWIG (http://www.swig.org).
+# Version 3.0.7
+#
+# Do not make changes to this file unless you know what you are doing--modify
+# the SWIG interface file instead.
+
+
+
+
+
+from sys import version_info
+if version_info >= (2, 6, 0):
+ def swig_import_helper():
+ from os.path import dirname
+ import imp
+ fp = None
+ try:
+ fp, pathname, description = imp.find_module('_gpgme', [dirname(__file__)])
+ except ImportError:
+ import _gpgme
+ return _gpgme
+ if fp is not None:
+ try:
+ _mod = imp.load_module('_gpgme', fp, pathname, description)
+ finally:
+ fp.close()
+ return _mod
+ _gpgme = swig_import_helper()
+ del swig_import_helper
+else:
+ import _gpgme
+del version_info
+from _gpgme import *
+try:
+ _swig_property = property
+except NameError:
+ pass # Python < 2.2 doesn't have 'property'.
+
+
+def _swig_setattr_nondynamic(self, class_type, name, value, static=1):
+ if (name == "thisown"):
+ return self.this.own(value)
+ if (name == "this"):
+ if type(value).__name__ == 'SwigPyObject':
+ self.__dict__[name] = value
+ return
+ method = class_type.__swig_setmethods__.get(name, None)
+ if method:
+ return method(self, value)
+ if (not static):
+ if _newclass:
+ object.__setattr__(self, name, value)
+ else:
+ self.__dict__[name] = value
+ else:
+ raise AttributeError("You cannot add attributes to %s" % self)
+
+
+def _swig_setattr(self, class_type, name, value):
+ return _swig_setattr_nondynamic(self, class_type, name, value, 0)
+
+
+def _swig_getattr_nondynamic(self, class_type, name, static=1):
+ if (name == "thisown"):
+ return self.this.own()
+ method = class_type.__swig_getmethods__.get(name, None)
+ if method:
+ return method(self)
+ if (not static):
+ return object.__getattr__(self, name)
+ else:
+ raise AttributeError(name)
+
+def _swig_getattr(self, class_type, name):
+ return _swig_getattr_nondynamic(self, class_type, name, 0)
+
+
+def _swig_repr(self):
+ try:
+ strthis = "proxy of " + self.this.__repr__()
+ except:
+ strthis = ""
+ return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,)
+
+try:
+ _object = object
+ _newclass = 1
+except AttributeError:
+ class _object:
+ pass
+ _newclass = 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# This file is compatible with both classic and new-style classes.
+
+
diff --git a/lang/python/gpg/results.py b/lang/python/gpg/results.py
new file mode 100644
index 0000000..3383896
--- /dev/null
+++ b/lang/python/gpg/results.py
@@ -0,0 +1,118 @@
+# Robust result objects
+#
+# Copyright (C) 2016 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
+
+"""Robust result objects
+
+Results returned by the underlying library are fragile, i.e. they are
+only valid until the next operation is performed in the context.
+
+We cannot arbitrarily constrain the lifetime of Python objects, we
+therefore create deep copies of the results.
+
+"""
+
+class Result(object):
+ """Result object
+
+ Describes the result of an operation.
+
+ """
+
+ """Convert to types"""
+ _type = {}
+
+ """Map functions over list attributes"""
+ _map = {}
+
+ """Automatically copy unless blacklisted"""
+ _blacklist = {
+ 'acquire', 'append', 'disown', 'next', 'own', 'this', 'thisown',
+ }
+ def __init__(self, fragile):
+ for key, func in self._type.items():
+ if hasattr(fragile, key):
+ setattr(self, key, func(getattr(fragile, key)))
+
+ for key, func in self._map.items():
+ if hasattr(fragile, key):
+ setattr(self, key, list(map(func, getattr(fragile, key))))
+
+ for key in dir(fragile):
+ if key.startswith('_') or key in self._blacklist:
+ continue
+ if hasattr(self, key):
+ continue
+
+ setattr(self, key, getattr(fragile, key))
+
+ def __str__(self):
+ return '<{} {}>'.format(
+ self.__class__.__name__,
+ ', '.join('{}: {}'.format(k, getattr(self, k))
+ for k in dir(self) if not k.startswith('_')))
+
+class InvalidKey(Result):
+ pass
+
+class EncryptResult(Result):
+ _map = dict(invalid_recipients=InvalidKey)
+
+class Recipient(Result):
+ pass
+
+class DecryptResult(Result):
+ _type = dict(wrong_key_usage=bool)
+ _map = dict(recipients=Recipient)
+
+class NewSignature(Result):
+ pass
+
+class SignResult(Result):
+ _map = dict(invalid_signers=InvalidKey, signatures=NewSignature)
+
+class Notation(Result):
+ pass
+
+class Signature(Result):
+ _type = dict(wrong_key_usage=bool, chain_model=bool)
+ _map = dict(notations=Notation)
+
+class VerifyResult(Result):
+ _map = dict(signatures=Signature)
+
+class ImportStatus(Result):
+ pass
+
+class ImportResult(Result):
+ _map = dict(imports=ImportStatus)
+
+class GenkeyResult(Result):
+ _type = dict(primary=bool, sub=bool)
+
+class KeylistResult(Result):
+ _type = dict(truncated=bool)
+
+class VFSMountResult(Result):
+ pass
+
+class EngineInfo(Result):
+ pass
diff --git a/lang/python/gpg/util.py b/lang/python/gpg/util.py
new file mode 100644
index 0000000..e4fca4c
--- /dev/null
+++ b/lang/python/gpg/util.py
@@ -0,0 +1,53 @@
+# Copyright (C) 2016 g10 Code GmbH
+# Copyright (C) 2004,2008 Igor Belyi <belyi@users.sourceforge.net>
+# Copyright (C) 2002 John Goerzen <jgoerzen@complete.org>
+#
+# This library 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.
+#
+# This library 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 library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+import sys
+
+def process_constants(prefix, scope):
+ """Called by the constant modules to load up the constants from the C
+ library starting with PREFIX. Matching constants will be inserted
+ into SCOPE with PREFIX stripped from the names. Returns the names
+ of inserted constants.
+
+ """
+ from . import gpgme
+ index = len(prefix)
+ constants = {identifier[index:]: getattr(gpgme, identifier)
+ for identifier in dir(gpgme)
+ if identifier.startswith(prefix)}
+ scope.update(constants)
+ return list(constants.keys())
+
+def percent_escape(s):
+ return ''.join(
+ '%{0:2x}'.format(ord(c))
+ if c == '+' or c == '"' or c == '%' or ord(c) <= 0x20 else c
+ for c in s)
+
+# Python2/3 compatibility
+if sys.version_info[0] == 3:
+ # Python3
+ def is_a_string(x):
+ return isinstance(x, str)
+else:
+ # Python2
+ def is_a_string(x):
+ return isinstance(x, basestring)
diff --git a/lang/python/gpg/version.py b/lang/python/gpg/version.py
new file mode 100644
index 0000000..9ec657d
--- /dev/null
+++ b/lang/python/gpg/version.py
@@ -0,0 +1,68 @@
+# Copyright (C) 2016 g10 Code GmbH
+# Copyright (C) 2015 Ben McGinnes <ben@adversary.org>
+# Copyright (C) 2004 Igor Belyi <belyi@users.sourceforge.net>
+#
+# This library 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.
+#
+# This library 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 library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+from __future__ import absolute_import, print_function
+del absolute_import, print_function
+
+from . import gpgme
+
+productname = 'gpg'
+versionstr = "1.8.0"
+gpgme_versionstr = gpgme.GPGME_VERSION
+in_tree_build = bool(gpgme.cvar.gpg_in_tree_build)
+
+versionlist = versionstr.split(".")
+major = versionlist[0]
+minor = versionlist[1]
+patch = versionlist[2]
+
+copyright = """\
+Copyright (C) 2016 g10 Code GmbH
+Copyright (C) 2015 Ben McGinnes
+Copyright (C) 2014-2015 Martin Albrecht
+Copyright (C) 2004-2008 Igor Belyi
+Copyright (C) 2002 John Goerzen"""
+
+author = "The GnuPG hackers"
+author_email = "gnupg-devel@gnupg.org"
+
+description = "Python support for GPGME GnuPG cryptography library"
+homepage = "https://gnupg.org"
+
+license = """Copyright (C) 2016 g10 Code GmbH
+Copyright (C) 2015 Ben McGinnes <ben@adversary.org>
+Copyright (C) 2014, 2015 Martin Albrecht <martinralbrecht@googlemail.com>
+Copyright (C) 2004, 2008 Igor Belyi <belyi@users.sourceforge.net>
+Copyright (C) 2002 John Goerzen <jgoerzen@complete.org>
+
+This library 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.
+
+This library 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 library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA"""
+
+# Interface hygiene. Keep this at the end.
+del gpgme
diff --git a/lang/python/gpg/version.py.in b/lang/python/gpg/version.py.in
new file mode 100644
index 0000000..1a1baf0
--- /dev/null
+++ b/lang/python/gpg/version.py.in
@@ -0,0 +1,68 @@
+# Copyright (C) 2016 g10 Code GmbH
+# Copyright (C) 2015 Ben McGinnes <ben@adversary.org>
+# Copyright (C) 2004 Igor Belyi <belyi@users.sourceforge.net>
+#
+# This library 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.
+#
+# This library 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 library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+from __future__ import absolute_import, print_function
+del absolute_import, print_function
+
+from . import gpgme
+
+productname = 'gpg'
+versionstr = "@VERSION@"
+gpgme_versionstr = gpgme.GPGME_VERSION
+in_tree_build = bool(gpgme.cvar.gpg_in_tree_build)
+
+versionlist = versionstr.split(".")
+major = versionlist[0]
+minor = versionlist[1]
+patch = versionlist[2]
+
+copyright = """\
+Copyright (C) 2016 g10 Code GmbH
+Copyright (C) 2015 Ben McGinnes
+Copyright (C) 2014-2015 Martin Albrecht
+Copyright (C) 2004-2008 Igor Belyi
+Copyright (C) 2002 John Goerzen"""
+
+author = "The GnuPG hackers"
+author_email = "gnupg-devel@gnupg.org"
+
+description = "Python support for GPGME GnuPG cryptography library"
+homepage = "https://gnupg.org"
+
+license = """Copyright (C) 2016 g10 Code GmbH
+Copyright (C) 2015 Ben McGinnes <ben@adversary.org>
+Copyright (C) 2014, 2015 Martin Albrecht <martinralbrecht@googlemail.com>
+Copyright (C) 2004, 2008 Igor Belyi <belyi@users.sourceforge.net>
+Copyright (C) 2002 John Goerzen <jgoerzen@complete.org>
+
+This library 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.
+
+This library 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 library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA"""
+
+# Interface hygiene. Keep this at the end.
+del gpgme