aboutsummaryrefslogtreecommitdiffstats
path: root/lang/python
diff options
context:
space:
mode:
Diffstat (limited to 'lang/python')
-rw-r--r--lang/python/README22
-rw-r--r--lang/python/README.org22
-rw-r--r--lang/python/docs/GPGMEpythonHOWTOen.org2271
-rw-r--r--lang/python/examples/assuan.py4
-rw-r--r--lang/python/examples/decryption-filter.py7
-rwxr-xr-xlang/python/examples/delkey.py3
-rwxr-xr-xlang/python/examples/exportimport.py3
-rwxr-xr-xlang/python/examples/genkey.py3
-rwxr-xr-xlang/python/examples/howto/decrypt-file.py4
-rwxr-xr-xlang/python/examples/howto/encrypt-file.py10
-rwxr-xr-xlang/python/examples/howto/encrypt-sign-file.py10
-rwxr-xr-xlang/python/examples/howto/encrypt-to-group-gullible.py81
-rwxr-xr-xlang/python/examples/howto/encrypt-to-group-trustno1.py90
-rwxr-xr-xlang/python/examples/howto/encrypt-to-group.py91
-rwxr-xr-xlang/python/examples/howto/export-key.py73
-rwxr-xr-xlang/python/examples/howto/export-minimised-key.py73
-rwxr-xr-xlang/python/examples/howto/export-secret-key.py77
-rwxr-xr-xlang/python/examples/howto/export-secret-keys.py110
-rw-r--r--lang/python/examples/howto/groups.py18
-rwxr-xr-xlang/python/examples/howto/import-key.py93
-rwxr-xr-xlang/python/examples/howto/import-keys.py70
-rwxr-xr-xlang/python/examples/howto/mutt-groups.py64
-rwxr-xr-xlang/python/examples/howto/pmkey-import-alt.py132
-rwxr-xr-xlang/python/examples/howto/pmkey-import.py116
-rwxr-xr-xlang/python/examples/howto/symcrypt-file.py63
-rwxr-xr-xlang/python/examples/howto/temp-homedir-config.py31
-rw-r--r--lang/python/examples/inter-edit.py12
-rwxr-xr-xlang/python/examples/low_level-encrypt_to_all.py13
-rwxr-xr-xlang/python/examples/sign.py3
-rwxr-xr-xlang/python/examples/signverify.py3
-rwxr-xr-xlang/python/examples/simple.py7
-rw-r--r--lang/python/examples/testCMSgetkey.py4
-rwxr-xr-xlang/python/examples/verifydetails.py22
-rwxr-xr-xlang/python/setup.py.in203
-rw-r--r--lang/python/src/__init__.py10
-rw-r--r--lang/python/src/callbacks.py10
-rw-r--r--lang/python/src/constants/__init__.py22
-rw-r--r--lang/python/src/constants/create.py3
-rw-r--r--lang/python/src/constants/data/__init__.py4
-rw-r--r--lang/python/src/constants/data/encoding.py3
-rw-r--r--lang/python/src/constants/event.py3
-rw-r--r--lang/python/src/constants/import.py3
-rw-r--r--lang/python/src/constants/keylist/__init__.py4
-rw-r--r--lang/python/src/constants/keylist/mode.py3
-rw-r--r--lang/python/src/constants/keysign.py3
-rw-r--r--lang/python/src/constants/md.py3
-rw-r--r--lang/python/src/constants/pk.py3
-rw-r--r--lang/python/src/constants/protocol.py3
-rw-r--r--lang/python/src/constants/sig/__init__.py4
-rw-r--r--lang/python/src/constants/sig/mode.py3
-rw-r--r--lang/python/src/constants/sig/notation.py3
-rw-r--r--lang/python/src/constants/sigsum.py3
-rw-r--r--lang/python/src/constants/tofu/__init__.py3
-rw-r--r--lang/python/src/constants/tofu/policy.py3
-rw-r--r--lang/python/src/constants/validity.py3
-rw-r--r--lang/python/src/core.py539
-rw-r--r--lang/python/src/errors.py51
-rw-r--r--lang/python/src/results.py33
-rw-r--r--lang/python/src/util.py20
-rw-r--r--lang/python/tests/Makefile.am3
-rwxr-xr-xlang/python/tests/final.py11
-rwxr-xr-xlang/python/tests/initial.py9
-rw-r--r--lang/python/tests/run-tests.py93
-rw-r--r--lang/python/tests/support.py29
-rwxr-xr-xlang/python/tests/t-callbacks.py53
-rwxr-xr-xlang/python/tests/t-data.py9
-rwxr-xr-xlang/python/tests/t-decrypt-verify.py26
-rwxr-xr-xlang/python/tests/t-decrypt.py3
-rwxr-xr-xlang/python/tests/t-edit.py21
-rwxr-xr-xlang/python/tests/t-encrypt-large.py13
-rwxr-xr-xlang/python/tests/t-encrypt-sign.py17
-rwxr-xr-xlang/python/tests/t-encrypt-sym.py14
-rwxr-xr-xlang/python/tests/t-encrypt.py36
-rwxr-xr-xlang/python/tests/t-export.py7
-rwxr-xr-xlang/python/tests/t-file-name.py5
-rwxr-xr-xlang/python/tests/t-idiomatic.py13
-rwxr-xr-xlang/python/tests/t-import.py73
-rwxr-xr-xlang/python/tests/t-keylist-from-data.py286
-rwxr-xr-xlang/python/tests/t-keylist.py322
-rwxr-xr-xlang/python/tests/t-protocol-assuan.py13
-rwxr-xr-xlang/python/tests/t-quick-key-creation.py28
-rwxr-xr-xlang/python/tests/t-quick-key-manipulation.py11
-rwxr-xr-xlang/python/tests/t-quick-key-signing.py54
-rwxr-xr-xlang/python/tests/t-quick-subkey-creation.py21
-rwxr-xr-xlang/python/tests/t-sig-notation.py28
-rwxr-xr-xlang/python/tests/t-sign.py12
-rwxr-xr-xlang/python/tests/t-signers.py6
-rwxr-xr-xlang/python/tests/t-trustlist.py13
-rwxr-xr-xlang/python/tests/t-verify.py52
-rwxr-xr-xlang/python/tests/t-wait.py5
-rwxr-xr-xlang/python/tests/t-wrapper.py4
-rw-r--r--lang/python/version.py.in15
92 files changed, 3979 insertions, 1876 deletions
diff --git a/lang/python/README b/lang/python/README
index 99da4dd7..a13345f7 100644
--- a/lang/python/README
+++ b/lang/python/README
@@ -13,7 +13,7 @@ Table of Contents
The "gpg" module is a python interface to the GPGME library:
-[https://www.gnupg.org/software/gpgme/]
+<https://www.gnupg.org/software/gpgme/>
"gpg" offers two interfaces, one is a high-level, curated, and idiomatic
interface that is implemented as a shim on top of the low-level
@@ -27,16 +27,16 @@ functionality of the underlying library.
══════════════
For general discussion and help see the gnupg-users mailing list:
- [https://lists.gnupg.org/mailman/listinfo/gnupg-users]
+ <https://lists.gnupg.org/mailman/listinfo/gnupg-users>
For development see the gnupg-devel mailing list:
- [https://lists.gnupg.org/mailman/listinfo/gnupg-devel]
+ <https://lists.gnupg.org/mailman/listinfo/gnupg-devel>
2 Bugs
══════
- Please report bugs using our bug tracker [https://bugs.gnupg.org] with
+ Please report bugs using our bug tracker <https://bugs.gnupg.org> with
tag (aka project) 'gpgme'.
@@ -44,8 +44,8 @@ functionality of the underlying library.
═════════
PyME was created by John Goerzen, and maintained, developed, and
- cherished by Igor Belyi, Martin Albrecht, Ben McGinnes, and everyone
- who contributed to it in any way.
+ cherished by Igor Belyi, Martin Albrecht, Ben McGinnes, Justus
+ Winter, and everyone who contributed to it in any way.
In 2016 we merged a port of PyME to into the GPGME repository, and
development will continue there. Please see the VCS history for the
@@ -64,14 +64,14 @@ functionality of the underlying library.
• The bindings have been merged into the GPGME repository in 2016.
• The latest version of PyME for Python 3.2 and above (as of May,
- 2015) is v0.9.1. [https://git.gnupg.org/gpgme.git/lang/py3-pyme]
+ 2015) is v0.9.1. <https://git.gnupg.org/gpgme.git/lang/py3-pyme>
• The latest version of PyME for Python 2.6 and 2.7 (as of this
- writing) is v0.9.0. [https://bitbucket.org/malb/pyme]
+ writing) is v0.9.0. <https://bitbucket.org/malb/pyme>
• A previous version of PyME v0.8.0 can be found on sourceforge:
- [http://pyme.sourceforge.net/]
+ <http://pyme.sourceforge.net/>
• A previous version of PyME v0.5.1 which works with GPGME v0.3.15 can
- be found on John Goerzen's PyME page: [http://quux.org/devel/pyme/]
- [http://www.complete.org/JohnGoerzen]
+ be found on John Goerzen's PyME page: <http://quux.org/devel/pyme/>
+ <http://www.complete.org/JohnGoerzen>
diff --git a/lang/python/README.org b/lang/python/README.org
index cba99669..bd7047cc 100644
--- a/lang/python/README.org
+++ b/lang/python/README.org
@@ -2,7 +2,7 @@
#+OPTIONS: author:nil
The "gpg" module is a python interface to the GPGME library:
-[[https://www.gnupg.org/software/gpgme/]]
+[[https://www.gnupg.org/software/gpgme/][https://www.gnupg.org/software/gpgme/]]
"gpg" offers two interfaces, one is a high-level, curated, and
idiomatic interface that is implemented as a shim on top of the
@@ -14,21 +14,21 @@ functionality of the underlying library.
* Mailing List
For general discussion and help see the gnupg-users mailing list:
-https://lists.gnupg.org/mailman/listinfo/gnupg-users
+[[https://lists.gnupg.org/mailman/listinfo/gnupg-users][gnupg-users]]
For development see the gnupg-devel mailing list:
-https://lists.gnupg.org/mailman/listinfo/gnupg-devel
+[[https://lists.gnupg.org/mailman/listinfo/gnupg-devel][gnupg-devel]]
* Bugs
Please report bugs using our bug tracker
-[[https://bugs.gnupg.org]] with tag (aka project) 'gpgme'.
+[[https://bugs.gnupg.org][bugs.gnupg.org]] with tag (aka project) 'gpgme'.
* Authors
PyME was created by John Goerzen, and maintained, developed, and
-cherished by Igor Belyi, Martin Albrecht, Ben McGinnes, and everyone
-who contributed to it in any way.
+cherished by Igor Belyi, Martin Albrecht, Ben McGinnes, Justus Winter,
+and everyone who contributed to it in any way.
In 2016 we merged a port of PyME to into the GPGME repository, and
development will continue there. Please see the VCS history for the
@@ -46,15 +46,15 @@ references to previous versions.
- The latest version of PyME for Python 3.2 and above (as of
May, 2015) is v0.9.1.
- https://git.gnupg.org/gpgme.git/lang/py3-pyme
+ [[https://git.gnupg.org/gpgme.git/lang/py3-pyme][Python 3 PyME]]
- The latest version of PyME for Python 2.6 and 2.7 (as of this
- writing) is v0.9.0. https://bitbucket.org/malb/pyme
+ writing) is v0.9.0. [[https://bitbucket.org/malb/pyme][PyME 0.9.0]]
- A previous version of PyME v0.8.0 can be found on sourceforge:
- http://pyme.sourceforge.net/
+ [[http://pyme.sourceforge.net/][PyME 0.8.0]]
- A previous version of PyME v0.5.1 which works with GPGME v0.3.15
can be found on John Goerzen's PyME page:
- http://quux.org/devel/pyme/
- http://www.complete.org/JohnGoerzen
+ [[http://quux.org/devel/pyme/][PyME 0.3.15]]
+ [[http://www.complete.org/JohnGoerzen][John Goerzen]]
diff --git a/lang/python/docs/GPGMEpythonHOWTOen.org b/lang/python/docs/GPGMEpythonHOWTOen.org
index ef66effc..a712ec27 100644
--- a/lang/python/docs/GPGMEpythonHOWTOen.org
+++ b/lang/python/docs/GPGMEpythonHOWTOen.org
@@ -1,4 +1,5 @@
#+TITLE: GNU Privacy Guard (GnuPG) Made Easy Python Bindings HOWTO (English)
+#+AUTHOR: Ben McGinnes
#+LATEX_COMPILER: xelatex
#+LATEX_CLASS: article
#+LATEX_CLASS_OPTIONS: [12pt]
@@ -14,14 +15,14 @@
:CUSTOM_ID: intro
:END:
- | Version: | 0.1.1 |
- | Author: | Ben McGinnes <[email protected]> |
- | Author GPG Key: | DB4724E6FA4286C92B4E55C4321E4E2373590E5D |
- | Language: | Australian English, British English |
- | xml:lang: | en-AU, en-GB, en |
+| Version: | 0.1.3 |
+| Author: | Ben McGinnes <[email protected]> |
+| Author GPG Key: | DB4724E6FA4286C92B4E55C4321E4E2373590E5D |
+| Language: | Australian English, British English |
+| xml:lang: | en-AU, en-GB, en |
- This document provides basic instruction in how to use the GPGME
- Python bindings to programmatically leverage the GPGME library.
+This document provides basic instruction in how to use the GPGME
+Python bindings to programmatically leverage the GPGME library.
** Python 2 versus Python 3
@@ -29,25 +30,25 @@
:CUSTOM_ID: py2-vs-py3
:END:
- Though the GPGME Python bindings themselves provide support for
- both Python 2 and 3, the focus is unequivocally on Python 3 and
- specifically from Python 3.4 and above. As a consequence all the
- examples and instructions in this guide use Python 3 code.
+Though the GPGME Python bindings themselves provide support for both
+Python 2 and 3, the focus is unequivocally on Python 3 and
+specifically from Python 3.4 and above. As a consequence all the
+examples and instructions in this guide use Python 3 code.
- Much of it will work with Python 2, but much of it also deals with
- Python 3 byte literals, particularly when reading and writing data.
- Developers concentrating on Python 2.7, and possibly even 2.6, will
- need to make the appropriate modifications to support the older
- string and unicode types as opposed to bytes.
+Much of it will work with Python 2, but much of it also deals with
+Python 3 byte literals, particularly when reading and writing data.
+Developers concentrating on Python 2.7, and possibly even 2.6, will
+need to make the appropriate modifications to support the older string
+and unicode types as opposed to bytes.
- There are multiple reasons for concentrating on Python 3; some of
- which relate to the immediate integration of these bindings, some
- of which relate to longer term plans for both GPGME and the python
- bindings and some of which relate to the impending EOL period for
- Python 2.7. Essentially, though, there is little value in tying
- the bindings to a version of the language which is a dead end and
- the advantages offered by Python 3 over Python 2 make handling the
- data types with which GPGME deals considerably easier.
+There are multiple reasons for concentrating on Python 3; some of
+which relate to the immediate integration of these bindings, some of
+which relate to longer term plans for both GPGME and the python
+bindings and some of which relate to the impending EOL period for
+Python 2.7. Essentially, though, there is little value in tying the
+bindings to a version of the language which is a dead end and the
+advantages offered by Python 3 over Python 2 make handling the data
+types with which GPGME deals considerably easier.
** Examples
@@ -55,8 +56,8 @@
:CUSTOM_ID: howto-python3-examples
:END:
- All of the examples found in this document can be found as Python 3
- scripts in the =lang/python/examples/howto= directory.
+All of the examples found in this document can be found as Python 3
+scripts in the =lang/python/examples/howto= directory.
* GPGME Concepts
@@ -70,19 +71,17 @@
:CUSTOM_ID: gpgme-c-api
:END:
- Unlike many modern APIs with which programmers will be more
- familiar with these days, the GPGME API is a C API. The API is
- intended for use by C coders who would be able to access its
- features by including the =gpgme.h= header file with their own C
- source code and then access its functions just as they would any
- other C headers.
+Unlike many modern APIs with which programmers will be more familiar
+with these days, the GPGME API is a C API. The API is intended for
+use by C coders who would be able to access its features by including
+the =gpgme.h= header file with their own C source code and then access
+its functions just as they would any other C headers.
- This is a very effective method of gaining complete access to the
- API and in the most efficient manner possible. It does, however,
- have the drawback that it cannot be directly used by other
- languages without some means of providing an interface to those
- languages. This is where the need for bindings in various
- languages stems.
+This is a very effective method of gaining complete access to the API
+and in the most efficient manner possible. It does, however, have the
+drawback that it cannot be directly used by other languages without
+some means of providing an interface to those languages. This is
+where the need for bindings in various languages stems.
** Python bindings
@@ -90,16 +89,16 @@
:CUSTOM_ID: gpgme-python-bindings
:END:
- The Python bindings for GPGME provide a higher level means of
- accessing the complete feature set of GPGME itself. It also
- provides a more pythonic means of calling these API functions.
+The Python bindings for GPGME provide a higher level means of
+accessing the complete feature set of GPGME itself. It also provides
+a more pythonic means of calling these API functions.
- The bindings are generated dynamically with SWIG and the copy of
- =gpgme.h= generated when GPGME is compiled.
+The bindings are generated dynamically with SWIG and the copy of
+=gpgme.h= generated when GPGME is compiled.
- This means that a version of the Python bindings is fundamentally
- tied to the exact same version of GPGME used to generate that copy
- of =gpgme.h=.
+This means that a version of the Python bindings is fundamentally tied
+to the exact same version of GPGME used to generate that copy of
+=gpgme.h=.
** Difference between the Python bindings and other GnuPG Python packages
@@ -107,9 +106,9 @@
:CUSTOM_ID: gpgme-python-bindings-diffs
:END:
- There have been numerous attempts to add GnuPG support to Python
- over the years. Some of the most well known are listed here, along
- with what differentiates them.
+There have been numerous attempts to add GnuPG support to Python over
+the years. Some of the most well known are listed here, along with
+what differentiates them.
*** The python-gnupg package maintained by Vinay Sajip
@@ -117,23 +116,23 @@
:CUSTOM_ID: diffs-python-gnupg
:END:
- This is arguably the most popular means of integrating GPG with
- Python. The package utilises the =subprocess= module to implement
- wrappers for the =gpg= and =gpg2= executables normally invoked on
- the command line (=gpg.exe= and =gpg2.exe= on Windows).
+This is arguably the most popular means of integrating GPG with
+Python. The package utilises the =subprocess= module to implement
+wrappers for the =gpg= and =gpg2= executables normally invoked on the
+command line (=gpg.exe= and =gpg2.exe= on Windows).
- The popularity of this package stemmed from its ease of use and
- capability in providing the most commonly required features.
+The popularity of this package stemmed from its ease of use and
+capability in providing the most commonly required features.
- Unfortunately it has been beset by a number of security issues in
- the past; most of which stemmed from using unsafe methods of
- accessing the command line via the =subprocess= calls. While some
- effort has been made over the last two to three years (as of 2018)
- to mitigate this, particularly by no longer providing shell access
- through those subprocess calls, the wrapper is still somewhat
- limited in the scope of its GnuPG features coverage.
+Unfortunately it has been beset by a number of security issues in the
+past; most of which stemmed from using unsafe methods of accessing the
+command line via the =subprocess= calls. While some effort has been
+made over the last two to three years (as of 2018) to mitigate this,
+particularly by no longer providing shell access through those
+subprocess calls, the wrapper is still somewhat limited in the scope
+of its GnuPG features coverage.
- The python-gnupg package is available under the MIT license.
+The python-gnupg package is available under the MIT license.
*** The gnupg package created and maintained by Isis Lovecruft
@@ -141,20 +140,20 @@
:CUSTOM_ID: diffs-isis-gnupg
:END:
- In 2015 Isis Lovecruft from the Tor Project forked and then
- re-implemented the python-gnupg package as just gnupg. This new
- package also relied on subprocess to call the =gpg= or =gpg2=
- binaries, but did so somewhat more securely.
+In 2015 Isis Lovecruft from the Tor Project forked and then
+re-implemented the python-gnupg package as just gnupg. This new
+package also relied on subprocess to call the =gpg= or =gpg2=
+binaries, but did so somewhat more securely.
- The naming and version numbering selected for this package,
- however, resulted in conflicts with the original python-gnupg and
- since its functions were called in a different manner to
- python-gnupg, the release of this package also resulted in a great
- deal of consternation when people installed what they thought was
- an upgrade that subsequently broke the code relying on it.
+The naming and version numbering selected for this package, however,
+resulted in conflicts with the original python-gnupg and since its
+functions were called in a different manner to python-gnupg, the
+release of this package also resulted in a great deal of consternation
+when people installed what they thought was an upgrade that
+subsequently broke the code relying on it.
- The gnupg package is available under the GNU General Public
- License version 3.0 (or any later version).
+The gnupg package is available under the GNU General Public License
+version 3.0 (or any later version).
*** The PyME package maintained by Martin Albrecht
@@ -162,26 +161,26 @@
:CUSTOM_ID: diffs-pyme
:END:
- This package is the origin of these bindings, though they are
- somewhat different now. For details of when and how the PyME
- package was folded back into GPGME itself see the /Short History/
- document[fn:1] in the Python bindings =docs= directory.[fn:2]
+This package is the origin of these bindings, though they are somewhat
+different now. For details of when and how the PyME package was
+folded back into GPGME itself see the /Short History/ document[fn:1]
+in the Python bindings =docs= directory.[fn:2]
- The PyME package was first released in 2002 and was also the first
- attempt to implement a low level binding to GPGME. In doing so it
- provided access to considerably more functionality than either the
- =python-gnupg= or =gnupg= packages.
+The PyME package was first released in 2002 and was also the first
+attempt to implement a low level binding to GPGME. In doing so it
+provided access to considerably more functionality than either the
+=python-gnupg= or =gnupg= packages.
- The PyME package is only available for Python 2.6 and 2.7.
+The PyME package is only available for Python 2.6 and 2.7.
- Porting the PyME package to Python 3.4 in 2015 is what resulted in
- it being folded into the GPGME project and the current bindings
- are the end result of that effort.
+Porting the PyME package to Python 3.4 in 2015 is what resulted in it
+being folded into the GPGME project and the current bindings are the
+end result of that effort.
- The PyME package is available under the same dual licensing as
- GPGME itself: the GNU General Public License version 2.0 (or any
- later version) and the GNU Lesser General Public License version
- 2.1 (or any later version).
+The PyME package is available under the same dual licensing as GPGME
+itself: the GNU General Public License version 2.0 (or any later
+version) and the GNU Lesser General Public License version 2.1 (or any
+later version).
* GPGME Python bindings installation
@@ -195,19 +194,18 @@
:CUSTOM_ID: do-not-use-pypi
:END:
- Most third-party Python packages and modules are available and
- distributed through the Python Package Installer, known as PyPI.
+Most third-party Python packages and modules are available and
+distributed through the Python Package Installer, known as PyPI.
- Due to the nature of what these bindings are and how they work, it
- is infeasible to install the GPGME Python bindings in the same way.
+Due to the nature of what these bindings are and how they work, it is
+infeasible to install the GPGME Python bindings in the same way.
- This is because the bindings use SWIG to dynamically generate C
- bindings against =gpgme.h= and =gpgme.h= is generated from
- =gpgme.h.in= at compile time when GPGME is built from source. Thus
- to include a package in PyPI which actually built correctly would
- require either statically built libraries for every architecture
- bundled with it or a full implementation of C for each
- architecture.
+This is because the bindings use SWIG to dynamically generate C
+bindings against =gpgme.h= and =gpgme.h= is generated from
+=gpgme.h.in= at compile time when GPGME is built from source. Thus to
+include a package in PyPI which actually built correctly would require
+either statically built libraries for every architecture bundled with
+it or a full implementation of C for each architecture.
** Requirements
@@ -215,14 +213,13 @@
:CUSTOM_ID: gpgme-python-requirements
:END:
- The GPGME Python bindings only have three requirements:
+The GPGME Python bindings only have three requirements:
- 1. A suitable version of Python 2 or Python 3. With Python 2 that
- means Python 2.7 and with Python 3 that means Python 3.4 or
- higher.
- 2. SWIG.
- 3. GPGME itself. Which also means that all of GPGME's dependencies
- must be installed too.
+1. A suitable version of Python 2 or Python 3. With Python 2 that
+ means Python 2.7 and with Python 3 that means Python 3.4 or higher.
+2. SWIG.
+3. GPGME itself. Which also means that all of GPGME's dependencies
+ must be installed too.
** Installation
@@ -230,24 +227,23 @@
:CUSTOM_ID: installation
:END:
- Installing the Python bindings is effectively achieved by compiling
- and installing GPGME itself.
+Installing the Python bindings is effectively achieved by compiling
+and installing GPGME itself.
- Once SWIG is installed with Python and all the dependencies for
- GPGME are installed you only need to confirm that the version(s) of
- Python you want the bindings installed for are in your =$PATH=.
+Once SWIG is installed with Python and all the dependencies for GPGME
+are installed you only need to confirm that the version(s) of Python
+you want the bindings installed for are in your =$PATH=.
- By default GPGME will attempt to install the bindings for the most
- recent or highest version number of Python 2 and Python 3 it
- detects in =$PATH=. It specifically checks for the =python= and
- =python3= executables first and then checks for specific version
- numbers.
+By default GPGME will attempt to install the bindings for the most
+recent or highest version number of Python 2 and Python 3 it detects
+in =$PATH=. It specifically checks for the =python= and =python3=
+executables first and then checks for specific version numbers.
- For Python 2 it checks for these executables in this order:
- =python=, =python2= and =python2.7=.
+For Python 2 it checks for these executables in this order: =python=,
+=python2= and =python2.7=.
- For Python 3 it checks for these executables in this order:
- =python3=, =python3.6=, =python3.5= and =python3.4=.
+For Python 3 it checks for these executables in this order: =python3=,
+=python3.6=, =python3.5=, =python3.4= and =python3.7=.[fn:4]
*** Installing GPGME
@@ -255,8 +251,8 @@
:CUSTOM_ID: install-gpgme
:END:
- See the GPGME =README= file for details of how to install GPGME from
- source.
+See the GPGME =README= file for details of how to install GPGME from
+source.
* Fundamentals
@@ -264,9 +260,9 @@
:CUSTOM_ID: howto-fund-a-mental
:END:
- Before we can get to the fun stuff, there are a few matters
- regarding GPGME's design which hold true whether you're dealing with
- the C code directly or these Python bindings.
+Before we can get to the fun stuff, there are a few matters regarding
+GPGME's design which hold true whether you're dealing with the C code
+directly or these Python bindings.
** No REST
@@ -274,23 +270,23 @@
:CUSTOM_ID: no-rest-for-the-wicked
:END:
- The first part of which is or will be fairly blatantly obvious upon
- viewing the first example, but it's worth reiterating anyway. That
- being that this API is /*not*/ a REST API. Nor indeed could it
- ever be one.
+The first part of which is or will be fairly blatantly obvious upon
+viewing the first example, but it's worth reiterating anyway. That
+being that this API is /*not*/ a REST API. Nor indeed could it ever
+be one.
- Most, if not all, Python programmers (and not just Python
- programmers) know how easy it is to work with a RESTful API. In
- fact they've become so popular that many other APIs attempt to
- emulate REST-like behaviour as much as they are able. Right down
- to the use of JSON formatted output to facilitate the use of their
- API without having to retrain developers.
+Most, if not all, Python programmers (and not just Python programmers)
+know how easy it is to work with a RESTful API. In fact they've
+become so popular that many other APIs attempt to emulate REST-like
+behaviour as much as they are able. Right down to the use of JSON
+formatted output to facilitate the use of their API without having to
+retrain developers.
- This API does not do that. It would not be able to do that and
- also provide access to the entire C API on which it's built. It
- does, however, provide a very pythonic interface on top of the
- direct bindings and it's this pythonic layer with which this HOWTO
- deals with.
+This API does not do that. It would not be able to do that and also
+provide access to the entire C API on which it's built. It does,
+however, provide a very pythonic interface on top of the direct
+bindings and it's this pythonic layer with which this HOWTO deals
+with.
** Context
@@ -298,22 +294,22 @@
:CUSTOM_ID: howto-get-context
:END:
- One of the reasons which prevents this API from being RESTful is
- that most operations require more than one instruction to the API
- to perform the task. Sure, there are certain functions which can
- be performed simultaneously, particularly if the result known or
- strongly anticipated (e.g. selecting and encrypting to a key known
- to be in the public keybox).
+One of the reasons which prevents this API from being RESTful is that
+most operations require more than one instruction to the API to
+perform the task. Sure, there are certain functions which can be
+performed simultaneously, particularly if the result known or strongly
+anticipated (e.g. selecting and encrypting to a key known to be in the
+public keybox).
- There are many more, however, which cannot be manipulated so
- readily: they must be performed in a specific sequence and the
- result of one operation has a direct bearing on the outcome of
- subsequent operations. Not merely by generating an error either.
+There are many more, however, which cannot be manipulated so readily:
+they must be performed in a specific sequence and the result of one
+operation has a direct bearing on the outcome of subsequent
+operations. Not merely by generating an error either.
- When dealing with this type of persistent state on the web, full of
- both the RESTful and REST-like, it's most commonly referred to as a
- session. In GPGME, however, it is called a context and every
- operation type has one.
+When dealing with this type of persistent state on the web, full of
+both the RESTful and REST-like, it's most commonly referred to as a
+session. In GPGME, however, it is called a context and every
+operation type has one.
* Working with keys
@@ -327,58 +323,58 @@
:CUSTOM_ID: howto-keys-selection
:END:
- Selecting keys to encrypt to or to sign with will be a common
- occurrence when working with GPGMe and the means available for
- doing so are quite simple.
+Selecting keys to encrypt to or to sign with will be a common
+occurrence when working with GPGMe and the means available for doing
+so are quite simple.
- They do depend on utilising a Context; however once the data is
- recorded in another variable, that Context does not need to be the
- same one which subsequent operations are performed.
+They do depend on utilising a Context; however once the data is
+recorded in another variable, that Context does not need to be the
+same one which subsequent operations are performed.
- The easiest way to select a specific key is by searching for that
- key's key ID or fingerprint, preferably the full fingerprint
- without any spaces in it. A long key ID will probably be okay, but
- is not advised and short key IDs are already a problem with some
- being generated to match specific patterns. It does not matter
- whether the pattern is upper or lower case.
+The easiest way to select a specific key is by searching for that
+key's key ID or fingerprint, preferably the full fingerprint without
+any spaces in it. A long key ID will probably be okay, but is not
+advised and short key IDs are already a problem with some being
+generated to match specific patterns. It does not matter whether the
+pattern is upper or lower case.
- So this is the best method:
+So this is the best method:
- #+begin_src python
- import gpg
+#+BEGIN_SRC python -i
+import gpg
- k = gpg.Context().keylist(pattern="258E88DCBD3CD44D8E7AB43F6ECB6AF0DEADBEEF")
- keys = list(k)
- #+end_src
+k = gpg.Context().keylist(pattern="258E88DCBD3CD44D8E7AB43F6ECB6AF0DEADBEEF")
+keys = list(k)
+#+END_SRC
- This is passable and very likely to be common:
+This is passable and very likely to be common:
- #+begin_src python
- import gpg
+#+BEGIN_SRC python -i
+import gpg
- k = gpg.Context().keylist(pattern="0x6ECB6AF0DEADBEEF")
- keys = list(k)
- #+end_src
+k = gpg.Context().keylist(pattern="0x6ECB6AF0DEADBEEF")
+keys = list(k)
+#+END_SRC
- And this is a really bad idea:
+And this is a really bad idea:
- #+begin_src python
- import gpg
+#+BEGIN_SRC python -i
+import gpg
- k = gpg.Context().keylist(pattern="0xDEADBEEF")
- keys = list(k)
- #+end_src
+k = gpg.Context().keylist(pattern="0xDEADBEEF")
+keys = list(k)
+#+END_SRC
- Alternatively it may be that the intention is to create a list of
- keys which all match a particular search string. For instance all
- the addresses at a particular domain, like this:
+Alternatively it may be that the intention is to create a list of keys
+which all match a particular search string. For instance all the
+addresses at a particular domain, like this:
- #+begin_src python
- import gpg
+#+BEGIN_SRC python -i
+import gpg
- ncsc = gpg.Context().keylist(pattern="ncsc.mil")
- nsa = list(ncsc)
- #+end_src
+ncsc = gpg.Context().keylist(pattern="ncsc.mil")
+nsa = list(ncsc)
+#+END_SRC
*** Counting keys
@@ -386,29 +382,28 @@
:CUSTOM_ID: howto-keys-counting
:END:
- Counting the number of keys in your public keybox (=pubring.kbx=),
- the format which has superseded the old keyring format
- (=pubring.gpg= and =secring.gpg=), or the number of secret keys is
- a very simple task.
+Counting the number of keys in your public keybox (=pubring.kbx=), the
+format which has superseded the old keyring format (=pubring.gpg= and
+=secring.gpg=), or the number of secret keys is a very simple task.
- #+begin_src python
- import gpg
+#+BEGIN_SRC python -i
+import gpg
- c = gpg.Context()
- seckeys = c.keylist(pattern=None, secret=True)
- pubkeys = c.keylist(pattern=None, secret=False)
+c = gpg.Context()
+seckeys = c.keylist(pattern=None, secret=True)
+pubkeys = c.keylist(pattern=None, secret=False)
- seclist = list(seckeys)
- secnum = len(seclist)
+seclist = list(seckeys)
+secnum = len(seclist)
- publist = list(pubkeys)
- pubnum = len(publist)
+publist = list(pubkeys)
+pubnum = len(publist)
- print("""
- Number of secret keys: {0}
- Number of public keys: {1}
- """.format(secnum, pubnum))
- #+end_src
+print("""
+ Number of secret keys: {0}
+ Number of public keys: {1}
+""".format(secnum, pubnum))
+#+END_SRC
** Get key
@@ -416,42 +411,398 @@
:CUSTOM_ID: howto-get-key
:END:
- An alternative method of getting a single key via its fingerprint
- is available directly within a Context with =Context().get_key=.
- This is the preferred method of selecting a key in order to modify
- it, sign or certify it and for obtaining relevant data about a
- single key as a part of other functions; when verifying a signature
- made by that key, for instance.
+An alternative method of getting a single key via its fingerprint is
+available directly within a Context with =Context().get_key=. This is
+the preferred method of selecting a key in order to modify it, sign or
+certify it and for obtaining relevant data about a single key as a
+part of other functions; when verifying a signature made by that key,
+for instance.
- By default this method will select public keys, but it can select
- secret keys as well.
+By default this method will select public keys, but it can select
+secret keys as well.
- This first example demonstrates selecting the current key of Werner
- Koch, which is due to expire at the end of 2018:
+This first example demonstrates selecting the current key of Werner
+Koch, which is due to expire at the end of 2018:
- #+begin_src python
- import gpg
+#+BEGIN_SRC python -i
+import gpg
- fingerprint = "80615870F5BAD690333686D0F2AD85AC1E42B367"
- key = gpg.Context().get_key(fingerprint)
- #+end_src
+fingerprint = "80615870F5BAD690333686D0F2AD85AC1E42B367"
+key = gpg.Context().get_key(fingerprint)
+#+END_SRC
- Whereas this example demonstrates selecting the author's current
- key with the =secret= key word argument set to =True=:
+Whereas this example demonstrates selecting the author's current key
+with the =secret= key word argument set to =True=:
- #+begin_src python
- import gpg
+#+BEGIN_SRC python -i
+import gpg
- fingerprint = "DB4724E6FA4286C92B4E55C4321E4E2373590E5D"
- key = gpg.Context().get_key(fingerprint, secret=True)
- #+end_src
+fingerprint = "DB4724E6FA4286C92B4E55C4321E4E2373590E5D"
+key = gpg.Context().get_key(fingerprint, secret=True)
+#+END_SRC
- It is, of course, quite possible to select expired, disabled and
- revoked keys with this function, but only to effectively display
- information about those keys.
+It is, of course, quite possible to select expired, disabled and
+revoked keys with this function, but only to effectively display
+information about those keys.
- It is also possible to use both unicode or string literals and byte
- literals with the fingerprint when getting a key in this way.
+It is also possible to use both unicode or string literals and byte
+literals with the fingerprint when getting a key in this way.
+
+
+** Importing keys
+ :PROPERTIES:
+ :CUSTOM_ID: howto-import-key
+ :END:
+
+Importing keys is possible with the =key_import()= method and takes
+one argument which is a bytes literal object containing either the
+binary or ASCII armoured key data for one or more keys.
+
+The following example retrieves one or more keys from the SKS
+keyservers via the web using the requests module. Since requests
+returns the content as a bytes literal object, we can then use that
+directly to import the resulting data into our keybox.
+
+#+BEGIN_SRC python -i
+import gpg
+import os.path
+import requests
+
+c = gpg.Context()
+url = "https://sks-keyservers.net/pks/lookup"
+pattern = input("Enter the pattern to search for key or user IDs: ")
+payload = { "op": "get", "search": pattern }
+
+r = requests.get(url, verify=True, params=payload)
+result = c.key_import(r.content)
+
+if result is not None and hasattr(result, "considered") is False:
+ print(result)
+elif result is not None and hasattr(result, "considered") is True:
+ num_keys = len(result.imports)
+ new_revs = result.new_revocations
+ new_sigs = result.new_signatures
+ new_subs = result.new_sub_keys
+ new_uids = result.new_user_ids
+ new_scrt = result.secret_imported
+ nochange = result.unchanged
+ print("""
+ The total number of keys considered for import was: {0}
+
+ Number of keys revoked: {1}
+ Number of new signatures: {2}
+ Number of new subkeys: {3}
+ Number of new user IDs: {4}
+ Number of new secret keys: {5}
+ Number of unchanged keys: {6}
+
+ The key IDs for all considered keys were:
+""".format(num_keys, new_revs, new_sigs, new_subs, new_uids, new_scrt,
+ nochange))
+ for i in range(num_keys):
+ print("{0}\n".format(result.imports[i].fpr))
+else:
+ pass
+#+END_SRC
+
+*NOTE:* When searching for a key ID of any length or a fingerprint
+(without spaces), the SKS servers require the the leading =0x=
+indicative of hexadecimal be included. Also note that the old short
+key IDs (e.g. =0xDEADBEEF=) should no longer be used due to the
+relative ease by which such key IDs can be reproduced, as demonstrated
+by the Evil32 Project in 2014 (which was subsequently exploited in
+2016).
+
+
+** Exporting keys
+ :PROPERTIES:
+ :CUSTOM_ID: howto-export-key
+ :END:
+
+Exporting keys remains a reasonably simple task, but has been
+separated into three different functions for the OpenPGP cryptographic
+engine. Two of those functions are for exporting public keys and the
+third is for exporting secret keys.
+
+
+*** Exporting public keys
+ :PROPERTIES:
+ :CUSTOM_ID: howto-export-public-key
+ :END:
+
+There are two methods of exporting public keys, both of which are very
+similar to the other. The default method, =key_export()=, will export
+a public key or keys matching a specified pattern as normal. The
+alternative, the =key_export_minimal()= method, will do the same thing
+except producing a minimised output with extra signatures and third
+party signatures or certifications removed.
+
+#+BEGIN_SRC python -i
+import gpg
+import os.path
+import sys
+
+print("""
+This script exports one or more public keys.
+""")
+
+c = gpg.Context(armor=True)
+
+if len(sys.argv) >= 4:
+ keyfile = sys.argv[1]
+ logrus = sys.argv[2]
+ homedir = sys.argv[3]
+elif len(sys.argv) == 3:
+ keyfile = sys.argv[1]
+ logrus = sys.argv[2]
+ homedir = input("Enter the GPG configuration directory path (optional): ")
+elif len(sys.argv) == 2:
+ keyfile = sys.argv[1]
+ logrus = input("Enter the UID matching the key(s) to export: ")
+ homedir = input("Enter the GPG configuration directory path (optional): ")
+else:
+ keyfile = input("Enter the path and filename to save the secret key to: ")
+ logrus = input("Enter the UID matching the key(s) to export: ")
+ homedir = input("Enter the GPG configuration directory path (optional): ")
+
+if homedir.startswith("~"):
+ if os.path.exists(os.path.expanduser(homedir)) is True:
+ c.home_dir = os.path.expanduser(homedir)
+ else:
+ pass
+elif os.path.exists(homedir) is True:
+ c.home_dir = homedir
+else:
+ pass
+
+try:
+ result = c.key_export(pattern=logrus)
+except:
+ result = c.key_export(pattern=None)
+
+if result is not None:
+ with open(keyfile, "wb") as f:
+ f.write(result)
+else:
+ pass
+#+END_SRC
+
+It is important to note that the result will only return =None= when a
+pattern has been entered for =logrus=, but it has not matched any
+keys. When the search pattern itself is set to =None= this triggers
+the exporting of the entire public keybox.
+
+#+BEGIN_SRC python -i
+import gpg
+import os.path
+import sys
+
+print("""
+This script exports one or more public keys in minimised form.
+""")
+
+c = gpg.Context(armor=True)
+
+if len(sys.argv) >= 4:
+ keyfile = sys.argv[1]
+ logrus = sys.argv[2]
+ homedir = sys.argv[3]
+elif len(sys.argv) == 3:
+ keyfile = sys.argv[1]
+ logrus = sys.argv[2]
+ homedir = input("Enter the GPG configuration directory path (optional): ")
+elif len(sys.argv) == 2:
+ keyfile = sys.argv[1]
+ logrus = input("Enter the UID matching the key(s) to export: ")
+ homedir = input("Enter the GPG configuration directory path (optional): ")
+else:
+ keyfile = input("Enter the path and filename to save the secret key to: ")
+ logrus = input("Enter the UID matching the key(s) to export: ")
+ homedir = input("Enter the GPG configuration directory path (optional): ")
+
+if homedir.startswith("~"):
+ if os.path.exists(os.path.expanduser(homedir)) is True:
+ c.home_dir = os.path.expanduser(homedir)
+ else:
+ pass
+elif os.path.exists(homedir) is True:
+ c.home_dir = homedir
+else:
+ pass
+
+try:
+ result = c.key_export_minimal(pattern=logrus)
+except:
+ result = c.key_export_minimal(pattern=None)
+
+if result is not None:
+ with open(keyfile, "wb") as f:
+ f.write(result)
+else:
+ pass
+#+END_SRC
+
+
+*** Exporting secret keys
+ :PROPERTIES:
+ :CUSTOM_ID: howto-export-secret-key
+ :END:
+
+Exporting secret keys is, functionally, very similar to exporting
+public keys; save for the invocation of =pinentry= via =gpg-agent= in
+order to securely enter the key's passphrase and authorise the export.
+
+The following example exports the secret key to a file which is then
+set with the same permissions as the output files created by the
+command line secret key export options.
+
+#+BEGIN_SRC python -i
+import gpg
+import os
+import os.path
+import sys
+
+print("""
+This script exports one or more secret keys.
+
+The gpg-agent and pinentry are invoked to authorise the export.
+""")
+
+c = gpg.Context(armor=True)
+
+if len(sys.argv) >= 4:
+ keyfile = sys.argv[1]
+ logrus = sys.argv[2]
+ homedir = sys.argv[3]
+elif len(sys.argv) == 3:
+ keyfile = sys.argv[1]
+ logrus = sys.argv[2]
+ homedir = input("Enter the GPG configuration directory path (optional): ")
+elif len(sys.argv) == 2:
+ keyfile = sys.argv[1]
+ logrus = input("Enter the UID matching the secret key(s) to export: ")
+ homedir = input("Enter the GPG configuration directory path (optional): ")
+else:
+ keyfile = input("Enter the path and filename to save the secret key to: ")
+ logrus = input("Enter the UID matching the secret key(s) to export: ")
+ homedir = input("Enter the GPG configuration directory path (optional): ")
+
+if homedir.startswith("~"):
+ if os.path.exists(os.path.expanduser(homedir)) is True:
+ c.home_dir = os.path.expanduser(homedir)
+ else:
+ pass
+elif os.path.exists(homedir) is True:
+ c.home_dir = homedir
+else:
+ pass
+
+try:
+ result = c.key_export_secret(pattern=logrus)
+except:
+ result = c.key_export_secret(pattern=None)
+
+if result is not None:
+ with open(keyfile, "wb") as f:
+ f.write(result)
+ os.chmod(keyfile, 0o600)
+else:
+ pass
+#+END_SRC
+
+Alternatively the approach of the following script can be used. This
+longer example saves the exported secret key(s) in files in the GnuPG
+home directory, in addition to setting the file permissions as only
+readable and writable by the user. It also exports the secret key(s)
+twice in order to output both GPG binary (=.gpg=) and ASCII armoured
+(=.asc=) files.
+
+#+BEGIN_SRC python -i
+import gpg
+import os
+import os.path
+import subprocess
+import sys
+
+print("""
+This script exports one or more secret keys as both ASCII armored and binary
+file formats, saved in files within the user's GPG home directory.
+
+The gpg-agent and pinentry are invoked to authorise the export.
+""")
+
+if sys.platform == "win32":
+ gpgconfcmd = "gpgconf.exe --list-dirs homedir"
+else:
+ gpgconfcmd = "gpgconf --list-dirs homedir"
+
+a = gpg.Context(armor=True)
+b = gpg.Context()
+c = gpg.Context()
+
+if len(sys.argv) >= 4:
+ keyfile = sys.argv[1]
+ logrus = sys.argv[2]
+ homedir = sys.argv[3]
+elif len(sys.argv) == 3:
+ keyfile = sys.argv[1]
+ logrus = sys.argv[2]
+ homedir = input("Enter the GPG configuration directory path (optional): ")
+elif len(sys.argv) == 2:
+ keyfile = sys.argv[1]
+ logrus = input("Enter the UID matching the secret key(s) to export: ")
+ homedir = input("Enter the GPG configuration directory path (optional): ")
+else:
+ keyfile = input("Enter the filename to save the secret key to: ")
+ logrus = input("Enter the UID matching the secret key(s) to export: ")
+ homedir = input("Enter the GPG configuration directory path (optional): ")
+
+if homedir.startswith("~"):
+ if os.path.exists(os.path.expanduser(homedir)) is True:
+ c.home_dir = os.path.expanduser(homedir)
+ else:
+ pass
+elif os.path.exists(homedir) is True:
+ c.home_dir = homedir
+else:
+ pass
+
+if c.home_dir is not None:
+ if c.home_dir.endswith("/"):
+ gpgfile = "{0}{1}.gpg".format(c.home_dir, keyfile)
+ ascfile = "{0}{1}.asc".format(c.home_dir, keyfile)
+ else:
+ gpgfile = "{0}/{1}.gpg".format(c.home_dir, keyfile)
+ ascfile = "{0}/{1}.asc".format(c.home_dir, keyfile)
+else:
+ if os.path.exists(os.environ["GNUPGHOME"]) is True:
+ hd = os.environ["GNUPGHOME"]
+ else:
+ hd = subprocess.getoutput(gpgconfcmd)
+ gpgfile = "{0}/{1}.gpg".format(hd, keyfile)
+ ascfile = "{0}/{1}.asc".format(hd, keyfile)
+
+try:
+ a_result = a.key_export_secret(pattern=logrus)
+ b_result = b.key_export_secret(pattern=logrus)
+except:
+ a_result = a.key_export_secret(pattern=None)
+ b_result = b.key_export_secret(pattern=None)
+
+if a_result is not None:
+ with open(ascfile, "wb") as f:
+ f.write(a_result)
+ os.chmod(ascfile, 0o600)
+else:
+ pass
+
+if b_result is not None:
+ with open(gpgfile, "wb") as f:
+ f.write(b_result)
+ os.chmod(gpgfile, 0o600)
+else:
+ pass
+#+END_SRC
* Basic Functions
@@ -459,10 +810,10 @@
:CUSTOM_ID: howto-the-basics
:END:
- The most frequently called features of any cryptographic library
- will be the most fundamental tasks for encryption software. In this
- section we will look at how to programmatically encrypt data,
- decrypt it, sign it and verify signatures.
+The most frequently called features of any cryptographic library will
+be the most fundamental tasks for encryption software. In this
+section we will look at how to programmatically encrypt data, decrypt
+it, sign it and verify signatures.
** Encryption
@@ -470,10 +821,9 @@
:CUSTOM_ID: howto-basic-encryption
:END:
- Encrypting is very straight forward. In the first example below
- the message, =text=, is encrypted to a single recipient's key. In
- the second example the message will be encrypted to multiple
- recipients.
+Encrypting is very straight forward. In the first example below the
+message, =text=, is encrypted to a single recipient's key. In the
+second example the message will be encrypted to multiple recipients.
*** Encrypting to one key
@@ -481,73 +831,70 @@
:CUSTOM_ID: howto-basic-encryption-single
:END:
- Once the the Context is set the main issues with encrypting data
- is essentially reduced to key selection and the keyword arguments
- specified in the =gpg.Context().encrypt()= method.
-
- Those keyword arguments are: =recipients=, a list of keys
- encrypted to (covered in greater detail in the following section);
- =sign=, whether or not to sign the plaintext data, see subsequent
- sections on signing and verifying signatures below (defaults to
- =True=); =sink=, to write results or partial results to a secure
- sink instead of returning it (defaults to =None=); =passphrase=,
- only used when utilising symmetric encryption (defaults to
- =None=); =always_trust=, used to override the trust model settings
- for recipient keys (defaults to =False=); =add_encrypt_to=,
- utilises any preconfigured =encrypt-to= or =default-key= settings
- in the user's =gpg.conf= file (defaults to =False=); =prepare=,
- prepare for encryption (defaults to =False=); =expect_sign=,
- prepare for signing (defaults to =False=); =compress=, compresses
- the plaintext prior to encryption (defaults to =True=).
-
- #+begin_src python
- import gpg
-
- a_key = "0x12345678DEADBEEF"
- text = b"""Some text to test with.
-
- Since the text in this case must be bytes, it is most likely that
- the input form will be a separate file which is opened with "rb"
- as this is the simplest method of obtaining the correct data
- format.
- """
-
- c = gpg.Context(armor=True)
- rkey = list(c.keylist(pattern=a_key, secret=False))
- ciphertext, result, sign_result = c.encrypt(text, recipients=rkey, sign=False)
-
- with open("secret_plans.txt.asc", "wb") as afile:
- afile.write(ciphertext)
- #+end_src
-
- Though this is even more likely to be used like this; with the
- plaintext input read from a file, the recipient keys used for
- encryption regardless of key trust status and the encrypted output
- also encrypted to any preconfigured keys set in the =gpg.conf=
- file:
-
- #+begin_src python
- import gpg
-
- a_key = "0x12345678DEADBEEF"
-
- with open("secret_plans.txt", "rb") as afile:
- text = afile.read()
-
- c = gpg.Context(armor=True)
- rkey = list(c.keylist(pattern=a_key, secret=False))
- ciphertext, result, sign_result = c.encrypt(text, recipients=rkey,
- sign=True, always_trust=True, add_encrypt_to=True)
-
- with open("secret_plans.txt.asc", "wb") as afile:
- afile.write(ciphertext)
- #+end_src
-
- If the =recipients= paramater is empty then the plaintext is
- encrypted symmetrically. If no =passphrase= is supplied as a
- parameter or via a callback registered with the =Context()= then
- an out-of-band prompt for the passphrase via pinentry will be
- invoked.
+Once the the Context is set the main issues with encrypting data is
+essentially reduced to key selection and the keyword arguments
+specified in the =gpg.Context().encrypt()= method.
+
+Those keyword arguments are: =recipients=, a list of keys encrypted to
+(covered in greater detail in the following section); =sign=, whether
+or not to sign the plaintext data, see subsequent sections on signing
+and verifying signatures below (defaults to =True=); =sink=, to write
+results or partial results to a secure sink instead of returning it
+(defaults to =None=); =passphrase=, only used when utilising symmetric
+encryption (defaults to =None=); =always_trust=, used to override the
+trust model settings for recipient keys (defaults to =False=);
+=add_encrypt_to=, utilises any preconfigured =encrypt-to= or
+=default-key= settings in the user's =gpg.conf= file (defaults to
+=False=); =prepare=, prepare for encryption (defaults to =False=);
+=expect_sign=, prepare for signing (defaults to =False=); =compress=,
+compresses the plaintext prior to encryption (defaults to =True=).
+
+#+BEGIN_SRC python -i
+import gpg
+
+a_key = "0x12345678DEADBEEF"
+text = b"""Some text to test with.
+
+Since the text in this case must be bytes, it is most likely that
+the input form will be a separate file which is opened with "rb"
+as this is the simplest method of obtaining the correct data format.
+"""
+
+c = gpg.Context(armor=True)
+rkey = list(c.keylist(pattern=a_key, secret=False))
+ciphertext, result, sign_result = c.encrypt(text, recipients=rkey, sign=False)
+
+with open("secret_plans.txt.asc", "wb") as afile:
+ afile.write(ciphertext)
+#+END_SRC
+
+Though this is even more likely to be used like this; with the
+plaintext input read from a file, the recipient keys used for
+encryption regardless of key trust status and the encrypted output
+also encrypted to any preconfigured keys set in the =gpg.conf= file:
+
+#+BEGIN_SRC python -i
+import gpg
+
+a_key = "0x12345678DEADBEEF"
+
+with open("secret_plans.txt", "rb") as afile:
+ text = afile.read()
+
+c = gpg.Context(armor=True)
+rkey = list(c.keylist(pattern=a_key, secret=False))
+ciphertext, result, sign_result = c.encrypt(text, recipients=rkey, sign=True,
+ always_trust=True,
+ add_encrypt_to=True)
+
+with open("secret_plans.txt.asc", "wb") as afile:
+ afile.write(ciphertext)
+#+END_SRC
+
+If the =recipients= paramater is empty then the plaintext is encrypted
+symmetrically. If no =passphrase= is supplied as a parameter or via a
+callback registered with the =Context()= then an out-of-band prompt
+for the passphrase via pinentry will be invoked.
*** Encrypting to multiple keys
@@ -555,101 +902,101 @@
:CUSTOM_ID: howto-basic-encryption-multiple
:END:
- Encrypting to multiple keys essentially just expands upon the key
- selection process and the recipients from the previous examples.
-
- The following example encrypts a message (=text=) to everyone with
- an email address on the =gnupg.org= domain,[fn:3] but does /not/ encrypt
- to a default key or other key which is configured to normally
- encrypt to.
-
- #+begin_src python
- import gpg
-
- text = b"""Oh look, another test message.
-
- The same rules apply as with the previous example and more likely
- than not, the message will actually be drawn from reading the
- contents of a file or, maybe, from entering data at an input()
- prompt.
-
- Since the text in this case must be bytes, it is most likely that
- the input form will be a separate file which is opened with "rb"
- as this is the simplest method of obtaining the correct data
- format.
- """
-
- c = gpg.Context(armor=True)
- rpattern = list(c.keylist(pattern="@gnupg.org", secret=False))
- logrus = []
-
- for i in range(len(rpattern)):
- if rpattern[i].can_encrypt == 1:
- logrus.append(rpattern[i])
-
- ciphertext, result, sign_result = c.encrypt(text, recipients=logrus, sign=False,
- always_trust=True)
-
- with open("secret_plans.txt.asc", "wb") as afile:
- afile.write(ciphertext)
- #+end_src
-
- All it would take to change the above example to sign the message
- and also encrypt the message to any configured default keys would
- be to change the =c.encrypt= line to this:
-
- #+begin_src python
- ciphertext, result, sign_result = c.encrypt(text, recipients=logrus,
- always_trust=True,
- add_encrypt_to=True)
- #+end_src
-
- The only keyword arguments requiring modification are those for
- which the default values are changing. The default value of
- =sign= is =True=, the default of =always_trust= is =False=, the
- default of =add_encrypt_to= is =False=.
-
- If =always_trust= is not set to =True= and any of the recipient
- keys are not trusted (e.g. not signed or locally signed) then the
- encryption will raise an error. It is possible to mitigate this
- somewhat with something more like this:
-
- #+begin_src python
- import gpg
-
- with open("secret_plans.txt.asc", "rb") as afile:
- text = afile.read()
-
- c = gpg.Context(armor=True)
- rpattern = list(c.keylist(pattern="@gnupg.org", secret=False))
- logrus = []
-
- for i in range(len(rpattern)):
- if rpattern[i].can_encrypt == 1:
- logrus.append(rpattern[i])
-
- try:
- ciphertext, result, sign_result = c.encrypt(text, recipients=logrus,
- add_encrypt_to=True)
- except gpg.errors.InvalidRecipients as e:
- for i in range(len(e.recipients)):
- for n in range(len(logrus)):
- if logrus[n].fpr == e.recipients[i].fpr:
- logrus.remove(logrus[n])
- else:
- pass
- try:
- ciphertext, result, sign_result = c.encrypt(text, recipients=logrus,
- add_encrypt_to=True)
- except:
- pass
-
- with open("secret_plans.txt.asc", "wb") as afile:
- afile.write(ciphertext)
- #+end_src
-
- This will attempt to encrypt to all the keys searched for, then
- remove invalid recipients if it fails and try again.
+Encrypting to multiple keys essentially just expands upon the key
+selection process and the recipients from the previous examples.
+
+The following example encrypts a message (=text=) to everyone with an
+email address on the =gnupg.org= domain,[fn:3] but does /not/ encrypt
+to a default key or other key which is configured to normally encrypt
+to.
+
+#+BEGIN_SRC python -i
+import gpg
+
+text = b"""Oh look, another test message.
+
+The same rules apply as with the previous example and more likely
+than not, the message will actually be drawn from reading the
+contents of a file or, maybe, from entering data at an input()
+prompt.
+
+Since the text in this case must be bytes, it is most likely that
+the input form will be a separate file which is opened with "rb"
+as this is the simplest method of obtaining the correct data
+format.
+"""
+
+c = gpg.Context(armor=True)
+rpattern = list(c.keylist(pattern="@gnupg.org", secret=False))
+logrus = []
+
+for i in range(len(rpattern)):
+ if rpattern[i].can_encrypt == 1:
+ logrus.append(rpattern[i])
+
+ciphertext, result, sign_result = c.encrypt(text, recipients=logrus,
+ sign=False, always_trust=True)
+
+with open("secret_plans.txt.asc", "wb") as afile:
+ afile.write(ciphertext)
+#+END_SRC
+
+All it would take to change the above example to sign the message
+and also encrypt the message to any configured default keys would
+be to change the =c.encrypt= line to this:
+
+#+BEGIN_SRC python -i
+ciphertext, result, sign_result = c.encrypt(text, recipients=logrus,
+ always_trust=True,
+ add_encrypt_to=True)
+#+END_SRC
+
+The only keyword arguments requiring modification are those for which
+the default values are changing. The default value of =sign= is
+=True=, the default of =always_trust= is =False=, the default of
+=add_encrypt_to= is =False=.
+
+If =always_trust= is not set to =True= and any of the recipient keys
+are not trusted (e.g. not signed or locally signed) then the
+encryption will raise an error. It is possible to mitigate this
+somewhat with something more like this:
+
+#+BEGIN_SRC python -i
+import gpg
+
+with open("secret_plans.txt.asc", "rb") as afile:
+ text = afile.read()
+
+c = gpg.Context(armor=True)
+rpattern = list(c.keylist(pattern="@gnupg.org", secret=False))
+logrus = []
+
+for i in range(len(rpattern)):
+ if rpattern[i].can_encrypt == 1:
+ logrus.append(rpattern[i])
+
+ try:
+ ciphertext, result, sign_result = c.encrypt(text, recipients=logrus,
+ add_encrypt_to=True)
+ except gpg.errors.InvalidRecipients as e:
+ for i in range(len(e.recipients)):
+ for n in range(len(logrus)):
+ if logrus[n].fpr == e.recipients[i].fpr:
+ logrus.remove(logrus[n])
+ else:
+ pass
+ try:
+ ciphertext, result, sign_result = c.encrypt(text,
+ recipients=logrus,
+ add_encrypt_to=True)
+ with open("secret_plans.txt.asc", "wb") as afile:
+ afile.write(ciphertext)
+ except:
+ pass
+#+END_SRC
+
+This will attempt to encrypt to all the keys searched for, then remove
+invalid recipients if it fails and try again.
** Decryption
@@ -657,39 +1004,39 @@
:CUSTOM_ID: howto-basic-decryption
:END:
- Decrypting something encrypted to a key in one's secret keyring is
- fairly straight forward.
+Decrypting something encrypted to a key in one's secret keyring is
+fairly straight forward.
- In this example code, however, preconfiguring either
- =gpg.Context()= or =gpg.core.Context()= as =c= is unnecessary
- because there is no need to modify the Context prior to conducting
- the decryption and since the Context is only used once, setting it
- to =c= simply adds lines for no gain.
+In this example code, however, preconfiguring either =gpg.Context()=
+or =gpg.core.Context()= as =c= is unnecessary because there is no need
+to modify the Context prior to conducting the decryption and since the
+Context is only used once, setting it to =c= simply adds lines for no
+gain.
- #+begin_src python
- import gpg
+#+BEGIN_SRC python -i
+import gpg
- ciphertext = input("Enter path and filename of encrypted file: ")
- newfile = input("Enter path and filename of file to save decrypted data to: ")
+ciphertext = input("Enter path and filename of encrypted file: ")
+newfile = input("Enter path and filename of file to save decrypted data to: ")
- with open(ciphertext, "rb") as cfile:
- try:
- plaintext, result, verify_result = gpg.Context().decrypt(cfile)
- except gpg.errors.GPGMEError as e:
- plaintext = None
- print(e)
+with open(ciphertext, "rb") as cfile:
+ try:
+ plaintext, result, verify_result = gpg.Context().decrypt(cfile)
+ except gpg.errors.GPGMEError as e:
+ plaintext = None
+ print(e)
- if plaintext is not None:
- with open(newfile, "wb") as nfile:
- nfile.write(plaintext)
- else:
- pass
- #+end_src
+if plaintext is not None:
+ with open(newfile, "wb") as nfile:
+ nfile.write(plaintext)
+ else:
+ pass
+#+END_SRC
- The data available in =plaintext= in this example is the decrypted
- content as a byte object, the recipient key IDs and algorithms in
- =result= and the results of verifying any signatures of the data in
- =verify_result=.
+The data available in =plaintext= in this example is the decrypted
+content as a byte object, the recipient key IDs and algorithms in
+=result= and the results of verifying any signatures of the data in
+=verify_result=.
** Signing text and files
@@ -697,7 +1044,7 @@
:CUSTOM_ID: howto-basic-signing
:END:
- The following sections demonstrate how to specify keys to sign with.
+The following sections demonstrate how to specify keys to sign with.
*** Signing key selection
@@ -705,30 +1052,30 @@
:CUSTOM_ID: howto-basic-signing-signers
:END:
- By default GPGME and the Python bindings will use the default key
- configured for the user invoking the GPGME API. If there is no
- default key specified and there is more than one secret key
- available it may be necessary to specify the key or keys with
- which to sign messages and files.
+By default GPGME and the Python bindings will use the default key
+configured for the user invoking the GPGME API. If there is no
+default key specified and there is more than one secret key available
+it may be necessary to specify the key or keys with which to sign
+messages and files.
- #+begin_src python
- import gpg
+#+BEGIN_SRC python -i
+import gpg
- logrus = input("Enter the email address or string to match signing keys to: ")
- hancock = gpg.Context().keylist(pattern=logrus, secret=True)
- sig_src = list(hancock)
- #+end_src
+logrus = input("Enter the email address or string to match signing keys to: ")
+hancock = gpg.Context().keylist(pattern=logrus, secret=True)
+sig_src = list(hancock)
+#+END_SRC
- The signing examples in the following sections include the
- explicitly designated =signers= parameter in two of the five
- examples; once where the resulting signature would be ASCII
- armoured and once where it would not be armoured.
+The signing examples in the following sections include the explicitly
+designated =signers= parameter in two of the five examples; once where
+the resulting signature would be ASCII armoured and once where it
+would not be armoured.
- While it would be possible to enter a key ID or fingerprint here
- to match a specific key, it is not possible to enter two
- fingerprints and match two keys since the patten expects a string,
- bytes or None and not a list. A string with two fingerprints
- won't match any single key.
+While it would be possible to enter a key ID or fingerprint here to
+match a specific key, it is not possible to enter two fingerprints and
+match two keys since the patten expects a string, bytes or None and
+not a list. A string with two fingerprints won't match any single
+key.
*** Normal or default signing messages or files
@@ -736,54 +1083,54 @@
:CUSTOM_ID: howto-basic-signing-normal
:END:
- The normal or default signing process is essentially the same as
- is most often invoked when also encrypting a message or file. So
- when the encryption component is not utilised, the result is to
- produce an encoded and signed output which may or may not be ASCII
- armoured and which may or may not also be compressed.
+The normal or default signing process is essentially the same as is
+most often invoked when also encrypting a message or file. So when
+the encryption component is not utilised, the result is to produce an
+encoded and signed output which may or may not be ASCII armoured and
+which may or may not also be compressed.
- By default compression will be used unless GnuPG detects that the
- plaintext is already compressed. ASCII armouring will be
- determined according to the value of =gpg.Context().armor=.
+By default compression will be used unless GnuPG detects that the
+plaintext is already compressed. ASCII armouring will be determined
+according to the value of =gpg.Context().armor=.
- The compression algorithm is selected in much the same way as the
- symmetric encryption algorithm or the hash digest algorithm is
- when multiple keys are involved; from the preferences saved into
- the key itself or by comparison with the preferences with all
- other keys involved.
+The compression algorithm is selected in much the same way as the
+symmetric encryption algorithm or the hash digest algorithm is when
+multiple keys are involved; from the preferences saved into the key
+itself or by comparison with the preferences with all other keys
+involved.
- #+begin_src python
- import gpg
+#+BEGIN_SRC python -i
+import gpg
- text0 = """Declaration of ... something.
+text0 = """Declaration of ... something.
- """
- text = text0.encode()
+"""
+text = text0.encode()
- c = gpg.Context(armor=True, signers=sig_src)
- signed_data, result = c.sign(text, mode=gpg.constants.sig.mode.NORMAL)
+c = gpg.Context(armor=True, signers=sig_src)
+signed_data, result = c.sign(text, mode=gpg.constants.sig.mode.NORMAL)
- with open("/path/to/statement.txt.asc", "w") as afile:
- afile.write(signed_data.decode())
- #+end_src
+with open("/path/to/statement.txt.asc", "w") as afile:
+ afile.write(signed_data.decode())
+#+END_SRC
- Though everything in this example is accurate, it is more likely
- that reading the input data from another file and writing the
- result to a new file will be performed more like the way it is done
- in the next example. Even if the output format is ASCII armoured.
+Though everything in this example is accurate, it is more likely that
+reading the input data from another file and writing the result to a
+new file will be performed more like the way it is done in the next
+example. Even if the output format is ASCII armoured.
- #+begin_src python
- import gpg
+#+BEGIN_SRC python -i
+import gpg
- with open("/path/to/statement.txt", "rb") as tfile:
- text = tfile.read()
+with open("/path/to/statement.txt", "rb") as tfile:
+ text = tfile.read()
- c = gpg.Context()
- signed_data, result = c.sign(text, mode=gpg.constants.sig.mode.NORMAL)
+c = gpg.Context()
+signed_data, result = c.sign(text, mode=gpg.constants.sig.mode.NORMAL)
- with open("/path/to/statement.txt.sig", "wb") as afile:
- afile.write(signed_data)
- #+end_src
+with open("/path/to/statement.txt.sig", "wb") as afile:
+ afile.write(signed_data)
+#+END_SRC
*** Detached signing messages and files
@@ -791,41 +1138,40 @@
:CUSTOM_ID: howto-basic-signing-detached
:END:
- Detached signatures will often be needed in programmatic uses of
- GPGME, either for signing files (e.g. tarballs of code releases)
- or as a component of message signing (e.g. PGP/MIME encoded
- email).
+Detached signatures will often be needed in programmatic uses of
+GPGME, either for signing files (e.g. tarballs of code releases) or as
+a component of message signing (e.g. PGP/MIME encoded email).
- #+begin_src python
- import gpg
+#+BEGIN_SRC python -i
+import gpg
- text0 = """Declaration of ... something.
+text0 = """Declaration of ... something.
- """
- text = text0.encode()
+"""
+text = text0.encode()
- c = gpg.Context(armor=True)
- signed_data, result = c.sign(text, mode=gpg.constants.sig.mode.DETACH)
+c = gpg.Context(armor=True)
+signed_data, result = c.sign(text, mode=gpg.constants.sig.mode.DETACH)
- with open("/path/to/statement.txt.asc", "w") as afile:
- afile.write(signed_data.decode())
- #+end_src
+with open("/path/to/statement.txt.asc", "w") as afile:
+ afile.write(signed_data.decode())
+#+END_SRC
- As with normal signatures, detached signatures are best handled as
- byte literals, even when the output is ASCII armoured.
+As with normal signatures, detached signatures are best handled as
+byte literals, even when the output is ASCII armoured.
- #+begin_src python
- import gpg
+#+BEGIN_SRC python -i
+import gpg
- with open("/path/to/statement.txt", "rb") as tfile:
- text = tfile.read()
+with open("/path/to/statement.txt", "rb") as tfile:
+ text = tfile.read()
- c = gpg.Context(signers=sig_src)
- signed_data, result = c.sign(text, mode=gpg.constants.sig.mode.DETACH)
+c = gpg.Context(signers=sig_src)
+signed_data, result = c.sign(text, mode=gpg.constants.sig.mode.DETACH)
- with open("/path/to/statement.txt.sig", "wb") as afile:
- afile.write(signed_data)
- #+end_src
+with open("/path/to/statement.txt.sig", "wb") as afile:
+ afile.write(signed_data)
+#+END_SRC
*** Clearsigning messages or text
@@ -833,41 +1179,40 @@
:CUSTOM_ID: howto-basic-signing-clear
:END:
- Though PGP/in-line messages are no longer encouraged in favour of
- PGP/MIME, there is still sometimes value in utilising in-line
- signatures. This is where clear-signed messages or text is of
- value.
+Though PGP/in-line messages are no longer encouraged in favour of
+PGP/MIME, there is still sometimes value in utilising in-line
+signatures. This is where clear-signed messages or text is of value.
- #+begin_src python
- import gpg
+#+BEGIN_SRC python -i
+import gpg
- text0 = """Declaration of ... something.
+text0 = """Declaration of ... something.
- """
- text = text0.encode()
+"""
+text = text0.encode()
- c = gpg.Context()
- signed_data, result = c.sign(text, mode=gpg.constants.sig.mode.CLEAR)
+c = gpg.Context()
+signed_data, result = c.sign(text, mode=gpg.constants.sig.mode.CLEAR)
- with open("/path/to/statement.txt.asc", "w") as afile:
- afile.write(signed_data.decode())
- #+end_src
+with open("/path/to/statement.txt.asc", "w") as afile:
+ afile.write(signed_data.decode())
+#+END_SRC
- In spite of the appearance of a clear-signed message, the data
- handled by GPGME in signing it must still be byte literals.
+In spite of the appearance of a clear-signed message, the data handled
+by GPGME in signing it must still be byte literals.
- #+begin_src python
- import gpg
+#+BEGIN_SRC python -i
+import gpg
- with open("/path/to/statement.txt", "rb") as tfile:
- text = tfile.read()
+with open("/path/to/statement.txt", "rb") as tfile:
+ text = tfile.read()
- c = gpg.Context()
- signed_data, result = c.sign(text, mode=gpg.constants.sig.mode.CLEAR)
+c = gpg.Context()
+signed_data, result = c.sign(text, mode=gpg.constants.sig.mode.CLEAR)
- with open("/path/to/statement.txt.asc", "wb") as afile:
- afile.write(signed_data)
- #+end_src
+with open("/path/to/statement.txt.asc", "wb") as afile:
+ afile.write(signed_data)
+#+END_SRC
** Signature verification
@@ -875,152 +1220,152 @@
:CUSTOM_ID: howto-basic-verification
:END:
- Essentially there are two principal methods of verification of a
- signature. The first of these is for use with the normal or
- default signing method and for clear-signed messages. The second is
- for use with files and data with detached signatures.
-
- The following example is intended for use with the default signing
- method where the file was not ASCII armoured:
-
- #+begin_src python
- import gpg
- import time
-
- filename = "statement.txt"
- gpg_file = "statement.txt.gpg"
-
- c = gpg.Context()
-
- try:
- data, result = c.verify(open(gpg_file))
- verified = True
- except gpg.errors.BadSignatures as e:
- verified = False
- print(e)
-
- if verified is True:
- for i in range(len(result.signatures)):
- sign = result.signatures[i]
- print("""Good signature from:
- {0}
- with key {1}
- made at {2}
- """.format(c.get_key(sign.fpr).uids[0].uid,
- sign.fpr, time.ctime(sign.timestamp)))
- else:
- pass
- #+end_src
-
- Whereas this next example, which is almost identical would work
- with normal ASCII armoured files and with clear-signed files:
-
- #+begin_src python
- import gpg
- import time
-
- filename = "statement.txt"
- asc_file = "statement.txt.asc"
-
- c = gpg.Context()
-
- try:
- data, result = c.verify(open(asc_file))
- verified = True
- except gpg.errors.BadSignatures as e:
- verified = False
- print(e)
-
- if verified is True:
- for i in range(len(result.signatures)):
- sign = result.signatures[i]
- print("""Good signature from:
- {0}
- with key {1}
- made at {2}
- """.format(c.get_key(sign.fpr).uids[0].uid,
- sign.fpr, time.ctime(sign.timestamp)))
- else:
- pass
- #+end_src
-
- In both of the previous examples it is also possible to compare the
- original data that was signed against the signed data in =data= to
- see if it matches with something like this:
-
- #+begin_src python
- with open(filename, "rb") as afile:
- text = afile.read()
-
- if text == data:
- print("Good signature.")
- else:
- pass
- #+end_src
-
- The following two examples, however, deal with detached signatures.
- With his method of verification the data that was signed does not
- get returned since it is already being explicitly referenced in the
- first argument of =c.verify=. So =data= is =None= and only the
- information in =result= is available.
-
- #+begin_src python
- import gpg
- import time
-
- filename = "statement.txt"
- sig_file = "statement.txt.sig"
-
- c = gpg.Context()
-
- try:
- data, result = c.verify(open(filename), open(sig_file))
- verified = True
- except gpg.errors.BadSignatures as e:
- verified = False
- print(e)
-
- if verified is True:
- for i in range(len(result.signatures)):
- sign = result.signatures[i]
- print("""Good signature from:
- {0}
- with key {1}
- made at {2}
- """.format(c.get_key(sign.fpr).uids[0].uid,
- sign.fpr, time.ctime(sign.timestamp)))
- else:
- pass
- #+end_src
-
- #+begin_src python
- import gpg
- import time
-
- filename = "statement.txt"
- asc_file = "statement.txt.asc"
-
- c = gpg.Context()
-
- try:
- data, result = c.verify(open(filename), open(asc_file))
- verified = True
- except gpg.errors.BadSignatures as e:
- verified = False
- print(e)
-
- if verified is not None:
- for i in range(len(result.signatures)):
- sign = result.signatures[i]
- print("""Good signature from:
- {0}
- with key {1}
- made at {2}
- """.format(c.get_key(sign.fpr).uids[0].uid,
- sign.fpr, time.ctime(sign.timestamp)))
- else:
- pass
- #+end_src
+Essentially there are two principal methods of verification of a
+signature. The first of these is for use with the normal or default
+signing method and for clear-signed messages. The second is for use
+with files and data with detached signatures.
+
+The following example is intended for use with the default signing
+method where the file was not ASCII armoured:
+
+#+BEGIN_SRC python -i
+import gpg
+import time
+
+filename = "statement.txt"
+gpg_file = "statement.txt.gpg"
+
+c = gpg.Context()
+
+try:
+ data, result = c.verify(open(gpg_file))
+ verified = True
+except gpg.errors.BadSignatures as e:
+ verified = False
+ print(e)
+
+if verified is True:
+ for i in range(len(result.signatures)):
+ sign = result.signatures[i]
+ print("""Good signature from:
+{0}
+with key {1}
+made at {2}
+""".format(c.get_key(sign.fpr).uids[0].uid, sign.fpr,
+ time.ctime(sign.timestamp)))
+else:
+ pass
+#+END_SRC
+
+Whereas this next example, which is almost identical would work with
+normal ASCII armoured files and with clear-signed files:
+
+#+BEGIN_SRC python -i
+import gpg
+import time
+
+filename = "statement.txt"
+asc_file = "statement.txt.asc"
+
+c = gpg.Context()
+
+try:
+ data, result = c.verify(open(asc_file))
+ verified = True
+except gpg.errors.BadSignatures as e:
+ verified = False
+ print(e)
+
+if verified is True:
+ for i in range(len(result.signatures)):
+ sign = result.signatures[i]
+ print("""Good signature from:
+{0}
+with key {1}
+made at {2}
+""".format(c.get_key(sign.fpr).uids[0].uid, sign.fpr,
+ time.ctime(sign.timestamp)))
+else:
+ pass
+#+END_SRC
+
+In both of the previous examples it is also possible to compare the
+original data that was signed against the signed data in =data= to see
+if it matches with something like this:
+
+#+BEGIN_SRC python -i
+with open(filename, "rb") as afile:
+ text = afile.read()
+
+if text == data:
+ print("Good signature.")
+else:
+ pass
+#+END_SRC
+
+The following two examples, however, deal with detached signatures.
+With his method of verification the data that was signed does not get
+returned since it is already being explicitly referenced in the first
+argument of =c.verify=. So =data= is =None= and only the information
+in =result= is available.
+
+#+BEGIN_SRC python -i
+import gpg
+import time
+
+filename = "statement.txt"
+sig_file = "statement.txt.sig"
+
+c = gpg.Context()
+
+try:
+ data, result = c.verify(open(filename), open(sig_file))
+ verified = True
+except gpg.errors.BadSignatures as e:
+ verified = False
+ print(e)
+
+if verified is True:
+ for i in range(len(result.signatures)):
+ sign = result.signatures[i]
+ print("""Good signature from:
+{0}
+with key {1}
+made at {2}
+""".format(c.get_key(sign.fpr).uids[0].uid, sign.fpr,
+ time.ctime(sign.timestamp)))
+else:
+ pass
+#+END_SRC
+
+#+BEGIN_SRC python -i
+import gpg
+import time
+
+filename = "statement.txt"
+asc_file = "statement.txt.asc"
+
+c = gpg.Context()
+
+try:
+ data, result = c.verify(open(filename), open(asc_file))
+ verified = True
+except gpg.errors.BadSignatures as e:
+ verified = False
+ print(e)
+
+if verified is True:
+ for i in range(len(result.signatures)):
+ sign = result.signatures[i]
+ print("""Good signature from:
+{0}
+with key {1}
+made at {2}
+""".format(c.get_key(sign.fpr).uids[0].uid, sign.fpr,
+ time.ctime(sign.timestamp)))
+else:
+ pass
+#+END_SRC
* Creating keys and subkeys
@@ -1028,35 +1373,33 @@
:CUSTOM_ID: key-generation
:END:
- The one thing, aside from GnuPG itself, that GPGME depends on, of
- course, is the keys themselves. So it is necessary to be able to
- generate them and modify them by adding subkeys, revoking or
- disabling them, sometimes deleting them and doing the same for user
- IDs.
-
- In the following examples a key will be created for the world's
- greatest secret agent, Danger Mouse. Since Danger Mouse is a secret
- agent he needs to be able to protect information to =SECRET= level
- clearance, so his keys will be 3072-bit keys.
-
- The pre-configured =gpg.conf= file which sets cipher, digest and
- other preferences contains the following configuration parameters:
-
- #+begin_src conf
- expert
- allow-freeform-uid
- allow-secret-key-import
- trust-model tofu+pgp
- tofu-default-policy unknown
- enable-large-rsa
- enable-dsa2
- # cert-digest-algo SHA256
- cert-digest-algo SHA512
- default-preference-list TWOFISH CAMELLIA256 AES256 CAMELLIA192 AES192 CAMELLIA128 AES BLOWFISH IDEA CAST5 3DES SHA512 SHA384 SHA256 SHA224 RIPEMD160 SHA1 ZLIB BZIP2 ZIP Uncompressed
- personal-cipher-preferences TWOFISH CAMELLIA256 AES256 CAMELLIA192 AES192 CAMELLIA128 AES BLOWFISH IDEA CAST5 3DES
- personal-digest-preferences SHA512 SHA384 SHA256 SHA224 RIPEMD160 SHA1
- personal-compress-preferences ZLIB BZIP2 ZIP Uncompressed
- #+end_src
+The one thing, aside from GnuPG itself, that GPGME depends on, of
+course, is the keys themselves. So it is necessary to be able to
+generate them and modify them by adding subkeys, revoking or disabling
+them, sometimes deleting them and doing the same for user IDs.
+
+In the following examples a key will be created for the world's
+greatest secret agent, Danger Mouse. Since Danger Mouse is a secret
+agent he needs to be able to protect information to =SECRET= level
+clearance, so his keys will be 3072-bit keys.
+
+The pre-configured =gpg.conf= file which sets cipher, digest and other
+preferences contains the following configuration parameters:
+
+#+BEGIN_SRC conf
+ expert
+ allow-freeform-uid
+ allow-secret-key-import
+ trust-model tofu+pgp
+ tofu-default-policy unknown
+ enable-large-rsa
+ enable-dsa2
+ cert-digest-algo SHA512
+ default-preference-list TWOFISH CAMELLIA256 AES256 CAMELLIA192 AES192 CAMELLIA128 AES BLOWFISH IDEA CAST5 3DES SHA512 SHA384 SHA256 SHA224 RIPEMD160 SHA1 ZLIB BZIP2 ZIP Uncompressed
+ personal-cipher-preferences TWOFISH CAMELLIA256 AES256 CAMELLIA192 AES192 CAMELLIA128 AES BLOWFISH IDEA CAST5 3DES
+ personal-digest-preferences SHA512 SHA384 SHA256 SHA224 RIPEMD160 SHA1
+ personal-compress-preferences ZLIB BZIP2 ZIP Uncompressed
+#+END_SRC
** Primary key
@@ -1064,100 +1407,98 @@
:CUSTOM_ID: keygen-primary
:END:
- Generating a primary key uses the =create_key= method in a Context.
- It contains multiple arguments and keyword arguments, including:
- =userid=, =algorithm=, =expires_in=, =expires=, =sign=, =encrypt=,
- =certify=, =authenticate=, =passphrase= and =force=. The defaults
- for all of those except =userid=, =algorithm=, =expires_in=,
- =expires= and =passphrase= is =False=. The defaults for
- =algorithm= and =passphrase= is =None=. The default for
- =expires_in= is =0=. The default for =expires= is =True=. There
- is no default for =userid=.
-
- If =passphrase= is left as =None= then the key will not be
- generated with a passphrase, if =passphrase= is set to a string
- then that will be the passphrase and if =passphrase= is set to
- =True= then gpg-agent will launch pinentry to prompt for a
- passphrase. For the sake of convenience, these examples will keep
- =passphrase= set to =None=.
-
- #+begin_src python
- import gpg
-
- c = gpg.Context()
-
- c.home_dir = "~/.gnupg-dm"
- userid = "Danger Mouse <[email protected]>"
-
- dmkey = c.create_key(userid, algorithm="rsa3072", expires_in=31536000,
- sign=True, certify=True)
- #+end_src
-
- One thing to note here is the use of setting the =c.home_dir=
- parameter. This enables generating the key or keys in a different
- location. In this case to keep the new key data created for this
- example in a separate location rather than adding it to existing
- and active key store data. As with the default directory,
- =~/.gnupg=, any temporary or separate directory needs the
- permissions set to only permit access by the directory owner. On
- posix systems this means setting the directory permissions to 700.
-
- The =temp-homedir-config.py= script in the HOWTO examples directory
- will create an alternative homedir with these configuration options
- already set and the correct directory and file permissions.
-
- The successful generation of the key can be confirmed via the
- returned =GenkeyResult= object, which includes the following data:
-
- #+begin_src python
- print("""
- Fingerprint: {0}
- Primary Key: {1}
- Public Key: {2}
- Secret Key: {3}
- Sub Key: {4}
- User IDs: {5}
- """.format(dmkey.fpr, dmkey.primary, dmkey.pubkey, dmkey.seckey, dmkey.sub,
- dmkey.uid))
- #+end_src
-
- Alternatively the information can be confirmed using the command
- line program:
-
- #+begin_src shell
- bash-4.4$ gpg --homedir ~/.gnupg-dm -K
- ~/.gnupg-dm/pubring.kbx
- ----------------------
- sec rsa3072 2018-03-15 [SC] [expires: 2019-03-15]
- 177B7C25DB99745EE2EE13ED026D2F19E99E63AA
- uid [ultimate] Danger Mouse <[email protected]>
-
- bash-4.4$
- #+end_src
-
- As with generating keys manually, to preconfigure expanded
- preferences for the cipher, digest and compression algorithms, the
- =gpg.conf= file must contain those details in the home directory in
- which the new key is being generated. I used a cut down version of
- my own =gpg.conf= file in order to be able to generate this:
-
- #+begin_src shell
- bash-4.4$ gpg --homedir ~/.gnupg-dm --edit-key 177B7C25DB99745EE2EE13ED026D2F19E99E63AA showpref quit
- Secret key is available.
-
- sec rsa3072/026D2F19E99E63AA
- created: 2018-03-15 expires: 2019-03-15 usage: SC
- trust: ultimate validity: ultimate
- [ultimate] (1). Danger Mouse <[email protected]>
-
- [ultimate] (1). Danger Mouse <[email protected]>
- Cipher: TWOFISH, CAMELLIA256, AES256, CAMELLIA192, AES192, CAMELLIA128, AES, BLOWFISH, IDEA, CAST5, 3DES
- Digest: SHA512, SHA384, SHA256, SHA224, RIPEMD160, SHA1
- Compression: ZLIB, BZIP2, ZIP, Uncompressed
- Features: MDC, Keyserver no-modify
-
- bash-4.4$
- #+end_src
+Generating a primary key uses the =create_key= method in a Context.
+It contains multiple arguments and keyword arguments, including:
+=userid=, =algorithm=, =expires_in=, =expires=, =sign=, =encrypt=,
+=certify=, =authenticate=, =passphrase= and =force=. The defaults for
+all of those except =userid=, =algorithm=, =expires_in=, =expires= and
+=passphrase= is =False=. The defaults for =algorithm= and
+=passphrase= is =None=. The default for =expires_in= is =0=. The
+default for =expires= is =True=. There is no default for =userid=.
+
+If =passphrase= is left as =None= then the key will not be generated
+with a passphrase, if =passphrase= is set to a string then that will
+be the passphrase and if =passphrase= is set to =True= then gpg-agent
+will launch pinentry to prompt for a passphrase. For the sake of
+convenience, these examples will keep =passphrase= set to =None=.
+
+#+BEGIN_SRC python -i
+import gpg
+
+c = gpg.Context()
+
+c.home_dir = "~/.gnupg-dm"
+userid = "Danger Mouse <[email protected]>"
+
+dmkey = c.create_key(userid, algorithm="rsa3072", expires_in=31536000,
+ sign=True, certify=True)
+#+END_SRC
+
+One thing to note here is the use of setting the =c.home_dir=
+parameter. This enables generating the key or keys in a different
+location. In this case to keep the new key data created for this
+example in a separate location rather than adding it to existing and
+active key store data. As with the default directory, =~/.gnupg=, any
+temporary or separate directory needs the permissions set to only
+permit access by the directory owner. On posix systems this means
+setting the directory permissions to 700.
+
+The =temp-homedir-config.py= script in the HOWTO examples directory
+will create an alternative homedir with these configuration options
+already set and the correct directory and file permissions.
+
+The successful generation of the key can be confirmed via the returned
+=GenkeyResult= object, which includes the following data:
+
+#+BEGIN_SRC python -i
+print("""
+ Fingerprint: {0}
+ Primary Key: {1}
+ Public Key: {2}
+ Secret Key: {3}
+ Sub Key: {4}
+User IDs: {5}
+""".format(dmkey.fpr, dmkey.primary, dmkey.pubkey, dmkey.seckey, dmkey.sub,
+ dmkey.uid))
+#+END_SRC
+
+Alternatively the information can be confirmed using the command line
+program:
+
+#+BEGIN_SRC shell
+ bash-4.4$ gpg --homedir ~/.gnupg-dm -K
+ ~/.gnupg-dm/pubring.kbx
+ ----------------------
+ sec rsa3072 2018-03-15 [SC] [expires: 2019-03-15]
+ 177B7C25DB99745EE2EE13ED026D2F19E99E63AA
+ uid [ultimate] Danger Mouse <[email protected]>
+
+ bash-4.4$
+#+END_SRC
+
+As with generating keys manually, to preconfigure expanded preferences
+for the cipher, digest and compression algorithms, the =gpg.conf= file
+must contain those details in the home directory in which the new key
+is being generated. I used a cut down version of my own =gpg.conf=
+file in order to be able to generate this:
+
+#+BEGIN_SRC shell
+ bash-4.4$ gpg --homedir ~/.gnupg-dm --edit-key 177B7C25DB99745EE2EE13ED026D2F19E99E63AA showpref quit
+ Secret key is available.
+
+ sec rsa3072/026D2F19E99E63AA
+ created: 2018-03-15 expires: 2019-03-15 usage: SC
+ trust: ultimate validity: ultimate
+ [ultimate] (1). Danger Mouse <[email protected]>
+
+ [ultimate] (1). Danger Mouse <[email protected]>
+ Cipher: TWOFISH, CAMELLIA256, AES256, CAMELLIA192, AES192, CAMELLIA128, AES, BLOWFISH, IDEA, CAST5, 3DES
+ Digest: SHA512, SHA384, SHA256, SHA224, RIPEMD160, SHA1
+ Compression: ZLIB, BZIP2, ZIP, Uncompressed
+ Features: MDC, Keyserver no-modify
+
+ bash-4.4$
+#+END_SRC
** Subkeys
@@ -1165,55 +1506,55 @@
:CUSTOM_ID: keygen-subkeys
:END:
- Adding subkeys to a primary key is fairly similar to creating the
- primary key with the =create_subkey= method. Most of the arguments
- are the same, but not quite all. Instead of the =userid= argument
- there is now a =key= argument for selecting which primary key to
- add the subkey to.
-
- In the following example an encryption subkey will be added to the
- primary key. Since Danger Mouse is a security conscious secret
- agent, this subkey will only be valid for about six months, half
- the length of the primary key.
-
- #+begin_src python
- import gpg
-
- c = gpg.Context()
- c.home_dir = "~/.gnupg-dm"
-
- key = c.get_key(dmkey.fpr, secret=True)
- dmsub = c.create_subkey(key, algorithm="rsa3072", expires_in=15768000,
- encrypt=True)
- #+end_src
-
- As with the primary key, the results here can be checked with:
-
- #+begin_src python
- print("""
- Fingerprint: {0}
- Primary Key: {1}
- Public Key: {2}
- Secret Key: {3}
- Sub Key: {4}
- User IDs: {5}
- """.format(dmsub.fpr, dmsub.primary, dmsub.pubkey, dmsub.seckey, dmsub.sub,
- dmsub.uid))
- #+end_src
-
- As well as on the command line with:
-
- #+begin_src shell
- bash-4.4$ gpg --homedir ~/.gnupg-dm -K
- ~/.gnupg-dm/pubring.kbx
- ----------------------
- sec rsa3072 2018-03-15 [SC] [expires: 2019-03-15]
- 177B7C25DB99745EE2EE13ED026D2F19E99E63AA
- uid [ultimate] Danger Mouse <[email protected]>
- ssb rsa3072 2018-03-15 [E] [expires: 2018-09-13]
-
- bash-4.4$
- #+end_src
+Adding subkeys to a primary key is fairly similar to creating the
+primary key with the =create_subkey= method. Most of the arguments
+are the same, but not quite all. Instead of the =userid= argument
+there is now a =key= argument for selecting which primary key to add
+the subkey to.
+
+In the following example an encryption subkey will be added to the
+primary key. Since Danger Mouse is a security conscious secret agent,
+this subkey will only be valid for about six months, half the length
+of the primary key.
+
+#+BEGIN_SRC python -i
+import gpg
+
+c = gpg.Context()
+c.home_dir = "~/.gnupg-dm"
+
+key = c.get_key(dmkey.fpr, secret=True)
+dmsub = c.create_subkey(key, algorithm="rsa3072", expires_in=15768000,
+ encrypt=True)
+#+END_SRC
+
+As with the primary key, the results here can be checked with:
+
+#+BEGIN_SRC python -i
+print("""
+ Fingerprint: {0}
+ Primary Key: {1}
+ Public Key: {2}
+ Secret Key: {3}
+ Sub Key: {4}
+User IDs: {5}
+""".format(dmsub.fpr, dmsub.primary, dmsub.pubkey, dmsub.seckey, dmsub.sub,
+ dmsub.uid))
+#+END_SRC
+
+As well as on the command line with:
+
+#+BEGIN_SRC shell
+ bash-4.4$ gpg --homedir ~/.gnupg-dm -K
+ ~/.gnupg-dm/pubring.kbx
+ ----------------------
+ sec rsa3072 2018-03-15 [SC] [expires: 2019-03-15]
+ 177B7C25DB99745EE2EE13ED026D2F19E99E63AA
+ uid [ultimate] Danger Mouse <[email protected]>
+ ssb rsa3072 2018-03-15 [E] [expires: 2018-09-13]
+
+ bash-4.4$
+#+END_SRC
** User IDs
@@ -1227,38 +1568,38 @@
:CUSTOM_ID: keygen-uids-add
:END:
- By comparison to creating primary keys and subkeys, adding a new
- user ID to an existing key is much simpler. The method used to do
- this is =key_add_uid= and the only arguments it takes are for the
- =key= and the new =uid=.
+By comparison to creating primary keys and subkeys, adding a new user
+ID to an existing key is much simpler. The method used to do this is
+=key_add_uid= and the only arguments it takes are for the =key= and
+the new =uid=.
- #+begin_src python
- import gpg
+#+BEGIN_SRC python -i
+import gpg
- c = gpg.Context()
- c.home_dir = "~/.gnupg-dm"
+c = gpg.Context()
+c.home_dir = "~/.gnupg-dm"
- dmfpr = "177B7C25DB99745EE2EE13ED026D2F19E99E63AA"
- key = c.get_key(dmfpr, secret=True)
- uid = "Danger Mouse <[email protected]>"
+dmfpr = "177B7C25DB99745EE2EE13ED026D2F19E99E63AA"
+key = c.get_key(dmfpr, secret=True)
+uid = "Danger Mouse <[email protected]>"
- c.key_add_uid(key, uid)
- #+end_src
+c.key_add_uid(key, uid)
+#+END_SRC
- Unsurprisingly the result of this is:
+Unsurprisingly the result of this is:
- #+begin_src shell
- bash-4.4$ gpg --homedir ~/.gnupg-dm -K
- ~/.gnupg-dm/pubring.kbx
- ----------------------
- sec rsa3072 2018-03-15 [SC] [expires: 2019-03-15]
- 177B7C25DB99745EE2EE13ED026D2F19E99E63AA
- uid [ultimate] Danger Mouse <[email protected]>
- uid [ultimate] Danger Mouse <[email protected]>
- ssb rsa3072 2018-03-15 [E] [expires: 2018-09-13]
+#+BEGIN_SRC shell
+ bash-4.4$ gpg --homedir ~/.gnupg-dm -K
+ ~/.gnupg-dm/pubring.kbx
+ ----------------------
+ sec rsa3072 2018-03-15 [SC] [expires: 2019-03-15]
+ 177B7C25DB99745EE2EE13ED026D2F19E99E63AA
+ uid [ultimate] Danger Mouse <[email protected]>
+ uid [ultimate] Danger Mouse <[email protected]>
+ ssb rsa3072 2018-03-15 [E] [expires: 2018-09-13]
- bash-4.4$
- #+end_src
+ bash-4.4$
+#+END_SRC
*** Revokinging User IDs
@@ -1266,21 +1607,21 @@
:CUSTOM_ID: keygen-uids-revoke
:END:
- Revoking a user ID is a fairly similar process, except that it
- uses the =key_revoke_uid= method.
+Revoking a user ID is a fairly similar process, except that it uses
+the =key_revoke_uid= method.
- #+begin_src python
- import gpg
+#+BEGIN_SRC python -i
+import gpg
- c = gpg.Context()
- c.home_dir = "~/.gnupg-dm"
+c = gpg.Context()
+c.home_dir = "~/.gnupg-dm"
- dmfpr = "177B7C25DB99745EE2EE13ED026D2F19E99E63AA"
- key = c.get_key(dmfpr, secret=True)
- uid = "Danger Mouse <[email protected]>"
+dmfpr = "177B7C25DB99745EE2EE13ED026D2F19E99E63AA"
+key = c.get_key(dmfpr, secret=True)
+uid = "Danger Mouse <[email protected]>"
- c.key_revoke_uid(key, uid)
- #+end_src
+c.key_revoke_uid(key, uid)
+#+END_SRC
** Key certification
@@ -1288,37 +1629,37 @@
:CUSTOM_ID: key-sign
:END:
- Since key certification is more frequently referred to as key
- signing, the method used to perform this function is =key_sign=.
+Since key certification is more frequently referred to as key signing,
+the method used to perform this function is =key_sign=.
- The =key_sign= method takes four arguments: =key=, =uids=,
- =expires_in= and =local=. The default value of =uids= is =None=
- and which results in all user IDs being selected. The default
- value of both =expires_in= and =local= is =False=; which results in
- the signature never expiring and being able to be exported.
+The =key_sign= method takes four arguments: =key=, =uids=,
+=expires_in= and =local=. The default value of =uids= is =None= and
+which results in all user IDs being selected. The default value of
+both =expires_in= and =local= is =False=; which results in the
+signature never expiring and being able to be exported.
- The =key= is the key being signed rather than the key doing the
- signing. To change the key doing the signing refer to the signing
- key selection above for signing messages and files.
+The =key= is the key being signed rather than the key doing the
+signing. To change the key doing the signing refer to the signing key
+selection above for signing messages and files.
- If the =uids= value is not =None= then it must either be a string
- to match a single user ID or a list of strings to match multiple
- user IDs. In this case the matching of those strings must be
- precise and it is case sensitive.
+If the =uids= value is not =None= then it must either be a string to
+match a single user ID or a list of strings to match multiple user
+IDs. In this case the matching of those strings must be precise and
+it is case sensitive.
- To sign Danger Mouse's key for just the initial user ID with a
- signature which will last a little over a month, do this:
+To sign Danger Mouse's key for just the initial user ID with a
+signature which will last a little over a month, do this:
- #+begin_src python
- import gpg
+#+BEGIN_SRC python -i
+import gpg
- c = gpg.Context()
- uid = "Danger Mouse <[email protected]>"
+c = gpg.Context()
+uid = "Danger Mouse <[email protected]>"
- dmfpr = "177B7C25DB99745EE2EE13ED026D2F19E99E63AA"
- key = c.get_key(dmfpr, secret=True)
- c.key_sign(key, uids=uid, expires_in=2764800)
- #+end_src
+dmfpr = "177B7C25DB99745EE2EE13ED026D2F19E99E63AA"
+key = c.get_key(dmfpr, secret=True)
+c.key_sign(key, uids=uid, expires_in=2764800)
+#+END_SRC
* Miscellaneous work-arounds
@@ -1332,44 +1673,52 @@
:CUSTOM_ID: group-lines
:END:
- There is not yet an easy way to access groups configured in the
- gpg.conf file from within GPGME. As a consequence these central
- groupings of keys cannot be shared amongst multiple programs, such
- as MUAs readily.
+There is not yet an easy way to access groups configured in the
+gpg.conf file from within GPGME. As a consequence these central
+groupings of keys cannot be shared amongst multiple programs, such as
+MUAs readily.
+
+The following code, however, provides a work-around for obtaining this
+information in Python.
- The following code, however, provides a work-around for obtaining
- this information in Python.
+#+BEGIN_SRC python -i
+import subprocess
- #+begin_src python
- import subprocess
+lines = subprocess.getoutput("gpgconf --list-options gpg").splitlines()
- lines = subprocess.getoutput("gpgconf --list-options gpg").splitlines()
+for i in range(len(lines)):
+ if lines[i].startswith("group") is True:
+ line = lines[i]
+ else:
+ pass
- for i in range(len(lines)):
- if lines[i].startswith("group") is True:
- line = lines[i]
- else:
- pass
+groups = line.split(":")[-1].replace('"', '').split(',')
- groups = line.split(":")[-1].replace('"', '').split(',')
+group_lines = []
+group_lists = []
- group_lines = groups
- for i in range(len(group_lines)):
- group_lines[i] = group_lines[i].split("=")
+for i in range(len(groups)):
+ group_lines.append(groups[i].split("="))
+ group_lists.append(groups[i].split("="))
- group_lists = group_lines
- for i in range(len(group_lists)):
- group_lists[i][1] = group_lists[i][1].split()
- #+end_src
+for i in range(len(group_lists)):
+ group_lists[i][1] = group_lists[i][1].split()
+#+END_SRC
- The result of that code is that =group_lines= is a list of lists
- where =group_lines[i][0]= is the name of the group and
- =group_lines[i][1]= is the key IDs of the group as a string.
+The result of that code is that =group_lines= is a list of lists where
+=group_lines[i][0]= is the name of the group and =group_lines[i][1]=
+is the key IDs of the group as a string.
- The =group_lists= result is very similar in that it is a list of
- lists. The first part, =group_lists[i][0]= matches
- =group_lines[i][0]= as the name of the group, but
- =group_lists[i][1]= is the key IDs of the group as a string.
+The =group_lists= result is very similar in that it is a list of
+lists. The first part, =group_lists[i][0]= matches
+=group_lines[i][0]= as the name of the group, but =group_lists[i][1]=
+is the key IDs of the group as a string.
+
+A demonstration of using the =groups.py= module is also available in
+the form of the executable =mutt-groups.py= script. This second
+script reads all the group entries in a user's =gpg.conf= file and
+converts them into crypt-hooks suitable for use with the Mutt and
+Neomutt mail clients.
* Copyright and Licensing
@@ -1383,7 +1732,7 @@
:CUSTOM_ID: copyright
:END:
- Copyright © The GnuPG Project, 2018.
+Copyright © The GnuPG Project, 2018.
** License GPL compatible
@@ -1391,14 +1740,14 @@
:CUSTOM_ID: license
:END:
- This file is free software; as a special exception the author gives
- unlimited permission to copy and/or distribute it, with or without
- modifications, as long as this notice is preserved.
+This file is free software; as a special exception the author gives
+unlimited permission to copy and/or distribute it, with or without
+modifications, as long as this notice is preserved.
- This file is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY, to the extent permitted by law; without even
- the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- PURPOSE.
+This file is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+PURPOSE.
* Footnotes
@@ -1411,3 +1760,7 @@
keyservers for "gnupg.org" produces over 400 results, the majority of
which aren't actually at the gnupg.org domain, but just included a
comment regarding the project in their key somewhere.
+
+[fn:4] As Python 3.7 is a very recent release, it is not given
+priority over 3.6 yet, but will probably be prioritised by the release
+of Python 3.7.2.
diff --git a/lang/python/examples/assuan.py b/lang/python/examples/assuan.py
index dd42ad40..6784c9eb 100644
--- a/lang/python/examples/assuan.py
+++ b/lang/python/examples/assuan.py
@@ -14,14 +14,14 @@
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
-
"""Demonstrate the use of the Assuan protocol engine"""
from __future__ import absolute_import, print_function, unicode_literals
-del absolute_import, print_function, unicode_literals
import gpg
+del absolute_import, print_function, unicode_literals
+
with gpg.Context(protocol=gpg.constants.protocol.ASSUAN) as c:
# Invoke the pinentry to get a confirmation.
err = c.assuan_transact(['GET_CONFIRMATION', 'Hello there'])
diff --git a/lang/python/examples/decryption-filter.py b/lang/python/examples/decryption-filter.py
index 987dfd13..4d99330b 100644
--- a/lang/python/examples/decryption-filter.py
+++ b/lang/python/examples/decryption-filter.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
#
-# Copyright (C) 2016 g10 Code GmbH
+# Copyright (C) 2016, 2018 g10 Code GmbH
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -14,7 +14,6 @@
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
-
"""A decryption filter
This demonstrates decryption using gpg3 in three lines of code. To
@@ -25,8 +24,10 @@ be used like this:
"""
from __future__ import absolute_import, print_function, unicode_literals
-del absolute_import, print_function, unicode_literals
import sys
import gpg
+
+del absolute_import, print_function, unicode_literals
+
gpg.Context().decrypt(sys.stdin, sink=sys.stdout)
diff --git a/lang/python/examples/delkey.py b/lang/python/examples/delkey.py
index 12510f3e..30b3145a 100755
--- a/lang/python/examples/delkey.py
+++ b/lang/python/examples/delkey.py
@@ -20,10 +20,11 @@
# It deletes keys for [email protected] generated by genkey.py script
from __future__ import absolute_import, print_function, unicode_literals
-del absolute_import, print_function, unicode_literals
import gpg
+del absolute_import, print_function, unicode_literals
+
with gpg.Context() as c:
# Note: We must not modify the key store during iteration,
# therefore, we explicitly make a list.
diff --git a/lang/python/examples/exportimport.py b/lang/python/examples/exportimport.py
index d84a01c3..36ced579 100755
--- a/lang/python/examples/exportimport.py
+++ b/lang/python/examples/exportimport.py
@@ -20,12 +20,13 @@
# It uses keys for [email protected] generated by genkey.py script
from __future__ import absolute_import, print_function, unicode_literals
-del absolute_import, print_function, unicode_literals
import sys
import os
import gpg
+del absolute_import, print_function, unicode_literals
+
with gpg.Context(armor=True) as c, gpg.Data() as expkey:
diff --git a/lang/python/examples/genkey.py b/lang/python/examples/genkey.py
index a043500e..710a530a 100755
--- a/lang/python/examples/genkey.py
+++ b/lang/python/examples/genkey.py
@@ -18,10 +18,11 @@
# 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
+del absolute_import, print_function, unicode_literals
+
# This is the example from the GPGME manual.
parms = """<GnupgKeyParms format="internal">
diff --git a/lang/python/examples/howto/decrypt-file.py b/lang/python/examples/howto/decrypt-file.py
index b38acc79..2fe37f27 100755
--- a/lang/python/examples/howto/decrypt-file.py
+++ b/lang/python/examples/howto/decrypt-file.py
@@ -32,10 +32,10 @@ if len(sys.argv) == 3:
newfile = sys.argv[2]
elif len(sys.argv) == 2:
ciphertext = sys.argv[1]
- newfile = input("Enter path and filename of file to save decrypted data to: ")
+ newfile = input("Enter path and filename to save decrypted data to: ")
else:
ciphertext = input("Enter path and filename of encrypted file: ")
- newfile = input("Enter path and filename of file to save decrypted data to: ")
+ newfile = input("Enter path and filename to save decrypted data to: ")
with open(ciphertext, "rb") as cfile:
try:
diff --git a/lang/python/examples/howto/encrypt-file.py b/lang/python/examples/howto/encrypt-file.py
index ad4e1cef..7c84a6f9 100755
--- a/lang/python/examples/howto/encrypt-file.py
+++ b/lang/python/examples/howto/encrypt-file.py
@@ -3,6 +3,9 @@
from __future__ import absolute_import, division, unicode_literals
+import gpg
+import sys
+
# Copyright (C) 2018 Ben McGinnes <[email protected]>
#
# This program is free software; you can redistribute it and/or modify it under
@@ -24,9 +27,6 @@ from __future__ import absolute_import, division, unicode_literals
# Lesser General Public along with this program; if not, see
# <http://www.gnu.org/licenses/>.
-import gpg
-import sys
-
"""
Encrypts a file to a specified key. If entering both the key and the filename
on the command line, the key must be entered first.
@@ -55,7 +55,7 @@ with open(filename, "rb") as f:
with gpg.Context(armor=True) as ca:
try:
ciphertext, result, sign_result = ca.encrypt(text, recipients=rkey,
- sign=False)
+ sign=False)
with open("{0}.asc".format(filename), "wb") as fa:
fa.write(ciphertext)
except gpg.errors.InvalidRecipients as e:
@@ -64,7 +64,7 @@ with gpg.Context(armor=True) as ca:
with gpg.Context() as cg:
try:
ciphertext, result, sign_result = cg.encrypt(text, recipients=rkey,
- sign=False)
+ sign=False)
with open("{0}.gpg".format(filename), "wb") as fg:
fg.write(ciphertext)
except gpg.errors.InvalidRecipients as e:
diff --git a/lang/python/examples/howto/encrypt-sign-file.py b/lang/python/examples/howto/encrypt-sign-file.py
index 41aaac86..a08176b7 100755
--- a/lang/python/examples/howto/encrypt-sign-file.py
+++ b/lang/python/examples/howto/encrypt-sign-file.py
@@ -3,6 +3,9 @@
from __future__ import absolute_import, division, unicode_literals
+import gpg
+import sys
+
# Copyright (C) 2018 Ben McGinnes <[email protected]>
#
# This program is free software; you can redistribute it and/or modify it under
@@ -24,9 +27,6 @@ from __future__ import absolute_import, division, unicode_literals
# Lesser General Public along with this program; if not, see
# <http://www.gnu.org/licenses/>.
-import gpg
-import sys
-
"""
Signs and encrypts a file to a specified key. If entering both the key and the
filename on the command line, the key must be entered first.
@@ -58,13 +58,13 @@ with open(filename, "rb") as f:
with gpg.Context(armor=True) as ca:
ciphertext, result, sign_result = ca.encrypt(text, recipients=rkey,
always_trust=True,
- add_encrypt_to=True)
+ add_encrypt_to=True)
with open("{0}.asc".format(filename), "wb") as fa:
fa.write(ciphertext)
with gpg.Context() as cg:
ciphertext, result, sign_result = cg.encrypt(text, recipients=rkey,
always_trust=True,
- add_encrypt_to=True)
+ add_encrypt_to=True)
with open("{0}.gpg".format(filename), "wb") as fg:
fg.write(ciphertext)
diff --git a/lang/python/examples/howto/encrypt-to-group-gullible.py b/lang/python/examples/howto/encrypt-to-group-gullible.py
new file mode 100755
index 00000000..c96e8294
--- /dev/null
+++ b/lang/python/examples/howto/encrypt-to-group-gullible.py
@@ -0,0 +1,81 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from __future__ import absolute_import, division, unicode_literals
+
+import gpg
+import sys
+from groups import group_lists
+
+# Copyright (C) 2018 Ben McGinnes <[email protected]>
+#
+# This program 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.
+#
+# This program 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 program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License and the GNU
+# Lesser General Public Licensefor more details.
+#
+# You should have received a copy of the GNU General Public License and the GNU
+# Lesser General Public along with this program; if not, see
+# <http://www.gnu.org/licenses/>.
+
+"""
+Uses the groups module to encrypt to multiple recipients.
+
+"""
+
+c = gpg.Context(armor=True)
+
+if len(sys.argv) > 3:
+ group_id = sys.argv[1]
+ filepath = sys.argv[2:]
+elif len(sys.argv) == 3:
+ group_id = sys.argv[1]
+ filepath = sys.argv[2]
+elif len(sys.argv) == 2:
+ group_id = sys.argv[1]
+ filepath = input("Enter the filename to encrypt: ")
+else:
+ group_id = input("Enter the group name to encrypt to: ")
+ filepath = input("Enter the filename to encrypt: ")
+
+with open(filepath, "rb") as f:
+ text = f.read()
+
+for i in range(len(group_lists)):
+ if group_lists[i][0] == group_id:
+ klist = group_lists[i][1]
+ else:
+ klist = None
+
+logrus = []
+
+if klist is not None:
+ for i in range(len(klist)):
+ apattern = list(c.keylist(pattern=klist[i], secret=False))
+ if apattern[0].can_encrypt == 1:
+ logrus.append(apattern[0])
+ else:
+ pass
+ try:
+ ciphertext, result, sign_result = c.encrypt(text, recipients=logrus,
+ add_encrypt_to=True)
+ except:
+ ciphertext, result, sign_result = c.encrypt(text, recipients=logrus,
+ add_encrypt_to=True,
+ always_trust=True)
+ with open("{0}.asc".format(filepath), "wb") as f:
+ f.write(ciphertext)
+else:
+ pass
+
+# EOF
diff --git a/lang/python/examples/howto/encrypt-to-group-trustno1.py b/lang/python/examples/howto/encrypt-to-group-trustno1.py
new file mode 100755
index 00000000..da0376b5
--- /dev/null
+++ b/lang/python/examples/howto/encrypt-to-group-trustno1.py
@@ -0,0 +1,90 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from __future__ import absolute_import, division, unicode_literals
+
+import gpg
+import sys
+from groups import group_lists
+
+# Copyright (C) 2018 Ben McGinnes <[email protected]>
+#
+# This program 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.
+#
+# This program 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 program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License and the GNU
+# Lesser General Public Licensefor more details.
+#
+# You should have received a copy of the GNU General Public License and the GNU
+# Lesser General Public along with this program; if not, see
+# <http://www.gnu.org/licenses/>.
+
+"""
+Uses the groups module to encrypt to multiple recipients.
+
+"""
+
+c = gpg.Context(armor=True)
+
+if len(sys.argv) > 3:
+ group_id = sys.argv[1]
+ filepath = sys.argv[2:]
+elif len(sys.argv) == 3:
+ group_id = sys.argv[1]
+ filepath = sys.argv[2]
+elif len(sys.argv) == 2:
+ group_id = sys.argv[1]
+ filepath = input("Enter the filename to encrypt: ")
+else:
+ group_id = input("Enter the group name to encrypt to: ")
+ filepath = input("Enter the filename to encrypt: ")
+
+with open(filepath, "rb") as f:
+ text = f.read()
+
+for i in range(len(group_lists)):
+ if group_lists[i][0] == group_id:
+ klist = group_lists[i][1]
+ else:
+ klist = None
+
+logrus = []
+
+if klist is not None:
+ for i in range(len(klist)):
+ apattern = list(c.keylist(pattern=klist[i], secret=False))
+ if apattern[0].can_encrypt == 1:
+ logrus.append(apattern[0])
+ else:
+ pass
+ try:
+ ciphertext, result, sign_result = c.encrypt(text, recipients=logrus,
+ add_encrypt_to=True)
+ except gpg.errors.InvalidRecipients as e:
+ for i in range(len(e.recipients)):
+ for n in range(len(logrus)):
+ if logrus[n].fpr == e.recipients[i].fpr:
+ logrus.remove(logrus[n])
+ else:
+ pass
+ try:
+ ciphertext, result, sign_result = c.encrypt(text,
+ recipients=logrus,
+ add_encrypt_to=True)
+ except:
+ pass
+ with open("{0}.asc".format(filepath), "wb") as f:
+ f.write(ciphertext)
+else:
+ pass
+
+# EOF
diff --git a/lang/python/examples/howto/encrypt-to-group.py b/lang/python/examples/howto/encrypt-to-group.py
new file mode 100755
index 00000000..d4cb0745
--- /dev/null
+++ b/lang/python/examples/howto/encrypt-to-group.py
@@ -0,0 +1,91 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from __future__ import absolute_import, division, unicode_literals
+
+import gpg
+import sys
+from groups import group_lists
+
+# Copyright (C) 2018 Ben McGinnes <[email protected]>
+#
+# This program 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.
+#
+# This program 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 program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License and the GNU
+# Lesser General Public Licensefor more details.
+#
+# You should have received a copy of the GNU General Public License and the GNU
+# Lesser General Public along with this program; if not, see
+# <http://www.gnu.org/licenses/>.
+
+"""
+Uses the groups module to encrypt to multiple recipients.
+
+"""
+
+c = gpg.Context(armor=True)
+
+if len(sys.argv) > 3:
+ group_id = sys.argv[1]
+ filepath = sys.argv[2:]
+elif len(sys.argv) == 3:
+ group_id = sys.argv[1]
+ filepath = sys.argv[2]
+elif len(sys.argv) == 2:
+ group_id = sys.argv[1]
+ filepath = input("Enter the filename to encrypt: ")
+else:
+ group_id = input("Enter the group name to encrypt to: ")
+ filepath = input("Enter the filename to encrypt: ")
+
+with open(filepath, "rb") as f:
+ text = f.read()
+
+for i in range(len(group_lists)):
+ if group_lists[i][0] == group_id:
+ klist = group_lists[i][1]
+ else:
+ klist = None
+
+logrus = []
+
+if klist is not None:
+ for i in range(len(klist)):
+ apattern = list(c.keylist(pattern=klist[i], secret=False))
+ if apattern[0].can_encrypt == 1:
+ logrus.append(apattern[0])
+ else:
+ pass
+ try:
+ ciphertext, result, sign_result = c.encrypt(text, recipients=logrus,
+ add_encrypt_to=True)
+ except gpg.errors.InvalidRecipients as e:
+ for i in range(len(e.recipients)):
+ for n in range(len(logrus)):
+ if logrus[n].fpr == e.recipients[i].fpr:
+ logrus.remove(logrus[n])
+ else:
+ pass
+ try:
+ ciphertext, result, sign_result = c.encrypt(text,
+ recipients=logrus,
+ add_encrypt_to=True,
+ always_trust=True)
+ except:
+ pass
+ with open("{0}.asc".format(filepath), "wb") as f:
+ f.write(ciphertext)
+else:
+ pass
+
+# EOF
diff --git a/lang/python/examples/howto/export-key.py b/lang/python/examples/howto/export-key.py
new file mode 100755
index 00000000..913bfce7
--- /dev/null
+++ b/lang/python/examples/howto/export-key.py
@@ -0,0 +1,73 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from __future__ import absolute_import, division, unicode_literals
+
+import gpg
+import os.path
+import sys
+
+# Copyright (C) 2018 Ben McGinnes <[email protected]>
+#
+# This program 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.
+#
+# This program 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 program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License and the GNU
+# Lesser General Public Licensefor more details.
+#
+# You should have received a copy of the GNU General Public License and the GNU
+# Lesser General Public along with this program; if not, see
+# <http://www.gnu.org/licenses/>.
+
+print("""
+This script exports one or more public keys.
+""")
+
+c = gpg.Context(armor=True)
+
+if len(sys.argv) >= 4:
+ keyfile = sys.argv[1]
+ logrus = sys.argv[2]
+ homedir = sys.argv[3]
+elif len(sys.argv) == 3:
+ keyfile = sys.argv[1]
+ logrus = sys.argv[2]
+ homedir = input("Enter the GPG configuration directory path (optional): ")
+elif len(sys.argv) == 2:
+ keyfile = sys.argv[1]
+ logrus = input("Enter the UID matching the key(s) to export: ")
+ homedir = input("Enter the GPG configuration directory path (optional): ")
+else:
+ keyfile = input("Enter the path and filename to save the key(s) to: ")
+ logrus = input("Enter the UID matching the key(s) to export: ")
+ homedir = input("Enter the GPG configuration directory path (optional): ")
+
+if homedir.startswith("~"):
+ if os.path.exists(os.path.expanduser(homedir)) is True:
+ c.home_dir = os.path.expanduser(homedir)
+ else:
+ pass
+elif os.path.exists(homedir) is True:
+ c.home_dir = homedir
+else:
+ pass
+
+try:
+ result = c.key_export(pattern=logrus)
+except:
+ result = c.key_export(pattern=None)
+
+if result is not None:
+ with open(keyfile, "wb") as f:
+ f.write(result)
+else:
+ pass
diff --git a/lang/python/examples/howto/export-minimised-key.py b/lang/python/examples/howto/export-minimised-key.py
new file mode 100755
index 00000000..3889adcd
--- /dev/null
+++ b/lang/python/examples/howto/export-minimised-key.py
@@ -0,0 +1,73 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from __future__ import absolute_import, division, unicode_literals
+
+import gpg
+import os.path
+import sys
+
+# Copyright (C) 2018 Ben McGinnes <[email protected]>
+#
+# This program 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.
+#
+# This program 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 program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License and the GNU
+# Lesser General Public Licensefor more details.
+#
+# You should have received a copy of the GNU General Public License and the GNU
+# Lesser General Public along with this program; if not, see
+# <http://www.gnu.org/licenses/>.
+
+print("""
+This script exports one or more public keys in minimised form.
+""")
+
+c = gpg.Context(armor=True)
+
+if len(sys.argv) >= 4:
+ keyfile = sys.argv[1]
+ logrus = sys.argv[2]
+ homedir = sys.argv[3]
+elif len(sys.argv) == 3:
+ keyfile = sys.argv[1]
+ logrus = sys.argv[2]
+ homedir = input("Enter the GPG configuration directory path (optional): ")
+elif len(sys.argv) == 2:
+ keyfile = sys.argv[1]
+ logrus = input("Enter the UID matching the key(s) to export: ")
+ homedir = input("Enter the GPG configuration directory path (optional): ")
+else:
+ keyfile = input("Enter the path and filename to save the key(s) to: ")
+ logrus = input("Enter the UID matching the key(s) to export: ")
+ homedir = input("Enter the GPG configuration directory path (optional): ")
+
+if homedir.startswith("~"):
+ if os.path.exists(os.path.expanduser(homedir)) is True:
+ c.home_dir = os.path.expanduser(homedir)
+ else:
+ pass
+elif os.path.exists(homedir) is True:
+ c.home_dir = homedir
+else:
+ pass
+
+try:
+ result = c.key_export_minimal(pattern=logrus)
+except:
+ result = c.key_export_minimal(pattern=None)
+
+if result is not None:
+ with open(keyfile, "wb") as f:
+ f.write(result)
+else:
+ pass
diff --git a/lang/python/examples/howto/export-secret-key.py b/lang/python/examples/howto/export-secret-key.py
new file mode 100755
index 00000000..e9c53fe5
--- /dev/null
+++ b/lang/python/examples/howto/export-secret-key.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from __future__ import absolute_import, division, unicode_literals
+
+import gpg
+import os
+import os.path
+import sys
+
+# Copyright (C) 2018 Ben McGinnes <[email protected]>
+#
+# This program 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.
+#
+# This program 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 program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License and the GNU
+# Lesser General Public Licensefor more details.
+#
+# You should have received a copy of the GNU General Public License and the GNU
+# Lesser General Public along with this program; if not, see
+# <http://www.gnu.org/licenses/>.
+
+print("""
+This script exports one or more secret keys.
+
+The gpg-agent and pinentry are invoked to authorise the export.
+""")
+
+c = gpg.Context(armor=True)
+
+if len(sys.argv) >= 4:
+ keyfile = sys.argv[1]
+ logrus = sys.argv[2]
+ homedir = sys.argv[3]
+elif len(sys.argv) == 3:
+ keyfile = sys.argv[1]
+ logrus = sys.argv[2]
+ homedir = input("Enter the GPG configuration directory path (optional): ")
+elif len(sys.argv) == 2:
+ keyfile = sys.argv[1]
+ logrus = input("Enter the UID matching the secret key(s) to export: ")
+ homedir = input("Enter the GPG configuration directory path (optional): ")
+else:
+ keyfile = input("Enter the path and filename to save the secret key to: ")
+ logrus = input("Enter the UID matching the secret key(s) to export: ")
+ homedir = input("Enter the GPG configuration directory path (optional): ")
+
+if homedir.startswith("~"):
+ if os.path.exists(os.path.expanduser(homedir)) is True:
+ c.home_dir = os.path.expanduser(homedir)
+ else:
+ pass
+elif os.path.exists(homedir) is True:
+ c.home_dir = homedir
+else:
+ pass
+
+try:
+ result = c.key_export_secret(pattern=logrus)
+except:
+ result = c.key_export_secret(pattern=None)
+
+if result is not None:
+ with open(keyfile, "wb") as f:
+ f.write(result)
+ os.chmod(keyfile, 0o600)
+else:
+ pass
diff --git a/lang/python/examples/howto/export-secret-keys.py b/lang/python/examples/howto/export-secret-keys.py
new file mode 100755
index 00000000..f0a791ef
--- /dev/null
+++ b/lang/python/examples/howto/export-secret-keys.py
@@ -0,0 +1,110 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from __future__ import absolute_import, division, unicode_literals
+
+import gpg
+import os
+import os.path
+import subprocess
+import sys
+
+# Copyright (C) 2018 Ben McGinnes <[email protected]>
+#
+# This program 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.
+#
+# This program 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 program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License and the GNU
+# Lesser General Public Licensefor more details.
+#
+# You should have received a copy of the GNU General Public License and the GNU
+# Lesser General Public along with this program; if not, see
+# <http://www.gnu.org/licenses/>.
+
+print("""
+This script exports one or more secret keys as both ASCII armored and binary
+file formats, saved in files within the user's GPG home directory.
+
+The gpg-agent and pinentry are invoked to authorise the export.
+""")
+
+if sys.platform == "win32":
+ gpgconfcmd = "gpgconf.exe --list-dirs homedir"
+else:
+ gpgconfcmd = "gpgconf --list-dirs homedir"
+
+a = gpg.Context(armor=True)
+b = gpg.Context()
+c = gpg.Context()
+
+if len(sys.argv) >= 4:
+ keyfile = sys.argv[1]
+ logrus = sys.argv[2]
+ homedir = sys.argv[3]
+elif len(sys.argv) == 3:
+ keyfile = sys.argv[1]
+ logrus = sys.argv[2]
+ homedir = input("Enter the GPG configuration directory path (optional): ")
+elif len(sys.argv) == 2:
+ keyfile = sys.argv[1]
+ logrus = input("Enter the UID matching the secret key(s) to export: ")
+ homedir = input("Enter the GPG configuration directory path (optional): ")
+else:
+ keyfile = input("Enter the filename to save the secret key to: ")
+ logrus = input("Enter the UID matching the secret key(s) to export: ")
+ homedir = input("Enter the GPG configuration directory path (optional): ")
+
+if homedir.startswith("~"):
+ if os.path.exists(os.path.expanduser(homedir)) is True:
+ c.home_dir = os.path.expanduser(homedir)
+ else:
+ pass
+elif os.path.exists(homedir) is True:
+ c.home_dir = homedir
+else:
+ pass
+
+if c.home_dir is not None:
+ if c.home_dir.endswith("/"):
+ gpgfile = "{0}{1}.gpg".format(c.home_dir, keyfile)
+ ascfile = "{0}{1}.asc".format(c.home_dir, keyfile)
+ else:
+ gpgfile = "{0}/{1}.gpg".format(c.home_dir, keyfile)
+ ascfile = "{0}/{1}.asc".format(c.home_dir, keyfile)
+else:
+ if os.path.exists(os.environ["GNUPGHOME"]) is True:
+ hd = os.environ["GNUPGHOME"]
+ else:
+ hd = subprocess.getoutput(gpgconfcmd)
+ gpgfile = "{0}/{1}.gpg".format(hd, keyfile)
+ ascfile = "{0}/{1}.asc".format(hd, keyfile)
+
+try:
+ a_result = a.key_export_secret(pattern=logrus)
+ b_result = b.key_export_secret(pattern=logrus)
+except:
+ a_result = a.key_export_secret(pattern=None)
+ b_result = b.key_export_secret(pattern=None)
+
+if a_result is not None:
+ with open(ascfile, "wb") as f:
+ f.write(a_result)
+ os.chmod(ascfile, 0o600)
+else:
+ pass
+
+if b_result is not None:
+ with open(gpgfile, "wb") as f:
+ f.write(b_result)
+ os.chmod(gpgfile, 0o600)
+else:
+ pass
diff --git a/lang/python/examples/howto/groups.py b/lang/python/examples/howto/groups.py
index 5e7fdf60..b8317b69 100644
--- a/lang/python/examples/howto/groups.py
+++ b/lang/python/examples/howto/groups.py
@@ -24,6 +24,7 @@ from __future__ import absolute_import, division, unicode_literals
# <http://www.gnu.org/licenses/>.
import subprocess
+import sys
"""
Intended for use with other scripts.
@@ -31,7 +32,12 @@ Intended for use with other scripts.
Usage: from groups import group_lists
"""
-lines = subprocess.getoutput("gpgconf --list-options gpg").splitlines()
+if sys.platform == "win32":
+ gpgconfcmd = "gpgconf.exe --list-options gpg"
+else:
+ gpgconfcmd = "gpgconf --list-options gpg"
+
+lines = subprocess.getoutput(gpgconfcmd).splitlines()
for i in range(len(lines)):
if lines[i].startswith("group") is True:
@@ -41,10 +47,12 @@ for i in range(len(lines)):
groups = line.split(":")[-1].replace('"', '').split(',')
-group_lines = groups
-for i in range(len(group_lines)):
- group_lines[i] = group_lines[i].split("=")
+group_lines = []
+group_lists = []
+
+for i in range(len(groups)):
+ group_lines.append(groups[i].split("="))
+ group_lists.append(groups[i].split("="))
-group_lists = group_lines
for i in range(len(group_lists)):
group_lists[i][1] = group_lists[i][1].split()
diff --git a/lang/python/examples/howto/import-key.py b/lang/python/examples/howto/import-key.py
new file mode 100755
index 00000000..25913785
--- /dev/null
+++ b/lang/python/examples/howto/import-key.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from __future__ import absolute_import, division, unicode_literals
+
+import gpg
+import os.path
+import sys
+
+del absolute_import, division, unicode_literals
+
+# Copyright (C) 2018 Ben McGinnes <[email protected]>
+#
+# This program 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.
+#
+# This program 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 program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License and the GNU
+# Lesser General Public Licensefor more details.
+#
+# You should have received a copy of the GNU General Public License and the GNU
+# Lesser General Public along with this program; if not, see
+# <http://www.gnu.org/licenses/>.
+
+print("""
+This script imports one or more public keys from a single file.
+""")
+
+c = gpg.Context(armor=True)
+
+if len(sys.argv) >= 3:
+ keyfile = sys.argv[1]
+ homedir = sys.argv[2]
+elif len(sys.argv) == 2:
+ keyfile = sys.argv[1]
+ homedir = input("Enter the GPG configuration directory path (optional): ")
+else:
+ keyfile = input("Enter the path and filename to import the key(s) from: ")
+ homedir = input("Enter the GPG configuration directory path (optional): ")
+
+if homedir.startswith("~"):
+ if os.path.exists(os.path.expanduser(homedir)) is True:
+ c.home_dir = os.path.expanduser(homedir)
+ else:
+ pass
+elif os.path.exists(homedir) is True:
+ c.home_dir = homedir
+else:
+ pass
+
+if os.path.isfile(keyfile) is True:
+ with open(keyfile, "rb") as f:
+ incoming = f.read()
+ result = c.key_import(incoming)
+else:
+ result = None
+
+if result is not None and hasattr(result, "considered") is False:
+ print(result)
+elif result is not None and hasattr(result, "considered") is True:
+ num_keys = len(result.imports)
+ new_revs = result.new_revocations
+ new_sigs = result.new_signatures
+ new_subs = result.new_sub_keys
+ new_uids = result.new_user_ids
+ new_scrt = result.secret_imported
+ nochange = result.unchanged
+ print("""
+The total number of keys considered for import was: {0}
+
+ Number of keys revoked: {1}
+ Number of new signatures: {2}
+ Number of new subkeys: {3}
+ Number of new user IDs: {4}
+Number of new secret keys: {5}
+ Number of unchanged keys: {6}
+
+The key IDs for all considered keys were:
+""".format(num_keys, new_revs, new_sigs, new_subs, new_uids, new_scrt,
+ nochange))
+ for i in range(num_keys):
+ print(result.imports[i].fpr)
+ print("")
+elif result is None:
+ print("You must specify a key file to import.")
diff --git a/lang/python/examples/howto/import-keys.py b/lang/python/examples/howto/import-keys.py
new file mode 100755
index 00000000..bdc15a68
--- /dev/null
+++ b/lang/python/examples/howto/import-keys.py
@@ -0,0 +1,70 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from __future__ import absolute_import, division, unicode_literals
+
+import gpg
+import os.path
+import requests
+
+# Copyright (C) 2018 Ben McGinnes <[email protected]>
+#
+# This program 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.
+#
+# This program 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 program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License and the GNU
+# Lesser General Public Licensefor more details.
+#
+# You should have received a copy of the GNU General Public License and the GNU
+# Lesser General Public along with this program; if not, see
+# <http://www.gnu.org/licenses/>.
+
+print("""
+This script imports one or more public keys from the SKS keyservers.
+""")
+
+c = gpg.Context()
+url = "https://sks-keyservers.net/pks/lookup"
+pattern = input("Enter the pattern to search for key or user IDs: ")
+payload = {"op": "get", "search": pattern}
+
+r = requests.get(url, verify=True, params=payload)
+result = c.key_import(r.content)
+
+if result is not None and hasattr(result, "considered") is False:
+ print(result)
+elif result is not None and hasattr(result, "considered") is True:
+ num_keys = len(result.imports)
+ new_revs = result.new_revocations
+ new_sigs = result.new_signatures
+ new_subs = result.new_sub_keys
+ new_uids = result.new_user_ids
+ new_scrt = result.secret_imported
+ nochange = result.unchanged
+ print("""
+The total number of keys considered for import was: {0}
+
+ Number of keys revoked: {1}
+ Number of new signatures: {2}
+ Number of new subkeys: {3}
+ Number of new user IDs: {4}
+Number of new secret keys: {5}
+ Number of unchanged keys: {6}
+
+The key IDs for all considered keys were:
+""".format(num_keys, new_revs, new_sigs, new_subs, new_uids, new_scrt,
+ nochange))
+ for i in range(num_keys):
+ print(result.imports[i].fpr)
+ print("")
+else:
+ pass
diff --git a/lang/python/examples/howto/mutt-groups.py b/lang/python/examples/howto/mutt-groups.py
new file mode 100755
index 00000000..c0b515a7
--- /dev/null
+++ b/lang/python/examples/howto/mutt-groups.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from __future__ import absolute_import, division, unicode_literals
+
+# Copyright (C) 2018 Ben McGinnes <[email protected]>
+#
+# This program 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.
+#
+# This program 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 program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License and the GNU
+# Lesser General Public Licensefor more details.
+#
+# You should have received a copy of the GNU General Public License and the GNU
+# Lesser General Public along with this program; if not, see
+# <http://www.gnu.org/licenses/>.
+
+import sys
+from groups import group_lists
+
+"""
+Uses the groups module to generate Mutt crypt-hooks from gpg.conf.
+
+"""
+
+if len(sys.argv) >= 2:
+ hook_file = sys.argv[1]
+else:
+ hook_file = input("Enter the filename to save the crypt-hooks in: ")
+
+with open(hook_file, "w") as f:
+ f.write("""# Change settings based upon message recipient
+#
+# send-hook [!]<pattern> <command>
+#
+# <command> is executed when sending mail to an address matching <pattern>
+#
+# crypt-hook regexp key-id
+# The crypt-hook command provides a method by which you can
+# specify the ID of the public key to be used when encrypting
+# messages to a certain recipient. The meaning of "key ID" is to
+# be taken broadly: This can be a different e-mail address, a
+# numerical key ID, or even just an arbitrary search string. You
+# may use multiple crypt-hooks with the same regexp; multiple
+# matching crypt-hooks result in the use of multiple key-ids for a
+# recipient.
+""")
+
+for n in range(len(group_lists)):
+ rule = group_lists[n][0].replace(".", "\\\\.")
+ with open(hook_file, "a") as f:
+ f.write("\n")
+ f.write("# {0}\n".format(group_lists[n][0]))
+ for i in range(len(group_lists[n][1])):
+ f.write("crypt-hook {0} {1}\n".format(rule, group_lists[n][1][i]))
diff --git a/lang/python/examples/howto/pmkey-import-alt.py b/lang/python/examples/howto/pmkey-import-alt.py
new file mode 100755
index 00000000..e9521b7f
--- /dev/null
+++ b/lang/python/examples/howto/pmkey-import-alt.py
@@ -0,0 +1,132 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from __future__ import absolute_import, division, unicode_literals
+
+import gpg
+import os.path
+import requests
+import sys
+
+del absolute_import, division, unicode_literals
+
+# Copyright (C) 2018 Ben McGinnes <[email protected]>
+#
+# This program 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.
+#
+# This program 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 program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License and the GNU
+# Lesser General Public Licensefor more details.
+#
+# You should have received a copy of the GNU General Public License and the GNU
+# Lesser General Public along with this program; if not, see
+# <http://www.gnu.org/licenses/>.
+
+print("""
+This script searches the ProtonMail key server for the specified key and
+imports it. Optionally enables specifying a different GnuPG home directory.
+""")
+
+c = gpg.Context(armor=True)
+url = "https://api.protonmail.ch/pks/lookup"
+ksearch = []
+
+if len(sys.argv) >= 3:
+ keyterm = sys.argv[1]
+ homedir = sys.argv[2]
+elif len(sys.argv) == 2:
+ keyterm = sys.argv[1]
+ homedir = input("Enter the GPG configuration directory path (optional): ")
+else:
+ keyterm = input("Enter the key ID, UID or search string: ")
+ homedir = input("Enter the GPG configuration directory path (optional): ")
+
+if homedir.startswith("~"):
+ if os.path.exists(os.path.expanduser(homedir)) is True:
+ c.home_dir = os.path.expanduser(homedir)
+ else:
+ pass
+elif os.path.exists(homedir) is True:
+ c.home_dir = homedir
+else:
+ pass
+
+if keyterm.count("@") == 2 and keyterm.startswith("@") is True:
+ ksearch.append(keyterm[1:])
+ ksearch.append(keyterm[1:])
+ ksearch.append(keyterm[1:])
+elif keyterm.count("@") == 1 and keyterm.startswith("@") is True:
+ ksearch.append("{0}@protonmail.com".format(keyterm[1:]))
+ ksearch.append("{0}@protonmail.ch".format(keyterm[1:]))
+ ksearch.append("{0}@pm.me".format(keyterm[1:]))
+elif keyterm.count("@") == 0:
+ ksearch.append("{0}@protonmail.com".format(keyterm))
+ ksearch.append("{0}@protonmail.ch".format(keyterm))
+ ksearch.append("{0}@pm.me".format(keyterm))
+elif keyterm.count("@") == 2 and keyterm.startswith("@") is False:
+ uidlist = keyterm.split("@")
+ for uid in uidlist:
+ ksearch.append("{0}@protonmail.com".format(uid))
+ ksearch.append("{0}@protonmail.ch".format(uid))
+ ksearch.append("{0}@pm.me".format(uid))
+elif keyterm.count("@") > 2:
+ uidlist = keyterm.split("@")
+ for uid in uidlist:
+ ksearch.append("{0}@protonmail.com".format(uid))
+ ksearch.append("{0}@protonmail.ch".format(uid))
+ ksearch.append("{0}@pm.me".format(uid))
+else:
+ ksearch.append(keyterm)
+
+for k in ksearch:
+ payload = {"op": "get", "search": k}
+ try:
+ r = requests.get(url, verify=True, params=payload)
+ if r.ok is True:
+ result = c.key_import(r.content)
+ elif r.ok is False:
+ result = r.content
+ except Exception as e:
+ result = None
+
+ if result is not None and hasattr(result, "considered") is False:
+ print("{0} for {1}".format(result.decode(), k))
+ elif result is not None and hasattr(result, "considered") is True:
+ num_keys = len(result.imports)
+ new_revs = result.new_revocations
+ new_sigs = result.new_signatures
+ new_subs = result.new_sub_keys
+ new_uids = result.new_user_ids
+ new_scrt = result.secret_imported
+ nochange = result.unchanged
+ print("""
+The total number of keys considered for import was: {0}
+
+With UIDs wholely or partially matching the following string:
+
+ {1}
+
+ Number of keys revoked: {2}
+ Number of new signatures: {3}
+ Number of new subkeys: {4}
+ Number of new user IDs: {5}
+Number of new secret keys: {6}
+ Number of unchanged keys: {7}
+
+The key IDs for all considered keys were:
+""".format(num_keys, k, new_revs, new_sigs, new_subs, new_uids, new_scrt,
+ nochange))
+ for i in range(num_keys):
+ print(result.imports[i].fpr)
+ print("")
+ elif result is None:
+ print(e)
diff --git a/lang/python/examples/howto/pmkey-import.py b/lang/python/examples/howto/pmkey-import.py
new file mode 100755
index 00000000..edbd18e8
--- /dev/null
+++ b/lang/python/examples/howto/pmkey-import.py
@@ -0,0 +1,116 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from __future__ import absolute_import, division, unicode_literals
+
+import gpg
+import requests
+import sys
+
+del absolute_import, division, unicode_literals
+
+# Copyright (C) 2018 Ben McGinnes <[email protected]>
+#
+# This program 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.
+#
+# This program 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 program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License and the GNU
+# Lesser General Public Licensefor more details.
+#
+# You should have received a copy of the GNU General Public License and the GNU
+# Lesser General Public along with this program; if not, see
+# <http://www.gnu.org/licenses/>.
+
+print("""
+This script searches the ProtonMail key server for the specified key and
+imports it.
+""")
+
+c = gpg.Context(armor=True)
+url = "https://api.protonmail.ch/pks/lookup"
+ksearch = []
+
+if len(sys.argv) >= 2:
+ keyterm = sys.argv[1]
+else:
+ keyterm = input("Enter the key ID, UID or search string: ")
+
+if keyterm.count("@") == 2 and keyterm.startswith("@") is True:
+ ksearch.append(keyterm[1:])
+ ksearch.append(keyterm[1:])
+ ksearch.append(keyterm[1:])
+elif keyterm.count("@") == 1 and keyterm.startswith("@") is True:
+ ksearch.append("{0}@protonmail.com".format(keyterm[1:]))
+ ksearch.append("{0}@protonmail.ch".format(keyterm[1:]))
+ ksearch.append("{0}@pm.me".format(keyterm[1:]))
+elif keyterm.count("@") == 0:
+ ksearch.append("{0}@protonmail.com".format(keyterm))
+ ksearch.append("{0}@protonmail.ch".format(keyterm))
+ ksearch.append("{0}@pm.me".format(keyterm))
+elif keyterm.count("@") == 2 and keyterm.startswith("@") is False:
+ uidlist = keyterm.split("@")
+ for uid in uidlist:
+ ksearch.append("{0}@protonmail.com".format(uid))
+ ksearch.append("{0}@protonmail.ch".format(uid))
+ ksearch.append("{0}@pm.me".format(uid))
+elif keyterm.count("@") > 2:
+ uidlist = keyterm.split("@")
+ for uid in uidlist:
+ ksearch.append("{0}@protonmail.com".format(uid))
+ ksearch.append("{0}@protonmail.ch".format(uid))
+ ksearch.append("{0}@pm.me".format(uid))
+else:
+ ksearch.append(keyterm)
+
+for k in ksearch:
+ payload = {"op": "get", "search": k}
+ try:
+ r = requests.get(url, verify=True, params=payload)
+ if r.ok is True:
+ result = c.key_import(r.content)
+ elif r.ok is False:
+ result = r.content
+ except Exception as e:
+ result = None
+
+ if result is not None and hasattr(result, "considered") is False:
+ print("{0} for {1}".format(result.decode(), k))
+ elif result is not None and hasattr(result, "considered") is True:
+ num_keys = len(result.imports)
+ new_revs = result.new_revocations
+ new_sigs = result.new_signatures
+ new_subs = result.new_sub_keys
+ new_uids = result.new_user_ids
+ new_scrt = result.secret_imported
+ nochange = result.unchanged
+ print("""
+The total number of keys considered for import was: {0}
+
+With UIDs wholely or partially matching the following string:
+
+ {1}
+
+ Number of keys revoked: {2}
+ Number of new signatures: {3}
+ Number of new subkeys: {4}
+ Number of new user IDs: {5}
+Number of new secret keys: {6}
+ Number of unchanged keys: {7}
+
+The key IDs for all considered keys were:
+""".format(num_keys, k, new_revs, new_sigs, new_subs, new_uids, new_scrt,
+ nochange))
+ for i in range(num_keys):
+ print(result.imports[i].fpr)
+ print("")
+ elif result is None:
+ print(e)
diff --git a/lang/python/examples/howto/symcrypt-file.py b/lang/python/examples/howto/symcrypt-file.py
new file mode 100755
index 00000000..785a4d04
--- /dev/null
+++ b/lang/python/examples/howto/symcrypt-file.py
@@ -0,0 +1,63 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from __future__ import absolute_import, division, unicode_literals
+
+import gpg
+import sys
+
+# Copyright (C) 2018 Ben McGinnes <[email protected]>
+#
+# This program 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.
+#
+# This program 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 program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License and the GNU
+# Lesser General Public Licensefor more details.
+#
+# You should have received a copy of the GNU General Public License and the GNU
+# Lesser General Public along with this program; if not, see
+# <http://www.gnu.org/licenses/>.
+
+"""
+Symmetrically encrypts a file. Passphrase will be prompted for via Pinentry.
+
+Will produce both an ASCII armoured and GPG binary format copy of the encrypted
+file.
+"""
+
+if len(sys.argv) > 2:
+ filename = " ".join(sys.argv[1:])
+elif len(sys.argv) == 2:
+ filename = sys.argv[1]
+else:
+ filename = input("Enter the path and filename to encrypt: ")
+
+with open(filename, "rb") as f:
+ text = f.read()
+
+with gpg.Context(armor=True) as ca:
+ try:
+ ciphertext, result, sign_result = ca.encrypt(text, passphrase=None,
+ sign=False)
+ with open("{0}.asc".format(filename), "wb") as fa:
+ fa.write(ciphertext)
+ except gpg.errors.GPGMEError as e:
+ print(e)
+
+with gpg.Context() as cg:
+ try:
+ ciphertext, result, sign_result = cg.encrypt(text, passphrase=None,
+ sign=False)
+ with open("{0}.gpg".format(filename), "wb") as fg:
+ fg.write(ciphertext)
+ except gpg.errors.GPGMEError as e:
+ print(e)
diff --git a/lang/python/examples/howto/temp-homedir-config.py b/lang/python/examples/howto/temp-homedir-config.py
index ddd79327..1111fe21 100755
--- a/lang/python/examples/howto/temp-homedir-config.py
+++ b/lang/python/examples/howto/temp-homedir-config.py
@@ -3,6 +3,10 @@
from __future__ import absolute_import, division, unicode_literals
+import os
+import os.path
+import sys
+
# Copyright (C) 2018 Ben McGinnes <[email protected]>
#
# This program is free software; you can redistribute it and/or modify it under
@@ -24,10 +28,6 @@ from __future__ import absolute_import, division, unicode_literals
# Lesser General Public along with this program; if not, see
# <http://www.gnu.org/licenses/>.
-import os
-import os.path
-import sys
-
intro = """
This script creates a temporary directory to use as a homedir for
testing key generation tasks with the correct permissions, along
@@ -54,6 +54,13 @@ message telling you to specify a new directory name. There is no
default directory name.
"""
+ciphers256 = "TWOFISH CAMELLIA256 AES256"
+ciphers192 = "CAMELLIA192 AES192"
+ciphers128 = "CAMELLIA128 AES"
+ciphersBad = "BLOWFISH IDEA CAST5 3DES"
+digests = "SHA512 SHA384 SHA256 SHA224 RIPEMD160 SHA1"
+compress = "ZLIB BZIP2 ZIP Uncompressed"
+
gpgconf = """# gpg.conf settings for key generation:
expert
allow-freeform-uid
@@ -63,11 +70,11 @@ tofu-default-policy unknown
enable-large-rsa
enable-dsa2
cert-digest-algo SHA512
-default-preference-list TWOFISH CAMELLIA256 AES256 CAMELLIA192 AES192 CAMELLIA128 AES BLOWFISH IDEA CAST5 3DES SHA512 SHA384 SHA256 SHA224 RIPEMD160 SHA1 ZLIB BZIP2 ZIP Uncompressed
-personal-cipher-preferences TWOFISH CAMELLIA256 AES256 CAMELLIA192 AES192 CAMELLIA128 AES BLOWFISH IDEA CAST5 3DES
-personal-digest-preferences SHA512 SHA384 SHA256 SHA224 RIPEMD160 SHA1
-personal-compress-preferences ZLIB BZIP2 ZIP Uncompressed
-"""
+default-preference-list {0} {1} {2} {3} {4} {5}
+personal-cipher-preferences {0} {1} {2} {3}
+personal-digest-preferences {4}
+personal-compress-preferences {5}
+""".format(ciphers256, ciphers192, ciphers128, ciphersBad, digests, compress)
agentconf = """# gpg-agent.conf settings for key generation:
default-cache-ttl 300
@@ -84,17 +91,17 @@ else:
userdir = os.path.expanduser("~")
if new_homedir.startswith("~"):
- new_homdir.replace("~", "")
+ new_homedir.replace("~", "")
else:
pass
if new_homedir.startswith("/"):
- new_homdir.replace("/", "")
+ new_homedir.replace("/", "")
else:
pass
if new_homedir.startswith("."):
- new_homdir.replace(".", "_")
+ new_homedir.replace(".", "_")
else:
pass
diff --git a/lang/python/examples/inter-edit.py b/lang/python/examples/inter-edit.py
index ed0d8c42..5b58c97b 100644
--- a/lang/python/examples/inter-edit.py
+++ b/lang/python/examples/inter-edit.py
@@ -15,15 +15,15 @@
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
-
"""Simple interactive editor to test editor scripts"""
from __future__ import absolute_import, print_function, unicode_literals
-del absolute_import, print_function, unicode_literals
import sys
import gpg
+del absolute_import, print_function, unicode_literals
+
if len(sys.argv) != 2:
sys.exit("Usage: %s <Gpg key pattern>\n" % sys.argv[0])
@@ -40,10 +40,12 @@ with gpg.Context() as c:
print("Editing key {} ({}):".format(key.uids[0].uid, key.subkeys[0].fpr))
def edit_fnc(keyword, args):
- print("Status: {}, args: {} > ".format(
- keyword, args), end='', flush=True)
+ print(
+ "Status: {}, args: {} > ".format(keyword, args),
+ end='',
+ flush=True)
- if not 'GET' in keyword:
+ if 'GET' not in keyword:
# no prompt
print()
return None
diff --git a/lang/python/examples/low_level-encrypt_to_all.py b/lang/python/examples/low_level-encrypt_to_all.py
index bad4220c..5c10d3df 100755
--- a/lang/python/examples/low_level-encrypt_to_all.py
+++ b/lang/python/examples/low_level-encrypt_to_all.py
@@ -16,18 +16,18 @@
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
-
"""
This program will try to encrypt a simple message to each key on your
keyring. If your keyring has any invalid keys on it, those keys will
be skipped and it will re-try the encryption."""
from __future__ import absolute_import, print_function, unicode_literals
-del absolute_import, print_function, unicode_literals
import sys
import gpg
+del absolute_import, print_function, unicode_literals
+
with gpg.Context(armor=True) as c:
recipients = list()
for key in c.keylist():
@@ -40,14 +40,15 @@ with gpg.Context(armor=True) as c:
while not ciphertext:
print("Encrypting to %d recipients" % len(recipients))
try:
- ciphertext, _, _ = c.encrypt(b'This is my message.',
- recipients=recipients)
+ ciphertext, _, _ = c.encrypt(
+ b'This is my message.', recipients=recipients)
except gpg.errors.InvalidRecipients as e:
print("Encryption failed for these keys:\n{0!s}".format(e))
# filter out the bad keys
bad_keys = {bad.fpr for bad in e.recipients}
- recipients = [r for r in recipients
- if not r.subkeys[0].fpr in bad_keys]
+ recipients = [
+ r for r in recipients if not r.subkeys[0].fpr in bad_keys
+ ]
sys.stdout.buffer.write(ciphertext)
diff --git a/lang/python/examples/sign.py b/lang/python/examples/sign.py
index 16c2256a..5b90b4b8 100755
--- a/lang/python/examples/sign.py
+++ b/lang/python/examples/sign.py
@@ -17,12 +17,13 @@
# 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 sys
import gpg
from gpg.constants.sig import mode
+del absolute_import, print_function, unicode_literals
+
with gpg.Context() as c:
signed, _ = c.sign(b"Test message", mode=mode.CLEAR)
sys.stdout.buffer.write(signed)
diff --git a/lang/python/examples/signverify.py b/lang/python/examples/signverify.py
index 5870ca95..2df72758 100755
--- a/lang/python/examples/signverify.py
+++ b/lang/python/examples/signverify.py
@@ -20,12 +20,13 @@
# It uses keys for [email protected] generated by genkey.py script
from __future__ import absolute_import, print_function, unicode_literals
-del absolute_import, print_function, unicode_literals
import sys
import gpg
from gpg.constants.sig import mode
+del absolute_import, print_function, unicode_literals
+
user = "joe+gpg"
with gpg.Context(pinentry_mode=gpg.constants.PINENTRY_MODE_LOOPBACK) as c:
diff --git a/lang/python/examples/simple.py b/lang/python/examples/simple.py
index 8f451d7c..17c3eba0 100755
--- a/lang/python/examples/simple.py
+++ b/lang/python/examples/simple.py
@@ -18,11 +18,12 @@
# 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 sys
import gpg
+del absolute_import, print_function, unicode_literals
+
with gpg.Context(armor=True) as c:
recipients = []
print("Enter name of your recipient(s), end with a blank line.")
@@ -40,8 +41,8 @@ with gpg.Context(armor=True) as c:
if not recipients:
sys.exit("No recipients.")
- print("Encrypting for {}.".format(", ".join(k.uids[0].name
- for k in recipients)))
+ print("Encrypting for {}.".format(", ".join(
+ k.uids[0].name for k in recipients)))
ciphertext, _, _ = c.encrypt(b"This is my message,", recipients)
sys.stdout.buffer.write(ciphertext)
diff --git a/lang/python/examples/testCMSgetkey.py b/lang/python/examples/testCMSgetkey.py
index d4c08840..f1cdb2ce 100644
--- a/lang/python/examples/testCMSgetkey.py
+++ b/lang/python/examples/testCMSgetkey.py
@@ -15,15 +15,15 @@
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
-
"""A test applicaton for the CMS protocol."""
from __future__ import absolute_import, print_function, unicode_literals
-del absolute_import, print_function, unicode_literals
import sys
import gpg
+del absolute_import, print_function, unicode_literals
+
if len(sys.argv) != 2:
sys.exit("fingerprint or unique key ID for gpgme_get_key()")
diff --git a/lang/python/examples/verifydetails.py b/lang/python/examples/verifydetails.py
index b3ca1339..dc0e7d38 100755
--- a/lang/python/examples/verifydetails.py
+++ b/lang/python/examples/verifydetails.py
@@ -18,11 +18,13 @@
# 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 sys
import gpg
+del absolute_import, print_function, unicode_literals
+
+
def print_engine_infos():
print("gpgme version:", gpg.core.check_version(None))
print("engines:")
@@ -31,8 +33,9 @@ def print_engine_infos():
print(engine.file_name, engine.version)
for proto in [gpg.constants.protocol.OpenPGP, gpg.constants.protocol.CMS]:
- print("Have {}? {}".format(gpg.core.get_protocol_name(proto),
- gpg.core.engine_check_version(proto)))
+ print("Have {}? {}".format(
+ gpg.core.get_protocol_name(proto),
+ gpg.core.engine_check_version(proto)))
def verifyprintdetails(filename, detached_sig_filename=None):
@@ -40,9 +43,9 @@ def verifyprintdetails(filename, detached_sig_filename=None):
with gpg.Context() as c:
# Verify.
- data, result = c.verify(open(filename),
- open(detached_sig_filename)
- if detached_sig_filename else None)
+ data, result = c.verify(
+ open(filename),
+ open(detached_sig_filename) if detached_sig_filename else None)
# List results for all signatures. Status equal 0 means "Ok".
for index, sign in enumerate(result.signatures):
@@ -57,15 +60,15 @@ def verifyprintdetails(filename, detached_sig_filename=None):
if data:
sys.stdout.buffer.write(data)
+
def main():
print_engine_infos()
print()
argc = len(sys.argv)
if argc < 2 or argc > 3:
- sys.exit(
- "Usage: {} <filename>[ <detached_signature_filename>]".format(
- sys.argv[0]))
+ sys.exit("Usage: {} <filename>[ <detached_signature_filename>]".format(
+ sys.argv[0]))
if argc == 2:
print("trying to verify file {}.".format(sys.argv[1]))
@@ -74,5 +77,6 @@ def main():
print("trying to verify signature {1} for file {0}.".format(*sys.argv))
verifyprintdetails(sys.argv[1], sys.argv[2])
+
if __name__ == "__main__":
main()
diff --git a/lang/python/setup.py.in b/lang/python/setup.py.in
index 2595073f..65a4be05 100755
--- a/lang/python/setup.py.in
+++ b/lang/python/setup.py.in
@@ -1,6 +1,6 @@
#!/usr/bin/env python
-# Copyright (C) 2016-2017 g10 Code GmbH
+# Copyright (C) 2016-2018 g10 Code GmbH
# Copyright (C) 2004,2008 Igor Belyi <[email protected]>
# Copyright (C) 2002 John Goerzen <[email protected]>
#
@@ -19,11 +19,15 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
from distutils.core import setup, Extension
-import os, os.path, sys
+from distutils.command.build import build
+
import glob
+import os
+import os.path
import re
import shutil
import subprocess
+import sys
# Out-of-tree build of the gpg bindings.
gpg_error_config = ["gpg-error-config"]
@@ -40,9 +44,11 @@ top_builddir = os.environ.get("top_builddir")
if top_builddir:
# In-tree build.
in_tree = True
- gpgme_config = [os.path.join(top_builddir, "src/gpgme-config")] + gpgme_config_flags
+ gpgme_config = [os.path.join(top_builddir, "src/gpgme-config")
+ ] + gpgme_config_flags
gpgme_h = os.path.join(top_builddir, "src/gpgme.h")
- library_dirs = [os.path.join(top_builddir, "src/.libs")] # XXX uses libtool internals
+ library_dirs = [os.path.join(top_builddir,
+ "src/.libs")] # XXX uses libtool internals
extra_macros.update(
HAVE_CONFIG_H=1,
HAVE_DATA_H=1,
@@ -55,17 +61,18 @@ else:
devnull = open(os.devnull, "w")
try:
- subprocess.check_call(gpgme_config + ['--version'],
- stdout=devnull)
+ subprocess.check_call(gpgme_config + ['--version'], stdout=devnull)
except:
sys.exit("Could not find gpgme-config. " +
"Please install the libgpgme development package.")
+
def getconfig(what, config=gpgme_config):
- confdata = subprocess.Popen(config + ["--%s" % what],
- stdout=subprocess.PIPE).communicate()[0]
+ confdata = subprocess.Popen(
+ config + ["--%s" % what], stdout=subprocess.PIPE).communicate()[0]
return [x for x in confdata.decode('utf-8').split() if x != '']
+
version = version_raw = getconfig("version")[0]
if '-' in version:
version = version.split('-')[0]
@@ -90,7 +97,7 @@ for item in getconfig('cflags'):
include_dirs.append(item[2:])
elif item.startswith("-D"):
defitem = item[2:].split("=", 1)
- if len(defitem)==2:
+ if len(defitem) == 2:
define_macros.append((defitem[0], defitem[1]))
else:
define_macros.append((defitem[0], None))
@@ -98,49 +105,67 @@ for item in getconfig('cflags'):
# Adjust include and library locations in case of win32
uname_s = os.popen("uname -s").read()
if uname_s.startswith("MINGW32"):
- mnts = [x.split()[0:3:2] for x in os.popen("mount").read().split("\n") if x]
- tmplist = sorted([(len(x[1]), x[1], x[0]) for x in mnts])
- tmplist.reverse()
- extra_dirs = []
- for item in include_dirs:
- for ln, mnt, tgt in tmplist:
- if item.startswith(mnt):
- item = os.path.normpath(item[ln:])
- while item[0] == os.path.sep:
- item = item[1:]
- extra_dirs.append(os.path.join(tgt, item))
- break
- include_dirs += extra_dirs
- for item in [x[2:] for x in libs if x.startswith("-L")]:
- for ln, mnt, tgt in tmplist:
- if item.startswith(mnt):
- item = os.path.normpath(item[ln:])
- while item[0] == os.path.sep:
- item = item[1:]
- library_dirs.append(os.path.join(tgt, item))
- break
+ mnts = [
+ x.split()[0:3:2] for x in os.popen("mount").read().split("\n") if x
+ ]
+ tmplist = sorted([(len(x[1]), x[1], x[0]) for x in mnts])
+ tmplist.reverse()
+ extra_dirs = []
+ for item in include_dirs:
+ for ln, mnt, tgt in tmplist:
+ if item.startswith(mnt):
+ item = os.path.normpath(item[ln:])
+ while item[0] == os.path.sep:
+ item = item[1:]
+ extra_dirs.append(os.path.join(tgt, item))
+ break
+ include_dirs += extra_dirs
+ for item in [x[2:] for x in libs if x.startswith("-L")]:
+ for ln, mnt, tgt in tmplist:
+ if item.startswith(mnt):
+ item = os.path.normpath(item[ln:])
+ while item[0] == os.path.sep:
+ item = item[1:]
+ library_dirs.append(os.path.join(tgt, item))
+ break
+
def in_srcdir(name):
return os.path.join(os.environ.get("srcdir", ""), name)
+
+
def up_to_date(source, target):
- return (os.path.exists(target)
- and os.path.getmtime(source) <= os.path.getmtime(target))
+ return (os.path.exists(target) and
+ os.path.getmtime(source) <= os.path.getmtime(target))
+
# We build an Extension using SWIG, which generates a Python module.
# By default, the 'build_py' step is run before 'build_ext', and
# therefore the generated Python module is not copied into the build
# directory.
-# Bug: http://bugs.python.org/issue1016626
+# Bugs: https://bugs.python.org/issue1016626
+# https://bugs.python.org/issue2624
# Workaround:
-# http://stackoverflow.com/questions/12491328/python-distutils-not-include-the-swig-generated-module
-from distutils.command.build import build
-class BuildExtFirstHack(build):
+# https://stackoverflow.com/questions/12491328/python-distutils-not-include-the-swig-generated-module
+#
+# To install to multiple Python installations or to alternate ones run the
+# following three commands (yes, run the build one twice):
+#
+# /path/to/pythonX.Y setup.py build
+# /path/to/pythonX.Y setup.py build
+# /path/to/pythonX.Y setup.py install
+#
+# It is highly likely that this will need to be run as root or with sudo (or
+# sudo -H). It may or may not work with venv. and outside a virtualenv
+class BuildExtFirstHack(build):
def _read_header(self, header, cflags):
tmp_include = self._in_build_base("include1.h")
with open(tmp_include, 'w') as f:
f.write("#include <%s>" % header)
- return subprocess.check_output(os.environ.get('CPP', 'cc -E').split() + cflags + [tmp_include]).decode('utf-8')
+ return subprocess.check_output(
+ os.environ.get('CPP', 'cc -E').split() + cflags +
+ [tmp_include]).decode('utf-8')
def _write_if_unchanged(self, target, content):
if os.path.exists(target):
@@ -158,13 +183,14 @@ class BuildExtFirstHack(build):
def _generate_errors_i(self):
try:
- subprocess.check_call(gpg_error_config + ['--version'],
- stdout=devnull)
+ subprocess.check_call(
+ gpg_error_config + ['--version'], stdout=devnull)
except:
sys.exit("Could not find gpg-error-config. " +
"Please install the libgpg-error development package.")
- gpg_error_content = self._read_header("gpg-error.h", getconfig("cflags", config=gpg_error_config))
+ gpg_error_content = self._read_header(
+ "gpg-error.h", getconfig("cflags", config=gpg_error_config))
filter_re = re.compile(r'GPG_ERR_[^ ]* =')
rewrite_re = re.compile(r' *(.*) = .*')
@@ -173,9 +199,11 @@ class BuildExtFirstHack(build):
for line in gpg_error_content.splitlines():
if not filter_re.search(line):
continue
- errors_i_content += rewrite_re.sub(r'%constant long \1 = \1;'+'\n', line.strip())
+ errors_i_content += rewrite_re.sub(
+ r'%constant long \1 = \1;' + '\n', line.strip())
- self._write_if_unchanged(self._in_build_base("errors.i"), errors_i_content)
+ self._write_if_unchanged(
+ self._in_build_base("errors.i"), errors_i_content)
def _in_build_base(self, name):
return os.path.join(self.build_base, name)
@@ -191,7 +219,8 @@ class BuildExtFirstHack(build):
# Copy due to http://bugs.python.org/issue2624
# Avoid creating in srcdir
for source, target in ((in_srcdir(n), self._in_build_base(n))
- for n in ('gpgme.i', 'helpers.c', 'private.h', 'helpers.h')):
+ for n in ('gpgme.i', 'helpers.c', 'private.h',
+ 'helpers.h')):
if not up_to_date(source, target):
shutil.copy2(source, target)
@@ -203,52 +232,60 @@ class BuildExtFirstHack(build):
def run(self):
self._generate()
- swig_sources.extend((self._in_build_base('gpgme.i'), self._in_build_base('helpers.c')))
- swig_opts.extend(['-I' + self.build_base,
- '-outdir', os.path.join(self.build_lib, 'gpg')])
+ swig_sources.extend((self._in_build_base('gpgme.i'),
+ self._in_build_base('helpers.c')))
+ swig_opts.extend([
+ '-I' + self.build_base, '-outdir',
+ os.path.join(self.build_lib, 'gpg')
+ ])
include_dirs.insert(0, self.build_base)
self.run_command('build_ext')
build.run(self)
+
py3 = [] if sys.version_info.major < 3 else ['-py3']
swig_sources = []
swig_opts = ['-threads'] + py3 + extra_swig_opts
-swige = Extension("gpg._gpgme",
- sources = swig_sources,
- swig_opts = swig_opts,
- include_dirs = include_dirs,
- define_macros = define_macros,
- library_dirs = library_dirs,
- extra_link_args = libs)
-
-setup(name="gpg",
- cmdclass={'build': BuildExtFirstHack},
- version="@VERSION@",
- description='Python bindings for GPGME GnuPG cryptography library',
- # XXX add a long description
- #long_description=long_description,
- author='The GnuPG hackers',
- author_email='[email protected]',
- url='https://www.gnupg.org',
- ext_modules=[swige],
- packages = ['gpg', 'gpg.constants', 'gpg.constants.data',
- 'gpg.constants.keylist', 'gpg.constants.sig',
- 'gpg.constants.tofu'],
- license="LGPL2.1+ (the library), GPL2+ (tests and examples)",
- classifiers=[
- 'Development Status :: 4 - Beta',
- 'Intended Audience :: Developers',
- 'License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+)',
- 'Programming Language :: Python :: 2',
- 'Programming Language :: Python :: 2.7',
- 'Programming Language :: Python :: 3',
- 'Programming Language :: Python :: 3.4',
- 'Programming Language :: Python :: 3.5',
- 'Programming Language :: Python :: 3.6',
- 'Operating System :: POSIX',
- 'Operating System :: Microsoft :: Windows',
- 'Topic :: Communications :: Email',
- 'Topic :: Security :: Cryptography',
- ],
+swige = Extension(
+ "gpg._gpgme",
+ sources=swig_sources,
+ swig_opts=swig_opts,
+ include_dirs=include_dirs,
+ define_macros=define_macros,
+ library_dirs=library_dirs,
+ extra_link_args=libs)
+
+setup(
+ name="gpg",
+ cmdclass={'build': BuildExtFirstHack},
+ version="@VERSION@",
+ description='Python bindings for GPGME GnuPG cryptography library',
+ # TODO: add a long description
+ # long_description=long_description,
+ author='The GnuPG hackers',
+ author_email='[email protected]',
+ url='https://www.gnupg.org',
+ ext_modules=[swige],
+ packages=[
+ 'gpg', 'gpg.constants', 'gpg.constants.data', 'gpg.constants.keylist',
+ 'gpg.constants.sig', 'gpg.constants.tofu'
+ ],
+ license="LGPL2.1+ (the library), GPL2+ (tests and examples)",
+ classifiers=[
+ 'Development Status :: 4 - Beta',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+)',
+ 'Programming Language :: Python :: 2',
+ 'Programming Language :: Python :: 2.7',
+ 'Programming Language :: Python :: 3',
+ 'Programming Language :: Python :: 3.4',
+ 'Programming Language :: Python :: 3.5',
+ 'Programming Language :: Python :: 3.6',
+ 'Programming Language :: Python :: 3.7',
+ 'Operating System :: POSIX',
+ 'Operating System :: Microsoft :: Windows',
+ 'Topic :: Communications :: Email',
+ 'Topic :: Security :: Cryptography',
+ ],
)
diff --git a/lang/python/src/__init__.py b/lang/python/src/__init__.py
index 385b17e3..30e638c4 100644
--- a/lang/python/src/__init__.py
+++ b/lang/python/src/__init__.py
@@ -15,7 +15,6 @@
# 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.
@@ -96,7 +95,6 @@ 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
@@ -107,6 +105,8 @@ from . import version
from .core import Context
from .core import Data
+del absolute_import, print_function, unicode_literals
+
# Interface hygiene.
# Drop the low-level gpgme that creeps in for some reason.
@@ -117,5 +117,7 @@ del gpgme
_ = [Context, Data, core, errors, constants, util, callbacks, version]
del _
-__all__ = ["Context", "Data",
- "core", "errors", "constants", "util", "callbacks", "version"]
+__all__ = [
+ "Context", "Data", "core", "errors", "constants", "util", "callbacks",
+ "version"
+]
diff --git a/lang/python/src/callbacks.py b/lang/python/src/callbacks.py
index b25a9a74..9aacf566 100644
--- a/lang/python/src/callbacks.py
+++ b/lang/python/src/callbacks.py
@@ -16,26 +16,30 @@
# 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
+del absolute_import, print_function, unicode_literals
+
+
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:
+ if hook is not 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" %\
+ 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:
diff --git a/lang/python/src/constants/__init__.py b/lang/python/src/constants/__init__.py
index 484ffd29..7a953aab 100644
--- a/lang/python/src/constants/__init__.py
+++ b/lang/python/src/constants/__init__.py
@@ -18,23 +18,29 @@
# 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
+# Globals may need to be set prior to module import, if so this prevents PEP8
+# compliance, but better that than code breakage.
util.process_constants('GPGME_', globals())
-del util
# For convenience, we import the modules here.
-from . import data, keylist, sig, tofu # The subdirs.
-from . import create, event, keysign, md, pk, protocol, sigsum, status, validity
+from . import data, keylist, sig, tofu # The subdirs.
+# The remaining modules can no longer fit on one line.
+from . import create, event, keysign, md, pk, protocol, sigsum, status
+from . import validity
+
+del absolute_import, print_function, unicode_literals, util
# A complication arises because 'import' is a reserved keyword.
# Import it as 'Import' instead.
-globals()['Import'] = getattr(__import__('', globals(), locals(),
- [str('import')], 1), "import")
+globals()['Import'] = getattr(
+ __import__('', globals(), locals(), [str('import')], 1), "import")
-__all__ = ['data', 'event', 'import', 'keysign', 'keylist', 'md', 'pk',
- 'protocol', 'sig', 'sigsum', 'status', 'tofu', 'validity', 'create']
+__all__ = [
+ 'data', 'event', 'import', 'keysign', 'keylist', 'md', 'pk', '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/src/constants/create.py b/lang/python/src/constants/create.py
index 132e96d4..382dad92 100644
--- a/lang/python/src/constants/create.py
+++ b/lang/python/src/constants/create.py
@@ -18,8 +18,7 @@
# 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_CREATE_', globals())
-del util
+del absolute_import, print_function, unicode_literals, util
diff --git a/lang/python/src/constants/data/__init__.py b/lang/python/src/constants/data/__init__.py
index 8274ab91..c0856679 100644
--- a/lang/python/src/constants/data/__init__.py
+++ b/lang/python/src/constants/data/__init__.py
@@ -1,6 +1,6 @@
-
from __future__ import absolute_import, print_function, unicode_literals
-del absolute_import, print_function, unicode_literals
from . import encoding
__all__ = ['encoding']
+
+del absolute_import, print_function, unicode_literals
diff --git a/lang/python/src/constants/data/encoding.py b/lang/python/src/constants/data/encoding.py
index e76a22ee..9afa7323 100644
--- a/lang/python/src/constants/data/encoding.py
+++ b/lang/python/src/constants/data/encoding.py
@@ -16,8 +16,7 @@
# 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
+del absolute_import, print_function, unicode_literals, util
diff --git a/lang/python/src/constants/event.py b/lang/python/src/constants/event.py
index 1b14d1d1..9f9273da 100644
--- a/lang/python/src/constants/event.py
+++ b/lang/python/src/constants/event.py
@@ -16,8 +16,7 @@
# 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
+del absolute_import, print_function, unicode_literals, util
diff --git a/lang/python/src/constants/import.py b/lang/python/src/constants/import.py
index 47c296cb..e477eb25 100644
--- a/lang/python/src/constants/import.py
+++ b/lang/python/src/constants/import.py
@@ -16,8 +16,7 @@
# 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
+del absolute_import, print_function, unicode_literals, util
diff --git a/lang/python/src/constants/keylist/__init__.py b/lang/python/src/constants/keylist/__init__.py
index 2ce0edfd..fa8f7f0b 100644
--- a/lang/python/src/constants/keylist/__init__.py
+++ b/lang/python/src/constants/keylist/__init__.py
@@ -1,6 +1,6 @@
-
from __future__ import absolute_import, print_function, unicode_literals
-del absolute_import, print_function, unicode_literals
from . import mode
__all__ = ['mode']
+
+del absolute_import, print_function, unicode_literals
diff --git a/lang/python/src/constants/keylist/mode.py b/lang/python/src/constants/keylist/mode.py
index 39e1819d..bda7710e 100644
--- a/lang/python/src/constants/keylist/mode.py
+++ b/lang/python/src/constants/keylist/mode.py
@@ -16,8 +16,7 @@
# 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
+del absolute_import, print_function, unicode_literals, util
diff --git a/lang/python/src/constants/keysign.py b/lang/python/src/constants/keysign.py
index fccdbc42..328dfb91 100644
--- a/lang/python/src/constants/keysign.py
+++ b/lang/python/src/constants/keysign.py
@@ -18,8 +18,7 @@
# 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
+del absolute_import, print_function, unicode_literals, util
diff --git a/lang/python/src/constants/md.py b/lang/python/src/constants/md.py
index f3e8bbdb..068b31d2 100644
--- a/lang/python/src/constants/md.py
+++ b/lang/python/src/constants/md.py
@@ -16,8 +16,7 @@
# 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
+del absolute_import, print_function, unicode_literals, util
diff --git a/lang/python/src/constants/pk.py b/lang/python/src/constants/pk.py
index 6bf2a215..3a826d12 100644
--- a/lang/python/src/constants/pk.py
+++ b/lang/python/src/constants/pk.py
@@ -16,8 +16,7 @@
# 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
+del absolute_import, print_function, unicode_literals, util
diff --git a/lang/python/src/constants/protocol.py b/lang/python/src/constants/protocol.py
index d086bbde..cc9ca079 100644
--- a/lang/python/src/constants/protocol.py
+++ b/lang/python/src/constants/protocol.py
@@ -16,8 +16,7 @@
# 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
+del absolute_import, print_function, unicode_literals, util
diff --git a/lang/python/src/constants/sig/__init__.py b/lang/python/src/constants/sig/__init__.py
index 39d4e6e1..f45af004 100644
--- a/lang/python/src/constants/sig/__init__.py
+++ b/lang/python/src/constants/sig/__init__.py
@@ -1,6 +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']
+
+del absolute_import, print_function, unicode_literals
diff --git a/lang/python/src/constants/sig/mode.py b/lang/python/src/constants/sig/mode.py
index 0f4f0efc..3a2d17a3 100644
--- a/lang/python/src/constants/sig/mode.py
+++ b/lang/python/src/constants/sig/mode.py
@@ -16,8 +16,7 @@
# 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
+del absolute_import, print_function, unicode_literals, util
diff --git a/lang/python/src/constants/sig/notation.py b/lang/python/src/constants/sig/notation.py
index 9a79e014..9e56be39 100644
--- a/lang/python/src/constants/sig/notation.py
+++ b/lang/python/src/constants/sig/notation.py
@@ -18,8 +18,7 @@
# 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
+del absolute_import, print_function, unicode_literals, util
diff --git a/lang/python/src/constants/sigsum.py b/lang/python/src/constants/sigsum.py
index 09ef9d78..0fe0e77e 100644
--- a/lang/python/src/constants/sigsum.py
+++ b/lang/python/src/constants/sigsum.py
@@ -16,8 +16,7 @@
# 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
+del absolute_import, print_function, unicode_literals, util
diff --git a/lang/python/src/constants/tofu/__init__.py b/lang/python/src/constants/tofu/__init__.py
index 819a58bb..5e58a6a8 100644
--- a/lang/python/src/constants/tofu/__init__.py
+++ b/lang/python/src/constants/tofu/__init__.py
@@ -18,7 +18,8 @@
# 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']
+
+del absolute_import, print_function, unicode_literals
diff --git a/lang/python/src/constants/tofu/policy.py b/lang/python/src/constants/tofu/policy.py
index 5a61f067..53d853de 100644
--- a/lang/python/src/constants/tofu/policy.py
+++ b/lang/python/src/constants/tofu/policy.py
@@ -18,8 +18,7 @@
# 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
+del absolute_import, print_function, unicode_literals, util
diff --git a/lang/python/src/constants/validity.py b/lang/python/src/constants/validity.py
index d3c53458..4ecf4ec3 100644
--- a/lang/python/src/constants/validity.py
+++ b/lang/python/src/constants/validity.py
@@ -16,8 +16,7 @@
# 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
+del absolute_import, print_function, unicode_literals, util
diff --git a/lang/python/src/core.py b/lang/python/src/core.py
index bd95d231..d4711317 100644
--- a/lang/python/src/core.py
+++ b/lang/python/src/core.py
@@ -1,5 +1,22 @@
-# Copyright (C) 2016-2017 g10 Code GmbH
-# Copyright (C) 2004,2008 Igor Belyi <[email protected]>
+# -*- coding: utf-8 -*-
+
+from __future__ import 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
+
+del absolute_import, print_function, unicode_literals
+
+# Copyright (C) 2016-2018 g10 Code GmbH
+# Copyright (C) 2004, 2008 Igor Belyi <[email protected]>
# Copyright (C) 2002 John Goerzen <[email protected]>
#
# This library is free software; you can redistribute it and/or
@@ -15,7 +32,6 @@
# 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.
@@ -24,18 +40,6 @@ 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
@@ -49,8 +53,8 @@ class GpgmeWrapper(object):
self.wrapped = wrapped
def __repr__(self):
- return '<{}/{!r}>'.format(super(GpgmeWrapper, self).__repr__(),
- self.wrapped)
+ return '<{}/{!r}>'.format(
+ super(GpgmeWrapper, self).__repr__(), self.wrapped)
def __str__(self):
acc = ['{}.{}'.format(__name__, self.__class__.__name__)]
@@ -64,7 +68,7 @@ class GpgmeWrapper(object):
return hash(repr(self.wrapped))
def __eq__(self, other):
- if other == None:
+ if other is None:
return False
else:
return repr(self.wrapped) == repr(other.wrapped)
@@ -98,12 +102,12 @@ class GpgmeWrapper(object):
_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))
+ 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))
@@ -116,9 +120,10 @@ class GpgmeWrapper(object):
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:
+ if key[0] == '_' or self._cprefix is None:
return None
if key in self._boolean_properties:
@@ -128,12 +133,14 @@ class GpgmeWrapper(object):
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, name)
else:
+
def _funcwrap(slf, *args):
result = func(slf.wrapped, *args)
if slf._callback_excinfo:
@@ -149,6 +156,7 @@ class GpgmeWrapper(object):
# Bind the method to 'self'.
def wrapper(*args):
return _funcwrap(self, *args)
+
wrapper.__doc__ = doc
return wrapper
@@ -160,6 +168,7 @@ class GpgmeWrapper(object):
else:
super(GpgmeWrapper, self).__setattr__(key, value)
+
class Context(GpgmeWrapper):
"""Context for cryptographic operations
@@ -173,10 +182,15 @@ class Context(GpgmeWrapper):
"""
- def __init__(self, armor=False, textmode=False, offline=False,
- signers=[], pinentry_mode=constants.PINENTRY_MODE_DEFAULT,
+ def __init__(self,
+ armor=False,
+ textmode=False,
+ offline=False,
+ signers=[],
+ pinentry_mode=constants.PINENTRY_MODE_DEFAULT,
protocol=constants.PROTOCOL_OpenPGP,
- wrapped=None, home_dir=None):
+ wrapped=None,
+ home_dir=None):
"""Construct a context object
Keyword arguments:
@@ -212,22 +226,29 @@ class Context(GpgmeWrapper):
Helper function to retrieve the results of an operation, or
None if SINK is given.
"""
- if sink or data == None:
+ if sink or data is None:
return None
data.seek(0, os.SEEK_SET)
return data.read()
def __repr__(self):
- return (
- "Context(armor={0.armor}, "
- "textmode={0.textmode}, offline={0.offline}, "
- "signers={0.signers}, pinentry_mode={0.pinentry_mode}, "
- "protocol={0.protocol}, home_dir={0.home_dir}"
- ")").format(self)
-
- 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):
+ return ("Context(armor={0.armor}, "
+ "textmode={0.textmode}, offline={0.offline}, "
+ "signers={0.signers}, pinentry_mode={0.pinentry_mode}, "
+ "protocol={0.protocol}, home_dir={0.home_dir}"
+ ")").format(self)
+
+ 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
@@ -267,12 +288,14 @@ class Context(GpgmeWrapper):
flags |= expect_sign * constants.ENCRYPT_EXPECT_SIGN
flags |= (not compress) * constants.ENCRYPT_NO_COMPRESS
- if passphrase != None:
+ if passphrase is not 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:
@@ -283,25 +306,26 @@ class Context(GpgmeWrapper):
except errors.GPGMEError as e:
result = self.op_encrypt_result()
sig_result = self.op_sign_result() if sign else None
- results = (self.__read__(sink, ciphertext),
- result, sig_result)
+ results = (self.__read__(sink, ciphertext), result, sig_result)
if e.getcode() == errors.UNUSABLE_PUBKEY:
if result.invalid_recipients:
- raise errors.InvalidRecipients(result.invalid_recipients,
- error=e.error,
- results=results)
+ raise errors.InvalidRecipients(
+ result.invalid_recipients,
+ error=e.error,
+ results=results)
if e.getcode() == errors.UNUSABLE_SECKEY:
sig_result = self.op_sign_result()
if sig_result.invalid_signers:
- raise errors.InvalidSigners(sig_result.invalid_signers,
- error=e.error,
- results=results)
+ raise errors.InvalidSigners(
+ sig_result.invalid_signers,
+ error=e.error,
+ results=results)
# Otherwise, just raise the error, but attach the results
# first.
e.results = results
raise e
finally:
- if passphrase != None:
+ if passphrase is not None:
self.pinentry_mode = old_pinentry_mode
if old_passphrase_cb:
self.set_passphrase_cb(*old_passphrase_cb[1:])
@@ -344,12 +368,14 @@ class Context(GpgmeWrapper):
"""
plaintext = sink if sink else Data()
- if passphrase != None:
+ if passphrase is not 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:
@@ -361,11 +387,10 @@ class Context(GpgmeWrapper):
result = self.op_decrypt_result()
verify_result = self.op_verify_result() if verify else None
# Just raise the error, but attach the results first.
- e.results = (self.__read__(sink, plaintext),
- result, verify_result)
+ e.results = (self.__read__(sink, plaintext), result, verify_result)
raise e
finally:
- if passphrase != None:
+ if passphrase is not None:
self.pinentry_mode = old_pinentry_mode
if old_passphrase_cb:
self.set_passphrase_cb(*old_passphrase_cb[1:])
@@ -374,15 +399,15 @@ class Context(GpgmeWrapper):
verify_result = self.op_verify_result() if verify else None
results = (self.__read__(sink, plaintext), result, verify_result)
if result.unsupported_algorithm:
- raise errors.UnsupportedAlgorithm(result.unsupported_algorithm,
- results=results)
+ raise errors.UnsupportedAlgorithm(
+ result.unsupported_algorithm, results=results)
if verify:
if any(s.status != errors.NO_ERROR
for s in verify_result.signatures):
raise errors.BadSignatures(verify_result, results=results)
- if verify and verify != True:
+ if not verify: # was: if verify and verify != True:
missing = list()
for key in verify:
ok = False
@@ -398,8 +423,8 @@ class Context(GpgmeWrapper):
if not ok:
missing.append(key)
if missing:
- raise errors.MissingSignatures(verify_result, missing,
- results=results)
+ raise errors.MissingSignatures(
+ verify_result, missing, results=results)
return results
@@ -431,13 +456,13 @@ class Context(GpgmeWrapper):
try:
self.op_sign(data, signeddata, mode)
except errors.GPGMEError as e:
- results = (self.__read__(sink, signeddata),
- self.op_sign_result())
+ results = (self.__read__(sink, signeddata), self.op_sign_result())
if e.getcode() == errors.UNUSABLE_SECKEY:
if results[1].invalid_signers:
- raise errors.InvalidSigners(results[1].invalid_signers,
- error=e.error,
- results=results)
+ raise errors.InvalidSigners(
+ results[1].invalid_signers,
+ error=e.error,
+ results=results)
e.results = results
raise e
@@ -481,8 +506,7 @@ class Context(GpgmeWrapper):
self.op_verify(signed_data, None, data)
except errors.GPGMEError as e:
# Just raise the error, but attach the results first.
- e.results = (self.__read__(sink, data),
- self.op_verify_result())
+ e.results = (self.__read__(sink, data), self.op_verify_result())
raise e
results = (self.__read__(sink, data), self.op_verify_result())
@@ -504,12 +528,171 @@ class Context(GpgmeWrapper):
if not ok:
missing.append(key)
if missing:
- raise errors.MissingSignatures(results[1], missing,
- results=results)
+ raise errors.MissingSignatures(
+ results[1], missing, results=results)
return results
- def keylist(self, pattern=None, secret=False,
+ def key_import(self, data):
+ """Import data
+
+ Imports the given data into the Context.
+
+ Returns:
+ -- an object describing the results of imported or updated
+ keys
+
+ Raises:
+ TypeError -- Very rarely.
+ GPGMEError -- as signaled by the underlying library:
+
+ Import status errors, when they occur, will usually
+ be of NODATA. NO_PUBKEY indicates something
+ managed to run the function without any
+ arguments, while an argument of None triggers
+ the first NODATA of errors.GPGME in the
+ exception.
+ """
+ try:
+ self.op_import(data)
+ result = self.op_import_result()
+ if result.considered == 0:
+ status = constants.STATUS_IMPORT_PROBLEM
+ else:
+ status = constants.STATUS_KEY_CONSIDERED
+ except Exception as e:
+ if e == errors.GPGMEError:
+ if e.code_str == "No data":
+ status = constants.STATUS_NODATA
+ else:
+ status = constants.STATUS_FILE_ERROR
+ elif e == TypeError and hasattr(data, "decode") is True:
+ status = constants.STATUS_NO_PUBKEY
+ elif e == TypeError and hasattr(data, "encode") is True:
+ status = constants.STATUS_FILE_ERROR
+ else:
+ status = constants.STATUS_ERROR
+
+ if status == constants.STATUS_KEY_CONSIDERED:
+ import_result = result
+ else:
+ import_result = status
+
+ return import_result
+
+ def key_export(self, pattern=None):
+ """Export keys.
+
+ Exports public keys matching the pattern specified. If no
+ pattern is specified then exports all available keys.
+
+ Keyword arguments:
+ pattern -- return keys matching pattern (default: all keys)
+
+ Returns:
+ -- A key block containing one or more OpenPGP keys in
+ either ASCII armoured or binary format as determined
+ by the Context(). If there are no matching keys it
+ returns None.
+
+ Raises:
+ GPGMEError -- as signaled by the underlying library.
+ """
+ data = Data()
+ mode = 0
+ try:
+ self.op_export(pattern, mode, data)
+ data.seek(0, os.SEEK_SET)
+ pk_result = data.read()
+ except GPGMEError as e:
+ pk_result = e
+
+ if len(pk_result) > 0:
+ result = pk_result
+ else:
+ result = None
+
+ return result
+
+ def key_export_minimal(self, pattern=None):
+ """Export keys.
+
+ Exports public keys matching the pattern specified in a
+ minimised format. If no pattern is specified then exports all
+ available keys.
+
+ Keyword arguments:
+ pattern -- return keys matching pattern (default: all keys)
+
+ Returns:
+ -- A key block containing one or more minimised OpenPGP
+ keys in either ASCII armoured or binary format as
+ determined by the Context(). If there are no matching
+ keys it returns None.
+
+ Raises:
+ GPGMEError -- as signaled by the underlying library.
+ """
+ data = Data()
+ mode = gpgme.GPGME_EXPORT_MODE_MINIMAL
+ try:
+ self.op_export(pattern, mode, data)
+ data.seek(0, os.SEEK_SET)
+ pk_result = data.read()
+ except GPGMEError as e:
+ pk_result = e
+
+ if len(pk_result) > 0:
+ result = pk_result
+ else:
+ result = None
+
+ return result
+
+ def key_export_secret(self, pattern=None):
+ """Export secret keys.
+
+ Exports secret keys matching the pattern specified. If no
+ pattern is specified then exports or attempts to export all
+ available secret keys.
+
+ IMPORTANT: Each secret key to be exported will prompt for its
+ passphrase via an invocation of pinentry and gpg-agent. If the
+ passphrase is not entered or does not match then no data will be
+ exported. This is the same result as when specifying a pattern
+ that is not matched by the available keys.
+
+ Keyword arguments:
+ pattern -- return keys matching pattern (default: all keys)
+
+ Returns:
+ -- On success a key block containing one or more OpenPGP
+ secret keys in either ASCII armoured or binary format
+ as determined by the Context().
+ -- On failure while not raising an exception, returns None.
+
+ Raises:
+ GPGMEError -- as signaled by the underlying library.
+ """
+ data = Data()
+ mode = gpgme.GPGME_EXPORT_MODE_SECRET
+ try:
+ self.op_export(pattern, mode, data)
+ data.seek(0, os.SEEK_SET)
+ sk_result = data.read()
+ except GPGMEError as e:
+ sk_result = e
+
+ if len(sk_result) > 0:
+ result = sk_result
+ else:
+ result = None
+
+ return result
+
+ def keylist(self,
+ pattern=None,
+ secret=False,
mode=constants.keylist.mode.LOCAL,
source=None):
"""List keys
@@ -544,9 +727,17 @@ class Context(GpgmeWrapper):
key = self.op_keylist_next()
self.op_keylist_end()
- def create_key(self, userid, algorithm=None, expires_in=0, expires=True,
- sign=False, encrypt=False, certify=False, authenticate=False,
- passphrase=None, force=False):
+ def create_key(self,
+ userid,
+ algorithm=None,
+ expires_in=0,
+ expires=True,
+ sign=False,
+ encrypt=False,
+ certify=False,
+ authenticate=False,
+ passphrase=None,
+ force=False):
"""Create a primary key
Create a primary key for the user id USERID.
@@ -583,9 +774,10 @@ class Context(GpgmeWrapper):
encrypt -- request the encryption capability (see above)
certify -- request the certification capability (see above)
authenticate -- request the authentication capability (see above)
- passphrase -- protect the key with a passphrase (default: no passphrase)
- force -- force key creation even if a key with the same userid exists
- (default: False)
+ passphrase -- protect the key with a passphrase (default: no
+ passphrase)
+ force -- force key creation even if a key with the same userid
+ exists (default: False)
Returns:
-- an object describing the result of the key creation
@@ -598,22 +790,26 @@ class Context(GpgmeWrapper):
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:
- self.op_createkey(userid, algorithm,
- 0, # reserved
- expires_in,
- None, # extrakey
- ((constants.create.SIGN if sign else 0)
- | (constants.create.ENCR if encrypt else 0)
- | (constants.create.CERT if certify else 0)
- | (constants.create.AUTH if authenticate else 0)
- | (constants.create.NOPASSWD if passphrase == None else 0)
- | (0 if expires else constants.create.NOEXPIRE)
- | (constants.create.FORCE if force else 0)))
+ self.op_createkey(
+ userid,
+ algorithm,
+ 0, # reserved
+ expires_in,
+ None, # extrakey
+ ((constants.create.SIGN if sign else 0) |
+ (constants.create.ENCR if encrypt else 0) |
+ (constants.create.CERT if certify else 0) |
+ (constants.create.AUTH if authenticate else 0) |
+ (constants.create.NOPASSWD if passphrase is None else 0) |
+ (0 if expires else constants.create.NOEXPIRE) |
+ (constants.create.FORCE if force else 0)))
finally:
if util.is_a_string(passphrase):
self.pinentry_mode = old_pinentry_mode
@@ -622,8 +818,15 @@ class Context(GpgmeWrapper):
return self.op_genkey_result()
- def create_subkey(self, key, algorithm=None, expires_in=0, expires=True,
- sign=False, encrypt=False, authenticate=False, passphrase=None):
+ def create_subkey(self,
+ key,
+ algorithm=None,
+ expires_in=0,
+ expires=True,
+ sign=False,
+ encrypt=False,
+ authenticate=False,
+ passphrase=None):
"""Create a subkey
Create a subkey for the given KEY. As subkeys are a concept
@@ -659,7 +862,8 @@ class Context(GpgmeWrapper):
sign -- request the signing capability (see above)
encrypt -- request the encryption capability (see above)
authenticate -- request the authentication capability (see above)
- passphrase -- protect the subkey with a passphrase (default: no passphrase)
+ passphrase -- protect the subkey with a passphrase (default: no
+ passphrase)
Returns:
-- an object describing the result of the subkey creation
@@ -672,20 +876,23 @@ class Context(GpgmeWrapper):
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:
- self.op_createsubkey(key, algorithm,
- 0, # reserved
- expires_in,
- ((constants.create.SIGN if sign else 0)
- | (constants.create.ENCR if encrypt else 0)
- | (constants.create.AUTH if authenticate else 0)
- | (constants.create.NOPASSWD
- if passphrase == None else 0)
- | (0 if expires else constants.create.NOEXPIRE)))
+ self.op_createsubkey(
+ key,
+ algorithm,
+ 0, # reserved
+ expires_in,
+ ((constants.create.SIGN if sign else 0) |
+ (constants.create.ENCR if encrypt else 0) |
+ (constants.create.AUTH if authenticate else 0) |
+ (constants.create.NOPASSWD if passphrase is None else 0) |
+ (0 if expires else constants.create.NOEXPIRE)))
finally:
if util.is_a_string(passphrase):
self.pinentry_mode = old_pinentry_mode
@@ -745,8 +952,8 @@ class Context(GpgmeWrapper):
"""
flags = 0
- if uids == None or util.is_a_string(uids):
- pass#through unchanged
+ if uids is None or util.is_a_string(uids):
+ pass # through unchanged
else:
flags |= constants.keysign.LFSEP
uids = "\n".join(uids)
@@ -771,8 +978,11 @@ class Context(GpgmeWrapper):
"""
self.op_tofu_policy(key, policy)
- def assuan_transact(self, command,
- data_cb=None, inquire_cb=None, status_cb=None):
+ 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
@@ -803,12 +1013,10 @@ class Context(GpgmeWrapper):
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)
+ 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)
@@ -836,10 +1044,10 @@ class Context(GpgmeWrapper):
GPGMEError -- as signaled by the underlying library
"""
- if key == None:
+ if key is None:
raise ValueError("First argument cannot be None")
- if sink == None:
+ if sink is None:
sink = Data()
if fnc_value:
@@ -847,8 +1055,8 @@ class Context(GpgmeWrapper):
else:
opaquedata = (weakref.ref(self), func)
- result = gpgme.gpgme_op_interact(self.wrapped, key, flags,
- opaquedata, sink)
+ result = gpgme.gpgme_op_interact(self.wrapped, key, flags, opaquedata,
+ sink)
if self._callback_excinfo:
gpgme.gpg_raise_callback_exception(self)
errorcheck(result)
@@ -857,6 +1065,7 @@ class Context(GpgmeWrapper):
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
@@ -872,6 +1081,7 @@ class Context(GpgmeWrapper):
def pinentry_mode(self):
"""Pinentry mode"""
return self.get_pinentry_mode()
+
@pinentry_mode.setter
def pinentry_mode(self, value):
self.set_pinentry_mode(value)
@@ -880,6 +1090,7 @@ class Context(GpgmeWrapper):
def protocol(self):
"""Protocol to use"""
return self.get_protocol()
+
@protocol.setter
def protocol(self, value):
errorcheck(gpgme.gpgme_engine_check_version(value))
@@ -889,6 +1100,7 @@ class Context(GpgmeWrapper):
def home_dir(self):
"""Engine's home directory"""
return self.engine_info.home_dir
+
@home_dir.setter
def home_dir(self, value):
self.set_engine_info(self.protocol, home_dir=value)
@@ -901,24 +1113,15 @@ class Context(GpgmeWrapper):
# The list of functions is created using:
#
# $ grep '^gpgme_error_t ' obj/lang/python/python3.5-gpg/gpgme.h \
- # | grep -v _op_ | awk "/\(gpgme_ctx/ { printf (\"'%s',\\n\", \$2) } "
- return ((name.startswith('gpgme_op_')
- and not name.endswith('_result'))
- or name in {
- 'gpgme_new',
- 'gpgme_set_ctx_flag',
- 'gpgme_set_protocol',
- 'gpgme_set_sub_protocol',
- 'gpgme_set_keylist_mode',
- 'gpgme_set_pinentry_mode',
- 'gpgme_set_locale',
- 'gpgme_ctx_set_engine_info',
- 'gpgme_signers_add',
- 'gpgme_sig_notation_add',
- 'gpgme_set_sender',
- 'gpgme_cancel',
- 'gpgme_cancel_async',
- 'gpgme_get_key',
+ # | grep -v _op_ | awk "/\(gpgme_ctx/ { printf (\"'%s',\\n\", \$2) } "
+ return ((name.startswith('gpgme_op_') and not
+ name.endswith('_result')) or name in {
+ 'gpgme_new', 'gpgme_set_ctx_flag', 'gpgme_set_protocol',
+ 'gpgme_set_sub_protocol', 'gpgme_set_keylist_mode',
+ 'gpgme_set_pinentry_mode', 'gpgme_set_locale',
+ 'gpgme_ctx_set_engine_info', 'gpgme_signers_add',
+ 'gpgme_sig_notation_add', 'gpgme_set_sender',
+ 'gpgme_cancel', 'gpgme_cancel_async', 'gpgme_get_key'
})
_boolean_properties = {'armor', 'textmode', 'offline'}
@@ -938,6 +1141,7 @@ class Context(GpgmeWrapper):
# Implement the context manager protocol.
def __enter__(self):
return self
+
def __exit__(self, type, value, tb):
self.__del__()
@@ -1032,10 +1236,10 @@ class Context(GpgmeWrapper):
Please see the GPGME manual for more information.
"""
- if func == None:
+ if func is None:
hookdata = None
else:
- if hook == None:
+ if hook is None:
hookdata = (weakref.ref(self), func)
else:
hookdata = (weakref.ref(self), func, hook)
@@ -1057,10 +1261,10 @@ class Context(GpgmeWrapper):
Please see the GPGME manual for more information.
"""
- if func == None:
+ if func is None:
hookdata = None
else:
- if hook == None:
+ if hook is None:
hookdata = (weakref.ref(self), func)
else:
hookdata = (weakref.ref(self), func, hook)
@@ -1081,10 +1285,10 @@ class Context(GpgmeWrapper):
Please see the GPGME manual for more information.
"""
- if func == None:
+ if func is None:
hookdata = None
else:
- if hook == None:
+ if hook is None:
hookdata = (weakref.ref(self), func)
else:
hookdata = (weakref.ref(self), func, hook)
@@ -1152,8 +1356,8 @@ class Context(GpgmeWrapper):
magic numbers will break as a result.
"""
- warnings.warn("Call to deprecated method op_edit.",
- category=DeprecationWarning)
+ warnings.warn(
+ "Call to deprecated method op_edit.", category=DeprecationWarning)
return self.interact(key, func, sink=out, fnc_value=fnc_value)
@@ -1182,7 +1386,8 @@ class Data(GpgmeWrapper):
# This list is compiled using
#
# $ grep -v '^gpgme_error_t ' obj/lang/python/python3.5-gpg/gpgme.h \
- # | awk "/\(gpgme_data_t/ { printf (\"'%s',\\n\", \$2) } " | sed "s/'\\*/'/"
+ # | awk "/\(gpgme_data_t/ { printf (\"'%s',\\n\", \$2) } " \
+ # | sed "s/'\\*/'/"
return name not in {
'gpgme_data_read',
'gpgme_data_write',
@@ -1194,8 +1399,13 @@ class Data(GpgmeWrapper):
'gpgme_data_identify',
}
- def __init__(self, string=None, file=None, offset=None,
- length=None, cbs=None, copy=True):
+ 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.
@@ -1239,13 +1449,13 @@ class Data(GpgmeWrapper):
super(Data, self).__init__(None)
self.data_cbs = None
- if cbs != None:
+ if cbs is not None:
self.new_from_cbs(*cbs)
- elif string != None:
+ elif string is not None:
self.new_from_mem(string, copy)
- elif file != None and offset != None and length != None:
+ elif file is not None and offset is not None and length is not None:
self.new_from_filepart(file, offset, length)
- elif file != None:
+ elif file is not None:
if util.is_a_string(file):
self.new_from_file(file, copy)
else:
@@ -1258,7 +1468,7 @@ class Data(GpgmeWrapper):
# At interpreter shutdown, gpgme is set to NONE.
return
- if self.wrapped != None and gpgme.gpgme_data_release:
+ if self.wrapped is not None and gpgme.gpgme_data_release:
gpgme.gpgme_data_release(self.wrapped)
if self._callback_excinfo:
gpgme.gpg_raise_callback_exception(self)
@@ -1268,6 +1478,7 @@ class Data(GpgmeWrapper):
# Implement the context manager protocol.
def __enter__(self):
return self
+
def __exit__(self, type, value, tb):
self.__del__()
@@ -1282,7 +1493,8 @@ class Data(GpgmeWrapper):
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))
+ 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)
@@ -1300,12 +1512,12 @@ class Data(GpgmeWrapper):
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)
+ if hook is not 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)
+ 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)
@@ -1327,12 +1539,13 @@ class Data(GpgmeWrapper):
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)))
+ if fp is 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))
+ 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)
@@ -1365,7 +1578,7 @@ class Data(GpgmeWrapper):
raise GPGMEError.fromSyserror()
return written
- def read(self, size = -1):
+ 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.
@@ -1400,6 +1613,7 @@ class Data(GpgmeWrapper):
chunks.append(result)
return b''.join(chunks)
+
def pubkey_algo_string(subkey):
"""Return short algorithm string
@@ -1412,6 +1626,7 @@ def pubkey_algo_string(subkey):
"""
return gpgme.gpgme_pubkey_algo_string(subkey)
+
def pubkey_algo_name(algo):
"""Return name of public key algorithm
@@ -1424,6 +1639,7 @@ def pubkey_algo_name(algo):
"""
return gpgme.gpgme_pubkey_algo_name(algo)
+
def hash_algo_name(algo):
"""Return name of hash algorithm
@@ -1436,6 +1652,7 @@ def hash_algo_name(algo):
"""
return gpgme.gpgme_hash_algo_name(algo)
+
def get_protocol_name(proto):
"""Get protocol description
@@ -1447,6 +1664,7 @@ def get_protocol_name(proto):
"""
return gpgme.gpgme_get_protocol_name(proto)
+
def addrspec_from_uid(uid):
"""Return the address spec
@@ -1458,22 +1676,26 @@ def addrspec_from_uid(uid):
"""
return gpgme.gpgme_addrspec_from_uid(uid)
+
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):
+
+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:
@@ -1484,6 +1706,7 @@ def get_engine_info():
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
@@ -1492,10 +1715,12 @@ def set_engine_info(proto, file_name, home_dir=None):
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.
@@ -1509,7 +1734,7 @@ def wait(hang):
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:
+ if context is None:
errorcheck(status)
else:
context = Context(context)
diff --git a/lang/python/src/errors.py b/lang/python/src/errors.py
index c41ac692..9c7f0378 100644
--- a/lang/python/src/errors.py
+++ b/lang/python/src/errors.py
@@ -17,11 +17,12 @@
# 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
+del absolute_import, print_function, unicode_literals
+
# To appease static analysis tools, we define some constants here.
# They are overwritten with the proper values by process_constants.
NO_ERROR = None
@@ -30,6 +31,7 @@ EOF = None
util.process_constants('GPG_ERR_', globals())
del util
+
class GpgError(Exception):
"""A GPG Error
@@ -55,6 +57,7 @@ class GpgError(Exception):
exception objects.
"""
+
def __init__(self, error=None, context=None, results=None):
self.error = error
self.context = context
@@ -62,37 +65,38 @@ class GpgError(Exception):
@property
def code(self):
- if self.error == None:
+ if self.error is None:
return None
return gpgme.gpgme_err_code(self.error)
@property
def code_str(self):
- if self.error == None:
+ if self.error is None:
return None
return gpgme.gpgme_strerror(self.error)
@property
def source(self):
- if self.error == None:
+ if self.error is None:
return None
return gpgme.gpgme_err_source(self.error)
@property
def source_str(self):
- if self.error == None:
+ if self.error is None:
return None
return gpgme.gpgme_strsource(self.error)
def __str__(self):
msgs = []
- if self.context != None:
+ if self.context is not None:
msgs.append(self.context)
- if self.error != None:
+ if self.error is not None:
msgs.append(self.source_str)
msgs.append(self.code_str)
return ': '.join(msgs)
+
class GPGMEError(GpgError):
'''Generic error
@@ -101,24 +105,30 @@ class GPGMEError(GpgError):
returns an error. This is the error that was used in PyME.
'''
+
@classmethod
def fromSyserror(cls):
return cls(gpgme.gpgme_err_code_from_syserror())
+
@property
def message(self):
return self.context
+
def getstring(self):
return str(self)
+
def getcode(self):
return self.code
+
def getsource(self):
return self.source
-def errorcheck(retval, extradata = None):
+def errorcheck(retval, extradata=None):
if retval:
raise GPGMEError(retval, extradata)
+
class KeyNotFound(GPGMEError, KeyError):
"""Raised if a key was not found
@@ -127,63 +137,76 @@ class KeyNotFound(GPGMEError, KeyError):
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, **kwargs):
EncryptionError.__init__(self, **kwargs)
self.recipients = recipients
+
def __str__(self):
- return ", ".join("{}: {}".format(r.fpr,
- gpgme.gpgme_strerror(r.reason))
+ 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, **kwargs):
DeryptionError.__init__(self, **kwargs)
self.algorithm = algorithm
+
def __str__(self):
return self.algorithm
+
class SigningError(GpgError):
pass
+
class InvalidSigners(SigningError):
def __init__(self, signers, **kwargs):
SigningError.__init__(self, **kwargs)
self.signers = signers
+
def __str__(self):
- return ", ".join("{}: {}".format(s.fpr,
- gpgme.gpgme_strerror(s.reason))
+ return ", ".join("{}: {}".format(s.fpr, gpgme.gpgme_strerror(s.reason))
for s in self.signers)
+
class VerificationError(GpgError):
def __init__(self, result, **kwargs):
GpgError.__init__(self, **kwargs)
self.result = result
+
class BadSignatures(VerificationError):
def __str__(self):
- return ", ".join("{}: {}".format(s.fpr,
- gpgme.gpgme_strerror(s.status))
+ 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, **kwargs):
VerificationError.__init__(self, result, **kwargs)
self.missing = missing
+
def __str__(self):
return ", ".join(k.subkeys[0].fpr for k in self.missing)
diff --git a/lang/python/src/results.py b/lang/python/src/results.py
index bfd0f683..6b5f63c2 100644
--- a/lang/python/src/results.py
+++ b/lang/python/src/results.py
@@ -19,7 +19,6 @@
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
@@ -30,23 +29,28 @@ 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',
+ 'acquire',
+ 'append',
+ 'disown',
+ 'next',
+ 'own',
+ 'this',
+ 'thisown',
}
+
def __init__(self, fragile):
for key, func in self._type.items():
if hasattr(fragile, key):
@@ -67,52 +71,67 @@ class Result(object):
def __repr__(self):
return '{}({})'.format(
self.__class__.__name__,
- ', '.join('{}={!r}'.format(k, getattr(self, k))
- for k in dir(self) if not k.startswith('_')))
+ ', '.join('{}={!r}'.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, is_de_vs=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, is_de_vs=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/src/util.py b/lang/python/src/util.py
index e4fca4c1..320a823e 100644
--- a/lang/python/src/util.py
+++ b/lang/python/src/util.py
@@ -17,10 +17,12 @@
# 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
+del absolute_import, print_function, unicode_literals
+
+
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
@@ -30,17 +32,19 @@ def process_constants(prefix, scope):
"""
from . import gpgme
index = len(prefix)
- constants = {identifier[index:]: getattr(gpgme, identifier)
- for identifier in dir(gpgme)
- if identifier.startswith(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)
+ 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:
diff --git a/lang/python/tests/Makefile.am b/lang/python/tests/Makefile.am
index 3864f8ba..62970765 100644
--- a/lang/python/tests/Makefile.am
+++ b/lang/python/tests/Makefile.am
@@ -21,7 +21,8 @@ GPG_AGENT = gpg-agent
test_srcdir = $(top_srcdir)/tests/gpg
-TESTS_ENVIRONMENT = GNUPGHOME=$(abs_builddir) \
+GNUPGHOME=$(abs_builddir) \
+TESTS_ENVIRONMENT = GNUPGHOME=$(GNUPGHOME) \
LC_ALL=C GPG_AGENT_INFO= \
top_srcdir=$(top_srcdir) \
srcdir=$(srcdir) \
diff --git a/lang/python/tests/final.py b/lang/python/tests/final.py
index 65375cb8..d0d52dc4 100755
--- a/lang/python/tests/final.py
+++ b/lang/python/tests/final.py
@@ -18,12 +18,15 @@
# 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 os
import subprocess
import support
-_ = support # to appease pyflakes.
+_ = support # to appease pyflakes.
+
+del absolute_import, print_function, unicode_literals
-subprocess.check_call([os.path.join(os.getenv('top_srcdir'),
- "tests", "start-stop-agent"), "--stop"])
+subprocess.check_call([
+ os.path.join(os.getenv('top_srcdir'), "tests", "start-stop-agent"),
+ "--stop"
+])
diff --git a/lang/python/tests/initial.py b/lang/python/tests/initial.py
index 49e4f82e..30a8de7a 100755
--- a/lang/python/tests/initial.py
+++ b/lang/python/tests/initial.py
@@ -18,17 +18,20 @@
# 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 os
import subprocess
import gpg
import support
+del absolute_import, print_function, unicode_literals
+
print("Using gpg module from {0!r}.".format(os.path.dirname(gpg.__file__)))
-subprocess.check_call([os.path.join(os.getenv('top_srcdir'),
- "tests", "start-stop-agent"), "--start"])
+subprocess.check_call([
+ os.path.join(os.getenv('top_srcdir'), "tests", "start-stop-agent"),
+ "--start"
+])
with gpg.Context() as c:
alpha = c.get_key("A0FF4590BB6122EDEF6E3C542D727CC768697734", False)
diff --git a/lang/python/tests/run-tests.py b/lang/python/tests/run-tests.py
index 95df1978..cec13b5e 100644
--- a/lang/python/tests/run-tests.py
+++ b/lang/python/tests/run-tests.py
@@ -17,10 +17,8 @@
# 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
-from __future__ import division
-from __future__ import print_function
-from __future__ import unicode_literals
+from __future__ import absolute_import, division
+from __future__ import print_function, unicode_literals
import argparse
import glob
@@ -28,34 +26,50 @@ import os
import subprocess
import sys
+del absolute_import, division, print_function, unicode_literals
+
+
class SplitAndAccumulate(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
current = getattr(namespace, self.dest, list())
current.extend(values.split())
setattr(namespace, self.dest, current)
+
parser = argparse.ArgumentParser(description='Run tests.')
-parser.add_argument('tests', metavar='TEST', type=str, nargs='+',
- help='A test to run')
-parser.add_argument('-v', '--verbose', action="store_true", default=False,
- help='Be verbose.')
-parser.add_argument('-q', '--quiet', action="store_true", default=False,
- help='Be quiet.')
-parser.add_argument('--interpreters', metavar='PYTHON', type=str,
- default=[], action=SplitAndAccumulate,
- help='Use these interpreters to run the tests, ' +
- 'separated by spaces.')
-parser.add_argument('--srcdir', type=str,
- default=os.environ.get("srcdir", ""),
- help='Location of the tests.')
-parser.add_argument('--builddir', type=str,
- default=os.environ.get("abs_builddir", ""),
- help='Location of the tests.')
-parser.add_argument('--python-libdir', type=str,
- default=None,
- help='Optional location of the in-tree module lib directory.')
-parser.add_argument('--parallel', action="store_true", default=False,
- help='Ignored. For compatibility with run-tests.scm.')
+parser.add_argument(
+ 'tests', metavar='TEST', type=str, nargs='+', help='A test to run')
+parser.add_argument(
+ '-v', '--verbose', action="store_true", default=False, help='Be verbose.')
+parser.add_argument(
+ '-q', '--quiet', action="store_true", default=False, help='Be quiet.')
+parser.add_argument(
+ '--interpreters',
+ metavar='PYTHON',
+ type=str,
+ default=[],
+ action=SplitAndAccumulate,
+ help='Use these interpreters to run the tests, ' + 'separated by spaces.')
+parser.add_argument(
+ '--srcdir',
+ type=str,
+ default=os.environ.get("srcdir", ""),
+ help='Location of the tests.')
+parser.add_argument(
+ '--builddir',
+ type=str,
+ default=os.environ.get("abs_builddir", ""),
+ help='Location of the tests.')
+parser.add_argument(
+ '--python-libdir',
+ type=str,
+ default=None,
+ help='Optional location of the in-tree module lib directory.')
+parser.add_argument(
+ '--parallel',
+ action="store_true",
+ default=False,
+ help='Ignored. For compatibility with run-tests.scm.')
args = parser.parse_args()
if not args.interpreters:
@@ -64,26 +78,31 @@ if not args.interpreters:
out = sys.stdout if args.verbose else None
err = sys.stderr if args.verbose else None
+
def status_to_str(code):
return {0: "PASS", 77: "SKIP", 99: "ERROR"}.get(code, "FAIL")
+
results = list()
for interpreter in args.interpreters:
- version = subprocess.check_output(
- [interpreter, "-c", "import sys; print('{0}.{1}'.format(sys.version_info[0], sys.version_info[1]))"]).strip().decode()
+ version = subprocess.check_output([
+ interpreter, "-c",
+ "import sys; print('{0}.{1}'.format(sys.version_info[0], sys.version_info[1]))"
+ ]).strip().decode()
if args.python_libdir:
python_libdir = args.python_libdir
else:
- pattern = os.path.join(args.builddir, "..",
- "{0}-gpg".format(os.path.basename(interpreter)),
- "lib*")
+ pattern = os.path.join(args.builddir, "..", "{0}-gpg".format(
+ os.path.basename(interpreter)), "lib*")
libdirs = glob.glob(pattern)
if len(libdirs) == 0:
- sys.exit("Build directory matching {0!r} not found.".format(pattern))
+ sys.exit(
+ "Build directory matching {0!r} not found.".format(pattern))
elif len(libdirs) > 1:
- sys.exit("Multiple build directories matching {0!r} found: {1}".format(
- pattern, libdirs))
+ sys.exit(
+ "Multiple build directories matching {0!r} found: {1}".format(
+ pattern, libdirs))
python_libdir = libdirs[0]
env = dict(os.environ)
@@ -95,16 +114,22 @@ for interpreter in args.interpreters:
for test in args.tests:
status = subprocess.call(
[interpreter, os.path.join(args.srcdir, test)],
- env=env, stdout=out, stderr=err)
+ env=env,
+ stdout=out,
+ stderr=err)
if not args.quiet:
print("{0}: {1}".format(status_to_str(status), test))
results.append(status)
+
def count(status):
return len(list(filter(lambda x: x == status, results)))
+
+
def failed():
return len(list(filter(lambda x: x not in (0, 77, 99), results)))
+
if not args.quiet:
print("{0} tests run, {1} succeeded, {2} failed, {3} skipped.".format(
len(results), count(0), failed(), count(77)))
diff --git a/lang/python/tests/support.py b/lang/python/tests/support.py
index efccf315..e6b3d8bb 100644
--- a/lang/python/tests/support.py
+++ b/lang/python/tests/support.py
@@ -16,7 +16,6 @@
# 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 contextlib
import shutil
@@ -27,20 +26,28 @@ import tempfile
import time
import gpg
+del absolute_import, print_function, unicode_literals
+
+
def assert_gpg_version(version=(2, 1, 0)):
with gpg.Context() as c:
- clean_version = re.match(r'\d+\.\d+\.\d+', c.engine_info.version).group(0)
+ clean_version = re.match(r'\d+\.\d+\.\d+',
+ c.engine_info.version).group(0)
if tuple(map(int, clean_version.split('.'))) < version:
print("GnuPG too old: have {0}, need {1}.".format(
c.engine_info.version, '.'.join(map(str, version))))
sys.exit(77)
+
def have_tofu_support(ctx, some_uid):
- keys = list(ctx.keylist(some_uid,
- mode=(gpg.constants.keylist.mode.LOCAL
- |gpg.constants.keylist.mode.WITH_TOFU)))
+ keys = list(
+ ctx.keylist(
+ some_uid,
+ mode=(gpg.constants.keylist.mode.LOCAL |
+ gpg.constants.keylist.mode.WITH_TOFU)))
return len(keys) > 0
+
# Skip the Python tests for GnuPG < 2.1.12. Prior versions do not
# understand the command line flags that we assume exist. C.f. issue
# 3008.
@@ -53,13 +60,18 @@ encrypt_only = "F52770D5C4DB41408D918C9F920572769B9FE19C"
sign_only = "7CCA20CCDE5394CEE71C9F0BFED153F12F18F45D"
no_such_key = "A" * 40
+
def make_filename(name):
return os.path.join(os.environ['top_srcdir'], 'tests', 'gpg', name)
+
def in_srcdir(name):
return os.path.join(os.environ['srcdir'], name)
+
verbose = int(os.environ.get('verbose', 0)) > 1
+
+
def print_data(data):
if verbose:
try:
@@ -75,10 +87,12 @@ def print_data(data):
else:
sys.stdout.write(data)
+
def mark_key_trusted(ctx, key):
class Editor(object):
def __init__(self):
self.steps = ["trust", "save"]
+
def edit(self, status, args, out):
if args == "keyedit.prompt":
result = self.steps.pop(0)
@@ -91,6 +105,7 @@ def mark_key_trusted(ctx, key):
else:
result = None
return result
+
with gpg.Data() as sink:
ctx.op_edit(key, Editor().edit, sink, sink)
@@ -103,9 +118,11 @@ class TemporaryDirectory(object):
def __enter__(self):
self.path = tempfile.mkdtemp()
return self.path
+
def __exit__(self, *args):
shutil.rmtree(self.path, ignore_errors=True)
+
@contextlib.contextmanager
def EphemeralContext():
with TemporaryDirectory() as tmp:
@@ -124,7 +141,7 @@ def EphemeralContext():
ctx.assuan_transact(["KILLAGENT"])
except gpg.errors.GPGMEError as e:
if e.getcode() == gpg.errors.ASS_CONNECT_FAILED:
- pass # the agent was not running
+ pass # the agent was not running
else:
raise
diff --git a/lang/python/tests/t-callbacks.py b/lang/python/tests/t-callbacks.py
index b311e3d4..25a1c238 100755
--- a/lang/python/tests/t-callbacks.py
+++ b/lang/python/tests/t-callbacks.py
@@ -18,12 +18,13 @@
# 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 os
import gpg
import support
-_ = support # to appease pyflakes.
+_ = support # to appease pyflakes.
+
+del absolute_import, print_function, unicode_literals
c = gpg.Context()
c.set_pinentry_mode(gpg.constants.PINENTRY_MODE_LOOPBACK)
@@ -33,6 +34,7 @@ sink = gpg.Data()
# Valid passphrases, both as string and bytes.
for passphrase in ('foo', b'foo'):
+
def passphrase_cb(hint, desc, prev_bad, hook=None):
assert hook == passphrase
return hook
@@ -40,10 +42,12 @@ for passphrase in ('foo', b'foo'):
c.set_passphrase_cb(passphrase_cb, passphrase)
c.op_encrypt([], 0, source, sink)
+
# Returning an invalid type.
def passphrase_cb(hint, desc, prev_bad, hook=None):
return 0
+
c.set_passphrase_cb(passphrase_cb, None)
try:
c.op_encrypt([], 0, source, sink)
@@ -55,9 +59,12 @@ else:
# Raising an exception inside callback.
myException = Exception()
+
+
def passphrase_cb(hint, desc, prev_bad, hook=None):
raise myException
+
c.set_passphrase_cb(passphrase_cb, None)
try:
c.op_encrypt([], 0, source, sink)
@@ -66,10 +73,12 @@ except Exception as e:
else:
assert False, "Expected an error, got none"
+
# Wrong kind of callback function.
def bad_passphrase_cb():
pass
+
c.set_passphrase_cb(bad_passphrase_cb, None)
try:
c.op_encrypt([], 0, source, sink)
@@ -78,8 +87,6 @@ except Exception as e:
else:
assert False, "Expected an error, got none"
-
-
# Test the progress callback.
parms = """<GnupgKeyParms format="internal">
Key-Type: RSA
@@ -93,21 +100,26 @@ Expire-Date: 2099-12-31
"""
messages = []
+
+
def progress_cb(what, typ, current, total, hook=None):
assert hook == messages
messages.append(
"PROGRESS UPDATE: what = {}, type = {}, current = {}, total = {}"
.format(what, typ, current, total))
+
c = gpg.Context()
c.set_progress_cb(progress_cb, messages)
c.op_genkey(parms, None, None)
assert len(messages) > 0
+
# Test exception handling.
def progress_cb(what, typ, current, total, hook=None):
raise myException
+
c = gpg.Context()
c.set_progress_cb(progress_cb, None)
try:
@@ -117,7 +129,6 @@ except Exception as e:
else:
assert False, "Expected an error, got none"
-
# Test the edit callback.
c = gpg.Context()
c.set_pinentry_mode(gpg.constants.PINENTRY_MODE_LOOPBACK)
@@ -127,11 +138,15 @@ alpha = c.get_key("A0FF4590BB6122EDEF6E3C542D727CC768697734", False)
cookie = object()
edit_cb_called = False
+
+
def edit_cb(status, args, hook):
global edit_cb_called
edit_cb_called = True
assert hook == cookie
return "quit" if args == "keyedit.prompt" else None
+
+
c.op_edit(alpha, edit_cb, cookie, sink)
assert edit_cb_called
@@ -141,8 +156,11 @@ c.set_pinentry_mode(gpg.constants.PINENTRY_MODE_LOOPBACK)
c.set_passphrase_cb(lambda *args: "abc")
sink = gpg.Data()
+
def edit_cb(status, args):
raise myException
+
+
try:
c.op_edit(alpha, edit_cb, None, sink)
except Exception as e:
@@ -150,18 +168,19 @@ except Exception as e:
else:
assert False, "Expected an error, got none"
-
-
# Test the status callback.
source = gpg.Data("Hallo Leute\n")
sink = gpg.Data()
status_cb_called = False
+
+
def status_cb(keyword, args, hook=None):
global status_cb_called
status_cb_called = True
assert hook == cookie
+
c = gpg.Context()
c.set_status_cb(status_cb, cookie)
c.set_ctx_flag("full-status", "1")
@@ -172,9 +191,11 @@ assert status_cb_called
source = gpg.Data("Hallo Leute\n")
sink = gpg.Data()
+
def status_cb(keyword, args):
raise myException
+
c = gpg.Context()
c.set_status_cb(status_cb, None)
c.set_ctx_flag("full-status", "1")
@@ -186,13 +207,16 @@ else:
assert False, "Expected an error, got none"
-
# Test the data callbacks.
def read_cb(amount, hook=None):
assert hook == cookie
return 0
+
+
def release_cb(hook=None):
assert hook == cookie
+
+
data = gpg.Data(cbs=(read_cb, None, None, release_cb, cookie))
try:
data.read()
@@ -201,8 +225,11 @@ except Exception as e:
else:
assert False, "Expected an error, got none"
+
def read_cb(amount):
raise myException
+
+
data = gpg.Data(cbs=(read_cb, None, None, lambda: None))
try:
data.read()
@@ -215,6 +242,8 @@ else:
def write_cb(what, hook=None):
assert hook == cookie
return "wrong type"
+
+
data = gpg.Data(cbs=(None, write_cb, None, release_cb, cookie))
try:
data.write(b'stuff')
@@ -223,8 +252,11 @@ except Exception as e:
else:
assert False, "Expected an error, got none"
+
def write_cb(what):
raise myException
+
+
data = gpg.Data(cbs=(None, write_cb, None, lambda: None))
try:
data.write(b'stuff')
@@ -237,6 +269,8 @@ else:
def seek_cb(offset, whence, hook=None):
assert hook == cookie
return "wrong type"
+
+
data = gpg.Data(cbs=(None, None, seek_cb, release_cb, cookie))
try:
data.seek(0, os.SEEK_SET)
@@ -245,8 +279,11 @@ except Exception as e:
else:
assert False, "Expected an error, got none"
+
def seek_cb(offset, whence):
raise myException
+
+
data = gpg.Data(cbs=(None, None, seek_cb, lambda: None))
try:
data.seek(0, os.SEEK_SET)
diff --git a/lang/python/tests/t-data.py b/lang/python/tests/t-data.py
index 5cf074c5..006c11f4 100755
--- a/lang/python/tests/t-data.py
+++ b/lang/python/tests/t-data.py
@@ -18,14 +18,15 @@
# 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 io
import os
import tempfile
import gpg
import support
-_ = support # to appease pyflakes.
+_ = support # to appease pyflakes.
+
+del absolute_import, print_function, unicode_literals
data = gpg.Data('Hello world!')
assert data.read() == b'Hello world!'
@@ -94,7 +95,8 @@ with tempfile.NamedTemporaryFile() as tmp:
# Open using name, offset, and length.
data = gpg.Data(file=tmp.name, offset=23, length=42)
- assert data.read() == binjunk[23:23+42]
+ assert data.read() == binjunk[23:23 + 42]
+
# Test callbacks.
class DataObject(object):
@@ -118,6 +120,7 @@ class DataObject(object):
assert not self.released
self.released = True
+
do = DataObject()
cookie = object()
data = gpg.Data(cbs=(do.read, do.write, do.seek, do.release, cookie))
diff --git a/lang/python/tests/t-decrypt-verify.py b/lang/python/tests/t-decrypt-verify.py
index 03bbc4b5..fcaa1346 100755
--- a/lang/python/tests/t-decrypt-verify.py
+++ b/lang/python/tests/t-decrypt-verify.py
@@ -18,11 +18,13 @@
# 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 support
+del absolute_import, print_function, unicode_literals
+
+
def check_verify_result(result, summary, fpr, status):
assert len(result.signatures) == 1, "Unexpected number of signatures"
sig = result.signatures[0]
@@ -32,7 +34,9 @@ def check_verify_result(result, summary, fpr, status):
assert len(sig.notations) == 0
assert not sig.wrong_key_usage
assert sig.validity == gpg.constants.validity.FULL
- assert gpg.errors.GPGMEError(sig.validity_reason).getcode() == gpg.errors.NO_ERROR
+ assert gpg.errors.GPGMEError(
+ sig.validity_reason).getcode() == gpg.errors.NO_ERROR
+
c = gpg.Context()
@@ -47,10 +51,9 @@ assert not result.unsupported_algorithm, \
support.print_data(sink)
verify_result = c.op_verify_result()
-check_verify_result(verify_result,
- gpg.constants.sigsum.VALID | gpg.constants.sigsum.GREEN,
- "A0FF4590BB6122EDEF6E3C542D727CC768697734",
- gpg.errors.NO_ERROR)
+check_verify_result(
+ verify_result, gpg.constants.sigsum.VALID | gpg.constants.sigsum.GREEN,
+ "A0FF4590BB6122EDEF6E3C542D727CC768697734", gpg.errors.NO_ERROR)
# Idiomatic interface.
with gpg.Context() as c:
@@ -60,14 +63,13 @@ with gpg.Context() as c:
c.decrypt(open(support.make_filename("cipher-2.asc")), verify=[alpha])
assert plaintext.find(b'Wenn Sie dies lesen k') >= 0, \
'Plaintext not found'
- check_verify_result(verify_result,
- gpg.constants.sigsum.VALID | gpg.constants.sigsum.GREEN,
- "A0FF4590BB6122EDEF6E3C542D727CC768697734",
- gpg.errors.NO_ERROR)
+ check_verify_result(
+ verify_result, gpg.constants.sigsum.VALID | gpg.constants.sigsum.GREEN,
+ "A0FF4590BB6122EDEF6E3C542D727CC768697734", gpg.errors.NO_ERROR)
try:
- c.decrypt(open(support.make_filename("cipher-2.asc")),
- verify=[alpha, bob])
+ c.decrypt(
+ open(support.make_filename("cipher-2.asc")), verify=[alpha, bob])
except gpg.errors.MissingSignatures as e:
assert len(e.missing) == 1
assert e.missing[0] == bob
diff --git a/lang/python/tests/t-decrypt.py b/lang/python/tests/t-decrypt.py
index 05b6d8b0..f2417c9a 100755
--- a/lang/python/tests/t-decrypt.py
+++ b/lang/python/tests/t-decrypt.py
@@ -18,11 +18,12 @@
# 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 support
+del absolute_import, print_function, unicode_literals
+
c = gpg.Context()
source = gpg.Data(file=support.make_filename("cipher-1.asc"))
diff --git a/lang/python/tests/t-edit.py b/lang/python/tests/t-edit.py
index b1075a96..cbc17d95 100755
--- a/lang/python/tests/t-edit.py
+++ b/lang/python/tests/t-edit.py
@@ -19,13 +19,15 @@
# 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 sys
import os
import gpg
import support
-_ = support # to appease pyflakes.
+_ = support # to appease pyflakes.
+
+del absolute_import, print_function, unicode_literals
+
class KeyEditor(object):
def __init__(self):
@@ -47,11 +49,12 @@ class KeyEditor(object):
result = None
if self.verbose:
- sys.stderr.write("Code: {}, args: {!r}, Returning: {!r}\n"
- .format(status, args, result))
+ sys.stderr.write("Code: {}, args: {!r}, Returning: {!r}\n".format(
+ status, args, result))
return result
+
c = gpg.Context()
c.set_pinentry_mode(gpg.constants.PINENTRY_MODE_LOOPBACK)
c.set_passphrase_cb(lambda *args: "abc")
@@ -59,13 +62,15 @@ c.set_armor(True)
# The deprecated interface.
editor = KeyEditor()
-c.interact(c.get_key("A0FF4590BB6122EDEF6E3C542D727CC768697734", False),
- editor.edit_fnc)
+c.interact(
+ c.get_key("A0FF4590BB6122EDEF6E3C542D727CC768697734", False),
+ editor.edit_fnc)
assert editor.done
# The deprecated interface.
sink = gpg.Data()
editor = KeyEditor()
-c.op_edit(c.get_key("A0FF4590BB6122EDEF6E3C542D727CC768697734", False),
- editor.edit_fnc, sink, sink)
+c.op_edit(
+ c.get_key("A0FF4590BB6122EDEF6E3C542D727CC768697734", False),
+ editor.edit_fnc, sink, sink)
assert editor.done
diff --git a/lang/python/tests/t-encrypt-large.py b/lang/python/tests/t-encrypt-large.py
index 56460851..18576ac3 100755
--- a/lang/python/tests/t-encrypt-large.py
+++ b/lang/python/tests/t-encrypt-large.py
@@ -18,13 +18,14 @@
# 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 sys
import random
import gpg
import support
+del absolute_import, print_function, unicode_literals
+
if len(sys.argv) == 2:
nbytes = int(sys.argv[1])
else:
@@ -33,6 +34,8 @@ else:
c = gpg.Context()
ntoread = nbytes
+
+
def read_cb(amount):
global ntoread
chunk = ntoread if ntoread < amount else amount
@@ -41,12 +44,16 @@ def read_cb(amount):
assert chunk >= 0
return bytes(bytearray(random.randrange(256) for i in range(chunk)))
+
nwritten = 0
+
+
def write_cb(data):
global nwritten
nwritten += len(data)
return len(data)
+
source = gpg.Data(cbs=(read_cb, None, None, lambda: None))
sink = gpg.Data(cbs=(None, write_cb, None, lambda: None))
@@ -61,5 +68,5 @@ assert not result.invalid_recipients, \
assert ntoread == 0
if support.verbose:
- sys.stderr.write(
- "plaintext={} bytes, ciphertext={} bytes\n".format(nbytes, nwritten))
+ sys.stderr.write("plaintext={} bytes, ciphertext={} bytes\n".format(
+ nbytes, nwritten))
diff --git a/lang/python/tests/t-encrypt-sign.py b/lang/python/tests/t-encrypt-sign.py
index f04783f4..84d1abb4 100755
--- a/lang/python/tests/t-encrypt-sign.py
+++ b/lang/python/tests/t-encrypt-sign.py
@@ -18,15 +18,17 @@
# 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 sys
import gpg
import support
+del absolute_import, print_function, unicode_literals
+
c = gpg.Context()
c.set_armor(True)
+
def check_result(r, typ):
if r.invalid_signers:
sys.exit("Invalid signer found: {}".format(r.invalid_signers.fpr))
@@ -42,7 +44,8 @@ def check_result(r, typ):
sys.exit("Wrong pubkey algorithm reported: {}".format(
signature.pubkey_algo))
- if signature.hash_algo not in (gpg.constants.md.SHA1, gpg.constants.md.RMD160):
+ if signature.hash_algo not in (gpg.constants.md.SHA1,
+ gpg.constants.md.RMD160):
sys.exit("Wrong hash algorithm reported: {}".format(
signature.hash_algo))
@@ -53,6 +56,7 @@ def check_result(r, typ):
if signature.fpr != "A0FF4590BB6122EDEF6E3C542D727CC768697734":
sys.exit("Wrong fingerprint reported: {}".format(signature.fpr))
+
keys = []
keys.append(c.get_key("A0FF4590BB6122EDEF6E3C542D727CC768697734", False))
keys.append(c.get_key("D695676BDCEDCC2CDD6152BCFE180B1DA9E3B0B2", False))
@@ -61,7 +65,8 @@ for recipients in (keys, []):
source = gpg.Data("Hallo Leute\n")
sink = gpg.Data()
- c.op_encrypt_sign(recipients, gpg.constants.ENCRYPT_ALWAYS_TRUST, source, sink)
+ c.op_encrypt_sign(recipients, gpg.constants.ENCRYPT_ALWAYS_TRUST, source,
+ sink)
result = c.op_encrypt_result()
assert not result.invalid_recipients, \
"Invalid recipient encountered: {}".format(
@@ -72,13 +77,11 @@ for recipients in (keys, []):
support.print_data(sink)
-
# Idiomatic interface.
with gpg.Context(armor=True) as c:
message = "Hallo Leute\n".encode()
- ciphertext, _, sig_result = c.encrypt(message,
- recipients=keys,
- always_trust=True)
+ ciphertext, _, sig_result = c.encrypt(
+ message, recipients=keys, always_trust=True)
assert len(ciphertext) > 0
assert ciphertext.find(b'BEGIN PGP MESSAGE') > 0, 'Marker not found'
check_result(sig_result, gpg.constants.sig.mode.NORMAL)
diff --git a/lang/python/tests/t-encrypt-sym.py b/lang/python/tests/t-encrypt-sym.py
index 82992934..9b099fe0 100755
--- a/lang/python/tests/t-encrypt-sym.py
+++ b/lang/python/tests/t-encrypt-sym.py
@@ -18,12 +18,13 @@
# 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 os
import gpg
import support
-_ = support # to appease pyflakes.
+_ = support # to appease pyflakes.
+
+del absolute_import, print_function, unicode_literals
for passphrase in ("abc", b"abc"):
c = gpg.Context()
@@ -34,6 +35,7 @@ for passphrase in ("abc", b"abc"):
cipher = gpg.Data()
passphrase_cb_called = 0
+
def passphrase_cb(hint, desc, prev_bad, hook=None):
global passphrase_cb_called
passphrase_cb_called += 1
@@ -55,7 +57,7 @@ for passphrase in ("abc", b"abc"):
c.op_decrypt(cipher, plain)
# Seems like the passphrase is cached.
- #assert passphrase_cb_called == 2, \
+ # assert passphrase_cb_called == 2, \
# "Callback called {} times".format(passphrase_cb_called)
support.print_data(plain)
@@ -70,12 +72,12 @@ for passphrase in ("abc", b"abc"):
# Check that the passphrase callback is not altered.
def f(*args):
assert False
+
c.set_passphrase_cb(f)
message = "Hallo Leute\n".encode()
- ciphertext, _, _ = c.encrypt(message,
- passphrase=passphrase,
- sign=False)
+ ciphertext, _, _ = c.encrypt(
+ message, passphrase=passphrase, sign=False)
assert ciphertext.find(b'BEGIN PGP MESSAGE') > 0, 'Marker not found'
plaintext, _, _ = c.decrypt(ciphertext, passphrase=passphrase)
diff --git a/lang/python/tests/t-encrypt.py b/lang/python/tests/t-encrypt.py
index 921502a7..e702daa6 100755
--- a/lang/python/tests/t-encrypt.py
+++ b/lang/python/tests/t-encrypt.py
@@ -18,11 +18,12 @@
# 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 support
+del absolute_import, print_function, unicode_literals
+
c = gpg.Context()
c.set_armor(True)
@@ -41,36 +42,37 @@ support.print_data(sink)
# Idiomatic interface.
with gpg.Context(armor=True) as c:
- ciphertext, _, _ = c.encrypt("Hallo Leute\n".encode(),
- recipients=keys,
- sign=False,
- always_trust=True)
+ ciphertext, _, _ = c.encrypt(
+ "Hallo Leute\n".encode(),
+ recipients=keys,
+ sign=False,
+ always_trust=True)
assert len(ciphertext) > 0
assert ciphertext.find(b'BEGIN PGP MESSAGE') > 0, 'Marker not found'
- c.encrypt("Hallo Leute\n".encode(),
- recipients=[c.get_key(support.encrypt_only, False)],
- sign=False, always_trust=True)
+ c.encrypt(
+ "Hallo Leute\n".encode(),
+ recipients=[c.get_key(support.encrypt_only, False)],
+ sign=False,
+ always_trust=True)
try:
- c.encrypt("Hallo Leute\n".encode(),
- recipients=[c.get_key(support.sign_only, False)],
- sign=False, always_trust=True)
+ c.encrypt(
+ "Hallo Leute\n".encode(),
+ recipients=[c.get_key(support.sign_only, False)],
+ sign=False,
+ always_trust=True)
except gpg.errors.InvalidRecipients as e:
assert len(e.recipients) == 1
assert support.sign_only.endswith(e.recipients[0].fpr)
else:
assert False, "Expected an InvalidRecipients error, got none"
-
-
try:
# People might be tempted to provide strings.
# We should raise something useful.
- ciphertext, _, _ = c.encrypt("Hallo Leute\n",
- recipients=keys,
- sign=False,
- always_trust=True)
+ ciphertext, _, _ = c.encrypt(
+ "Hallo Leute\n", recipients=keys, sign=False, always_trust=True)
except TypeError as e:
# This test is a bit fragile, because the message
# may very well change. So if the behaviour will change
diff --git a/lang/python/tests/t-export.py b/lang/python/tests/t-export.py
index b9d52048..6d771dd4 100755
--- a/lang/python/tests/t-export.py
+++ b/lang/python/tests/t-export.py
@@ -18,11 +18,12 @@
# 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 support
+del absolute_import, print_function, unicode_literals
+
c = gpg.Context()
c.set_armor(True)
@@ -32,8 +33,8 @@ support.print_data(sink)
# Again. Now using a key array.
keys = []
-keys.append(c.get_key("0x68697734", False)) # Alpha
-keys.append(c.get_key("0xA9E3B0B2", False)) # Bob
+keys.append(c.get_key("0x68697734", False)) # Alpha
+keys.append(c.get_key("0xA9E3B0B2", False)) # Bob
sink = gpg.Data()
c.op_export_keys(keys, 0, sink)
support.print_data(sink)
diff --git a/lang/python/tests/t-file-name.py b/lang/python/tests/t-file-name.py
index 32fe84a0..d9c226fa 100755
--- a/lang/python/tests/t-file-name.py
+++ b/lang/python/tests/t-file-name.py
@@ -18,12 +18,13 @@
# 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 os
import gpg
import support
-_ = support # to appease pyflakes.
+_ = support # to appease pyflakes.
+
+del absolute_import, print_function, unicode_literals
testname = "abcde12345"
diff --git a/lang/python/tests/t-idiomatic.py b/lang/python/tests/t-idiomatic.py
index b7ae4eb9..238bbf31 100755
--- a/lang/python/tests/t-idiomatic.py
+++ b/lang/python/tests/t-idiomatic.py
@@ -18,7 +18,6 @@
# 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 sys
import io
@@ -26,7 +25,9 @@ import os
import tempfile
import gpg
import support
-_ = support # to appease pyflakes.
+_ = support # to appease pyflakes.
+
+del absolute_import, print_function, unicode_literals
# Both Context and Data can be used as context manager:
with gpg.Context() as c, gpg.Data() as d:
@@ -34,8 +35,9 @@ with gpg.Context() as c, gpg.Data() as d:
d.write(b"Halloechen")
leak_c = c
leak_d = d
-assert leak_c.wrapped == None
-assert leak_d.wrapped == None
+assert leak_c.wrapped is None
+assert leak_d.wrapped is None
+
def sign_and_verify(source, signed, sink):
with gpg.Context() as c:
@@ -53,6 +55,7 @@ def sign_and_verify(source, signed, sink):
sink.seek(0, os.SEEK_SET)
assert sink.read() == b"Hallo Leute\n"
+
# Demonstrate automatic wrapping of file-like objects with 'fileno'
# method.
with tempfile.TemporaryFile() as source, \
@@ -73,7 +76,7 @@ if sys.version_info[0] == 3:
bio.truncate(1)
if len(bio.getvalue()) != 1:
# This version of Python is affected, preallocate buffer.
- preallocate = 128*b'\x00'
+ preallocate = 128 * b'\x00'
else:
preallocate = b''
diff --git a/lang/python/tests/t-import.py b/lang/python/tests/t-import.py
index e2edf5a2..8d8a6998 100755
--- a/lang/python/tests/t-import.py
+++ b/lang/python/tests/t-import.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
-# Copyright (C) 2016 g10 Code GmbH
+# Copyright (C) 2016 Tobias Mueller <muelli at cryptobitch.de>
#
# This file is part of GPGME.
#
@@ -18,45 +18,47 @@
# 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 support
+del absolute_import, print_function, unicode_literals
+
+
def check_result(result, fpr, secret):
assert result.considered == 1 or (secret and result.considered == 3)
assert result.no_user_id == 0
- assert not ((secret and result.imported != 0)
- or (not secret and (result.imported != 0
- and result.imported != 1)))
+ assert not ((secret and result.imported != 0) or
+ (not secret and
+ (result.imported != 0 and result.imported != 1)))
assert result.imported_rsa == 0
- assert not ((secret and (result.unchanged != 0 and result.unchanged != 1))
- or (not secret and ((result.imported == 0
- and result.unchanged != 1)
- or (result.imported == 1
- and result.unchanged != 0))))
+ assert not ((secret and
+ (result.unchanged != 0 and result.unchanged != 1)) or
+ (not secret and
+ ((result.imported == 0 and result.unchanged != 1) or
+ (result.imported == 1 and result.unchanged != 0))))
assert result.new_user_ids == 0
assert result.new_sub_keys == 0
- assert not ((secret
- and ((result.secret_imported == 0
- and result.new_signatures != 0)
- or (result.secret_imported == 1
- and result.new_signatures > 1)))
- or (not secret and result.new_signatures != 0))
+ assert not ((secret and (
+ (result.secret_imported == 0 and result.new_signatures != 0) or
+ (result.secret_imported == 1 and result.new_signatures > 1))) or
+ (not secret and result.new_signatures != 0))
assert result.new_revocations == 0
- assert not ((secret and result.secret_read != 1 and result.secret_read != 3)
- or (not secret and result.secret_read != 0))
- assert not ((secret and result.secret_imported != 0
- and result.secret_imported != 1
- and result.secret_imported != 2)
- or (not secret and result.secret_imported != 0))
- assert not ((secret
- and ((result.secret_imported == 0
- and result.secret_unchanged != 1
- and result.secret_unchanged != 2)
- or (result.secret_imported == 1
- and result.secret_unchanged != 0)))
- or (not secret and result.secret_unchanged != 0))
+ assert not (
+ (secret and result.secret_read != 1 and result.secret_read != 3) or
+ (not secret and result.secret_read != 0))
+ assert not (
+ (secret and result.secret_imported != 0 and result.
+ secret_imported != 1 and result.
+ secret_imported != 2) or (not secret and result.
+ secret_imported != 0))
+ assert not ((secret and
+ ((result.secret_imported == 0 and result.
+ secret_unchanged != 1 and result.
+ secret_unchanged != 2) or (result.
+ secret_imported == 1 and result.
+ secret_unchanged != 0))) or
+ (not secret and result.secret_unchanged != 0))
assert result.not_imported == 0
if secret:
assert not (len(result.imports) in (0, 3))
@@ -67,12 +69,17 @@ def check_result(result, fpr, secret):
assert len(result.imports) == 1 or fpr == result.imports[1].fpr
assert result.imports[0].result == 0
+
c = gpg.Context()
-c.op_import(gpg.Data(file=support.make_filename("pubkey-1.asc")))
-result = c.op_import_result()
+result = c.key_import(open(support.make_filename("pubkey-1.asc"), 'rb').read())
check_result(result, "ADAB7FCC1F4DE2616ECFA402AF82244F9CD9FD55", False)
-c.op_import(gpg.Data(file=support.make_filename("seckey-1.asc")))
-result = c.op_import_result()
+result = c.key_import(open(support.make_filename("seckey-1.asc"), 'rb').read())
check_result(result, "ADAB7FCC1F4DE2616ECFA402AF82244F9CD9FD55", True)
+
+try:
+ result = c.key_import(b"thisisnotakey")
+except ValueError:
+ pass
+assert result.considered == 0
diff --git a/lang/python/tests/t-keylist-from-data.py b/lang/python/tests/t-keylist-from-data.py
index 6503eb7a..f82ca842 100755
--- a/lang/python/tests/t-keylist-from-data.py
+++ b/lang/python/tests/t-keylist-from-data.py
@@ -18,87 +18,142 @@
# 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 support
+del absolute_import, print_function, unicode_literals
+
support.assert_gpg_version((2, 1, 14))
+
# Check expration of keys. This test assumes three subkeys of which
# 2 are expired; it is used with the "Whisky" test key. It has
# already been checked that these 3 subkeys are available.
def check_whisky(name, key):
- sub1 = key.subkeys[2]
- sub2 = key.subkeys[3]
+ sub1 = key.subkeys[2]
+ sub2 = key.subkeys[3]
+
+ assert sub1.expired and sub2.expired, \
+ "Subkey of `{}' not flagged as expired".format(name)
+ assert sub1.expires == 1129636886 and sub2.expires == 1129636939, \
+ "Subkey of `{}' has wrong expiration date".format(name)
- assert sub1.expired and sub2.expired, \
- "Subkey of `{}' not flagged as expired".format(name)
- assert sub1.expires == 1129636886 and sub2.expires == 1129636939, \
- "Subkey of `{}' has wrong expiration date".format(name)
keys = [
- [ "A0FF4590BB6122EDEF6E3C542D727CC768697734", "6AE6D7EE46A871F8",
- [ [ "Alfa Test", "demo key", "[email protected]" ],
- [ "Alpha Test", "demo key", "[email protected]" ],
- [ "Alice", "demo key", "" ] ], 1 ],
- [ "D695676BDCEDCC2CDD6152BCFE180B1DA9E3B0B2", "5381EA4EE29BA37F",
- [ [ "Bob", "demo key", "" ],
- [ "Bravo Test", "demo key", "[email protected]" ] ], 1 ],
- [ "61EE841A2A27EB983B3B3C26413F4AF31AFDAB6C", "E71E72ACBC43DA60",
- [ [ "Charlie Test", "demo key", "[email protected]" ] ], 1 ],
- [ "6560C59C43D031C54D7C588EEBA9F240EB9DC9E6", "06F22880B0C45424",
- [ [ "Delta Test", "demo key", "[email protected]" ] ], 1 ],
- [ "3531152DE293E26A07F504BC318C1FAEFAEF6D1B", "B5C79E1A7272144D",
- [ [ "Echelon", "demo key", "" ],
- [ "Echo Test", "demo key", "[email protected]" ],
- [ "Eve", "demo key", "" ] ], 1 ],
- [ "56D33268F7FE693FBB594762D4BF57F37372E243", "0A32EE79EE45198E",
- [ [ "Foxtrot Test", "demo key", "[email protected]" ] ], 1 ],
- [ "C9C07DCC6621B9FB8D071B1D168410A48FC282E6", "247491CC9DCAD354",
- [ [ "Golf Test", "demo key", "[email protected]" ] ], 1 ],
- [ "9E91CBB11E4D4135583EF90513DB965534C6E3F1", "76E26537D622AD0A",
- [ [ "Hotel Test", "demo key", "[email protected]" ] ], 1 ],
- [ "CD538D6CC9FB3D745ECDA5201FE8FC6F04259677", "C1C8EFDE61F76C73",
- [ [ "India Test", "demo key", "[email protected]" ] ], 1 ],
- [ "F8F1EDC73995AB739AD54B380C820C71D2699313", "BD0B108735F8F136",
- [ [ "Juliet Test", "demo key", "[email protected]" ] ], 1 ],
- [ "3FD11083779196C2ECDD9594AD1B0FAD43C2D0C7", "86CBB34A9AF64D02",
- [ [ "Kilo Test", "demo key", "[email protected]" ] ], 1 ],
- [ "1DDD28CEF714F5B03B8C246937CAB51FB79103F8", "0363B449FE56350C",
- [ [ "Lima Test", "demo key", "[email protected]" ] ], 1 ],
- [ "2686AA191A278013992C72EBBE794852BE5CF886", "5F600A834F31EAE8",
- [ [ "Mallory", "demo key", "" ],
- [ "Mike Test", "demo key", "[email protected]" ] ], 1 ],
- [ "5AB9D6D7BAA1C95B3BAA3D9425B00FD430CEC684", "4C1D63308B70E472",
- [ [ "November Test", "demo key", "[email protected]" ] ], 1 ],
- [ "43929E89F8F79381678CAE515F6356BA6D9732AC", "FF0785712681619F",
- [ [ "Oscar Test", "demo key", "[email protected]" ] ], 1 ],
- [ "6FAA9C201E5E26DCBAEC39FD5D15E01D3FF13206", "2764E18263330D9C",
- [ [ "Papa test", "demo key", "[email protected]" ] ], 1 ],
- [ "A7969DA1C3297AA96D49843F1C67EC133C661C84", "6CDCFC44A029ACF4",
- [ [ "Quebec Test", "demo key", "[email protected]" ] ], 1 ],
- [ "38FBE1E4BF6A5E1242C8F6A13BDBEDB1777FBED3", "9FAB805A11D102EA",
- [ [ "Romeo Test", "demo key", "[email protected]" ] ], 1 ],
- [ "045B2334ADD69FC221076841A5E67F7FA3AE3EA1", "93B88B0F0F1B50B4",
- [ [ "Sierra Test", "demo key", "[email protected]" ] ], 1 ],
- [ "ECAC774F4EEEB0620767044A58CB9A4C85A81F38", "97B60E01101C0402",
- [ [ "Tango Test", "demo key", "[email protected]" ] ], 1 ],
- [ "0DBCAD3F08843B9557C6C4D4A94C0F75653244D6", "93079B915522BDB9",
- [ [ "Uniform Test", "demo key", "[email protected]" ] ], 1 ],
- [ "E8143C489C8D41124DC40D0B47AF4B6961F04784", "04071FB807287134",
- [ [ "Victor Test", "demo key", "[email protected]" ] ], 1 ],
- [ "E8D6C90B683B0982BD557A99DEF0F7B8EC67DBDE", "D7FBB421FD6E27F6",
- [ [ "Whisky Test", "demo key", "[email protected]" ] ], 3,
- check_whisky ],
- [ "04C1DF62EFA0EBB00519B06A8979A6C5567FB34A", "5CC6F87F41E408BE",
- [ [ "XRay Test", "demo key", "[email protected]" ] ], 1 ],
- [ "ED9B316F78644A58D042655A9EEF34CD4B11B25F", "5ADFD255F7B080AD",
- [ [ "Yankee Test", "demo key", "[email protected]" ] ], 1 ],
- [ "23FD347A419429BACCD5E72D6BC4778054ACD246", "EF9DC276A172C881",
- [ [ "Zulu Test", "demo key", "[email protected]" ] ], 1 ],
+ [
+ "A0FF4590BB6122EDEF6E3C542D727CC768697734", "6AE6D7EE46A871F8",
+ [["Alfa Test", "demo key", "[email protected]"],
+ ["Alpha Test", "demo key", "[email protected]"],
+ ["Alice", "demo key", ""]], 1
+ ],
+ [
+ "D695676BDCEDCC2CDD6152BCFE180B1DA9E3B0B2", "5381EA4EE29BA37F",
+ [["Bob", "demo key", ""],
+ ["Bravo Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "61EE841A2A27EB983B3B3C26413F4AF31AFDAB6C", "E71E72ACBC43DA60",
+ [["Charlie Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "6560C59C43D031C54D7C588EEBA9F240EB9DC9E6", "06F22880B0C45424",
+ [["Delta Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "3531152DE293E26A07F504BC318C1FAEFAEF6D1B", "B5C79E1A7272144D",
+ [["Echelon", "demo key",
+ ""], ["Echo Test", "demo key", "[email protected]"],
+ ["Eve", "demo key", ""]], 1
+ ],
+ [
+ "56D33268F7FE693FBB594762D4BF57F37372E243", "0A32EE79EE45198E",
+ [["Foxtrot Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "C9C07DCC6621B9FB8D071B1D168410A48FC282E6", "247491CC9DCAD354",
+ [["Golf Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "9E91CBB11E4D4135583EF90513DB965534C6E3F1", "76E26537D622AD0A",
+ [["Hotel Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "CD538D6CC9FB3D745ECDA5201FE8FC6F04259677", "C1C8EFDE61F76C73",
+ [["India Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "F8F1EDC73995AB739AD54B380C820C71D2699313", "BD0B108735F8F136",
+ [["Juliet Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "3FD11083779196C2ECDD9594AD1B0FAD43C2D0C7", "86CBB34A9AF64D02",
+ [["Kilo Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "1DDD28CEF714F5B03B8C246937CAB51FB79103F8", "0363B449FE56350C",
+ [["Lima Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "2686AA191A278013992C72EBBE794852BE5CF886", "5F600A834F31EAE8",
+ [["Mallory", "demo key", ""],
+ ["Mike Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "5AB9D6D7BAA1C95B3BAA3D9425B00FD430CEC684", "4C1D63308B70E472",
+ [["November Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "43929E89F8F79381678CAE515F6356BA6D9732AC", "FF0785712681619F",
+ [["Oscar Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "6FAA9C201E5E26DCBAEC39FD5D15E01D3FF13206", "2764E18263330D9C",
+ [["Papa test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "A7969DA1C3297AA96D49843F1C67EC133C661C84", "6CDCFC44A029ACF4",
+ [["Quebec Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "38FBE1E4BF6A5E1242C8F6A13BDBEDB1777FBED3", "9FAB805A11D102EA",
+ [["Romeo Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "045B2334ADD69FC221076841A5E67F7FA3AE3EA1", "93B88B0F0F1B50B4",
+ [["Sierra Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "ECAC774F4EEEB0620767044A58CB9A4C85A81F38", "97B60E01101C0402",
+ [["Tango Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "0DBCAD3F08843B9557C6C4D4A94C0F75653244D6", "93079B915522BDB9",
+ [["Uniform Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "E8143C489C8D41124DC40D0B47AF4B6961F04784", "04071FB807287134",
+ [["Victor Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "E8D6C90B683B0982BD557A99DEF0F7B8EC67DBDE", "D7FBB421FD6E27F6",
+ [["Whisky Test", "demo key", "[email protected]"]], 3, check_whisky
+ ],
+ [
+ "04C1DF62EFA0EBB00519B06A8979A6C5567FB34A", "5CC6F87F41E408BE",
+ [["XRay Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "ED9B316F78644A58D042655A9EEF34CD4B11B25F", "5ADFD255F7B080AD",
+ [["Yankee Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "23FD347A419429BACCD5E72D6BC4778054ACD246", "EF9DC276A172C881",
+ [["Zulu Test", "demo key", "[email protected]"]], 1
+ ],
]
+
def check_global(key, uids, n_subkeys):
assert not key.revoked, "Key unexpectedly revoked"
assert not key.expired, "Key unexpectedly expired"
@@ -107,18 +162,18 @@ def check_global(key, uids, n_subkeys):
assert key.can_sign, "Key unexpectedly unusable for signing"
assert key.can_certify, "Key unexpectedly unusable for certifications"
assert not key.secret, "Key unexpectedly secret"
- assert not key.protocol != gpg.constants.protocol.OpenPGP, \
- "Key has unexpected protocol: {}".format(key.protocol)
- assert not key.issuer_serial, \
- "Key unexpectedly carries issuer serial: {}".format(key.issuer_serial)
- assert not key.issuer_name, \
- "Key unexpectedly carries issuer name: {}".format(key.issuer_name)
- assert not key.chain_id, \
- "Key unexpectedly carries chain ID: {}".format(key.chain_id)
- assert key.owner_trust == gpg.constants.validity.UNKNOWN, \
- "Key has unexpected owner trust: {}".format(key.owner_trust)
- assert len(key.subkeys) - 1 == n_subkeys, \
- "Key `{}' has unexpected number of subkeys".format(uids[0][0])
+ assert not key.protocol != gpg.constants.protocol.
+ OpenPGP, "Key has unexpected protocol: {}".format(key.protocol)
+ assert not key.issuer_serial,
+ "Key unexpectedly carries issuer serial: {}".format(key.issuer_serial)
+ assert not key.issuer_name,
+ "Key unexpectedly carries issuer name: {}".format(key.issuer_name)
+ assert not key.chain_id,
+ "Key unexpectedly carries chain ID: {}".format(key.chain_id)
+ assert key.owner_trust == gpg.constants.validity.UNKNOWN,
+ "Key has unexpected owner trust: {}".format(key.owner_trust)
+ assert len(key.subkeys) - 1 == n_subkeys,
+ "Key `{}' has unexpected number of subkeys".format(uids[0][0])
def check_subkey(fpr, which, subkey):
@@ -128,54 +183,55 @@ def check_subkey(fpr, which, subkey):
assert not subkey.invalid, which + " key unexpectedly invalid"
if which == "Primary":
- assert not subkey.can_encrypt, \
- which + " key unexpectedly usable for encryption"
- assert subkey.can_sign, \
- which + " key unexpectedly unusable for signing"
- assert subkey.can_certify, \
- which + " key unexpectedly unusable for certifications"
+ assert not subkey.can_encrypt,
+ which + " key unexpectedly usable for encryption"
+ assert subkey.can_sign,
+ which + " key unexpectedly unusable for signing"
+ assert subkey.can_certify,
+ which + " key unexpectedly unusable for certifications"
else:
- assert subkey.can_encrypt, \
- which + " key unexpectedly unusable for encryption"
- assert not subkey.can_sign, \
- which + " key unexpectedly usable for signing"
- assert not subkey.can_certify, \
- which + " key unexpectedly usable for certifications"
+ assert subkey.can_encrypt,
+ which + " key unexpectedly unusable for encryption"
+ assert not subkey.can_sign,
+ which + " key unexpectedly usable for signing"
+ assert not subkey.can_certify,
+ which + " key unexpectedly usable for certifications"
assert not subkey.secret, which + " key unexpectedly secret"
assert not subkey.is_cardkey, "Public key marked as card key"
assert not subkey.card_number, "Public key with card number set"
- assert not subkey.pubkey_algo != (gpg.constants.pk.DSA if which == "Primary"
- else gpg.constants.pk.ELG_E), \
- which + " key has unexpected public key algo: {}".\
- format(subkey.pubkey_algo)
- assert subkey.length == 1024, \
- which + " key has unexpected length: {}".format(subkey.length)
- assert fpr.endswith(subkey.keyid), \
- which + " key has unexpected key ID: {}".format(subkey.keyid)
- assert which == "Secondary" or subkey.fpr == fpr, \
- which + " key has unexpected fingerprint: {}".format(subkey.fpr)
- assert not subkey.expires, \
- which + " key unexpectedly expires: {}".format(subkey.expires)
+ assert not subkey.pubkey_algo !=
+ (gpg.constants.pk.DSA if which == "Primary" else gpg.constants.pk.ELG_E),
+ which + " key has unexpected public key algo: {}".format(subkey.
+ pubkey_algo)
+ assert subkey.length == 1024,
+ which + " key has unexpected length: {}".format(subkey.length)
+ assert fpr.endswith(subkey.keyid),
+ which + " key has unexpected key ID: {}".format(subkey.keyid)
+ assert which == "Secondary" or subkey.fpr == fpr,
+ which + " key has unexpected fingerprint: {}".format(subkey.fpr)
+ assert not subkey.expires,
+ which + " key unexpectedly expires: {}".format(subkey.expires)
+
def check_uid(which, ref, uid):
assert not uid.revoked, which + " user ID unexpectedly revoked"
assert not uid.invalid, which + " user ID unexpectedly invalid"
- assert uid.validity == gpg.constants.validity.UNKNOWN, \
- which + " user ID has unexpected validity: {}".format(uid.validity)
+ assert uid.validity == gpg.constants.validity.UNKNOWN,
+ which + " user ID has unexpected validity: {}".format(uid.validity)
assert not uid.signatures, which + " user ID unexpectedly signed"
- assert uid.name == ref[0], \
- "Unexpected name in {} user ID: {!r}".format(which.lower(), uid.name)
- assert uid.comment == ref[1], \
- "Unexpected comment in {} user ID: {!r}".format(which.lower(),
- uid.comment)
- assert uid.email == ref[2], \
- "Unexpected email in {} user ID: {!r}".format(which.lower(), uid.email)
+ assert uid.name == ref[0],
+ "Unexpected name in {} user ID: {!r}".format(which.lower(), uid.name)
+ assert uid.comment == ref[1],
+ "Unexpected comment in {} user ID: {!r}".format(which.lower(), uid.comment)
+ assert uid.email == ref[2],
+ "Unexpected email in {} user ID: {!r}".format(which.lower(), uid.email)
+
# Export all the data from our keyring...
key_data = gpg.Data()
with gpg.Context() as c:
- c.op_export_keys([c.get_key(k[0]) for k in keys], 0, key_data)
+ c.op_export_keys([c.get_key(k[0]) for k in keys], 0, key_data)
# ... rewind the tape...
key_data.rewind()
@@ -201,11 +257,11 @@ with support.EphemeralContext() as c:
assert len(key.uids) == len(uids)
check_uid("First", uids[0], key.uids[0])
if len(key.uids) > 1:
- check_uid("Second", uids[1], key.uids[1])
+ check_uid("Second", uids[1], key.uids[1])
if len(key.uids) > 2:
- check_uid("Third", uids[2], key.uids[2])
+ check_uid("Third", uids[2], key.uids[2])
if misc_check:
- misc_check (uids[0][0], key)
+ misc_check(uids[0][0], key)
assert len(list(c.keylist())) == 0, "Keys were imported"
diff --git a/lang/python/tests/t-keylist.py b/lang/python/tests/t-keylist.py
index 4505d3c9..b725fc36 100755
--- a/lang/python/tests/t-keylist.py
+++ b/lang/python/tests/t-keylist.py
@@ -18,87 +18,142 @@
# 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 support
+del absolute_import, print_function, unicode_literals
+
c = gpg.Context()
+
# Check expration of keys. This test assumes three subkeys of which
# 2 are expired; it is used with the "Whisky" test key. It has
# already been checked that these 3 subkeys are available.
def check_whisky(name, key):
- sub1 = key.subkeys[2]
- sub2 = key.subkeys[3]
+ sub1 = key.subkeys[2]
+ sub2 = key.subkeys[3]
+
+ assert sub1.expired and sub2.expired, \
+ "Subkey of `{}' not flagged as expired".format(name)
+ assert sub1.expires == 1129636886 and sub2.expires == 1129636939, \
+ "Subkey of `{}' has wrong expiration date".format(name)
- assert sub1.expired and sub2.expired, \
- "Subkey of `{}' not flagged as expired".format(name)
- assert sub1.expires == 1129636886 and sub2.expires == 1129636939, \
- "Subkey of `{}' has wrong expiration date".format(name)
keys = [
- [ "A0FF4590BB6122EDEF6E3C542D727CC768697734", "6AE6D7EE46A871F8",
- [ [ "Alfa Test", "demo key", "[email protected]" ],
- [ "Alpha Test", "demo key", "[email protected]" ],
- [ "Alice", "demo key", "" ] ], 1 ],
- [ "D695676BDCEDCC2CDD6152BCFE180B1DA9E3B0B2", "5381EA4EE29BA37F",
- [ [ "Bob", "demo key", "" ],
- [ "Bravo Test", "demo key", "[email protected]" ] ], 1 ],
- [ "61EE841A2A27EB983B3B3C26413F4AF31AFDAB6C", "E71E72ACBC43DA60",
- [ [ "Charlie Test", "demo key", "[email protected]" ] ], 1 ],
- [ "6560C59C43D031C54D7C588EEBA9F240EB9DC9E6", "06F22880B0C45424",
- [ [ "Delta Test", "demo key", "[email protected]" ] ], 1 ],
- [ "3531152DE293E26A07F504BC318C1FAEFAEF6D1B", "B5C79E1A7272144D",
- [ [ "Echelon", "demo key", "" ],
- [ "Echo Test", "demo key", "[email protected]" ],
- [ "Eve", "demo key", "" ] ], 1 ],
- [ "56D33268F7FE693FBB594762D4BF57F37372E243", "0A32EE79EE45198E",
- [ [ "Foxtrot Test", "demo key", "[email protected]" ] ], 1 ],
- [ "C9C07DCC6621B9FB8D071B1D168410A48FC282E6", "247491CC9DCAD354",
- [ [ "Golf Test", "demo key", "[email protected]" ] ], 1 ],
- [ "9E91CBB11E4D4135583EF90513DB965534C6E3F1", "76E26537D622AD0A",
- [ [ "Hotel Test", "demo key", "[email protected]" ] ], 1 ],
- [ "CD538D6CC9FB3D745ECDA5201FE8FC6F04259677", "C1C8EFDE61F76C73",
- [ [ "India Test", "demo key", "[email protected]" ] ], 1 ],
- [ "F8F1EDC73995AB739AD54B380C820C71D2699313", "BD0B108735F8F136",
- [ [ "Juliet Test", "demo key", "[email protected]" ] ], 1 ],
- [ "3FD11083779196C2ECDD9594AD1B0FAD43C2D0C7", "86CBB34A9AF64D02",
- [ [ "Kilo Test", "demo key", "[email protected]" ] ], 1 ],
- [ "1DDD28CEF714F5B03B8C246937CAB51FB79103F8", "0363B449FE56350C",
- [ [ "Lima Test", "demo key", "[email protected]" ] ], 1 ],
- [ "2686AA191A278013992C72EBBE794852BE5CF886", "5F600A834F31EAE8",
- [ [ "Mallory", "demo key", "" ],
- [ "Mike Test", "demo key", "[email protected]" ] ], 1 ],
- [ "5AB9D6D7BAA1C95B3BAA3D9425B00FD430CEC684", "4C1D63308B70E472",
- [ [ "November Test", "demo key", "[email protected]" ] ], 1 ],
- [ "43929E89F8F79381678CAE515F6356BA6D9732AC", "FF0785712681619F",
- [ [ "Oscar Test", "demo key", "[email protected]" ] ], 1 ],
- [ "6FAA9C201E5E26DCBAEC39FD5D15E01D3FF13206", "2764E18263330D9C",
- [ [ "Papa test", "demo key", "[email protected]" ] ], 1 ],
- [ "A7969DA1C3297AA96D49843F1C67EC133C661C84", "6CDCFC44A029ACF4",
- [ [ "Quebec Test", "demo key", "[email protected]" ] ], 1 ],
- [ "38FBE1E4BF6A5E1242C8F6A13BDBEDB1777FBED3", "9FAB805A11D102EA",
- [ [ "Romeo Test", "demo key", "[email protected]" ] ], 1 ],
- [ "045B2334ADD69FC221076841A5E67F7FA3AE3EA1", "93B88B0F0F1B50B4",
- [ [ "Sierra Test", "demo key", "[email protected]" ] ], 1 ],
- [ "ECAC774F4EEEB0620767044A58CB9A4C85A81F38", "97B60E01101C0402",
- [ [ "Tango Test", "demo key", "[email protected]" ] ], 1 ],
- [ "0DBCAD3F08843B9557C6C4D4A94C0F75653244D6", "93079B915522BDB9",
- [ [ "Uniform Test", "demo key", "[email protected]" ] ], 1 ],
- [ "E8143C489C8D41124DC40D0B47AF4B6961F04784", "04071FB807287134",
- [ [ "Victor Test", "demo key", "[email protected]" ] ], 1 ],
- [ "E8D6C90B683B0982BD557A99DEF0F7B8EC67DBDE", "D7FBB421FD6E27F6",
- [ [ "Whisky Test", "demo key", "[email protected]" ] ], 3,
- check_whisky ],
- [ "04C1DF62EFA0EBB00519B06A8979A6C5567FB34A", "5CC6F87F41E408BE",
- [ [ "XRay Test", "demo key", "[email protected]" ] ], 1 ],
- [ "ED9B316F78644A58D042655A9EEF34CD4B11B25F", "5ADFD255F7B080AD",
- [ [ "Yankee Test", "demo key", "[email protected]" ] ], 1 ],
- [ "23FD347A419429BACCD5E72D6BC4778054ACD246", "EF9DC276A172C881",
- [ [ "Zulu Test", "demo key", "[email protected]" ] ], 1 ],
+ [
+ "A0FF4590BB6122EDEF6E3C542D727CC768697734", "6AE6D7EE46A871F8",
+ [["Alfa Test", "demo key",
+ "[email protected]"], ["Alpha Test", "demo key", "[email protected]"],
+ ["Alice", "demo key", ""]], 1
+ ],
+ [
+ "D695676BDCEDCC2CDD6152BCFE180B1DA9E3B0B2", "5381EA4EE29BA37F",
+ [["Bob", "demo key", ""],
+ ["Bravo Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "61EE841A2A27EB983B3B3C26413F4AF31AFDAB6C", "E71E72ACBC43DA60",
+ [["Charlie Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "6560C59C43D031C54D7C588EEBA9F240EB9DC9E6", "06F22880B0C45424",
+ [["Delta Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "3531152DE293E26A07F504BC318C1FAEFAEF6D1B", "B5C79E1A7272144D",
+ [["Echelon", "demo key",
+ ""], ["Echo Test", "demo key", "[email protected]"],
+ ["Eve", "demo key", ""]], 1
+ ],
+ [
+ "56D33268F7FE693FBB594762D4BF57F37372E243", "0A32EE79EE45198E",
+ [["Foxtrot Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "C9C07DCC6621B9FB8D071B1D168410A48FC282E6", "247491CC9DCAD354",
+ [["Golf Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "9E91CBB11E4D4135583EF90513DB965534C6E3F1", "76E26537D622AD0A",
+ [["Hotel Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "CD538D6CC9FB3D745ECDA5201FE8FC6F04259677", "C1C8EFDE61F76C73",
+ [["India Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "F8F1EDC73995AB739AD54B380C820C71D2699313", "BD0B108735F8F136",
+ [["Juliet Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "3FD11083779196C2ECDD9594AD1B0FAD43C2D0C7", "86CBB34A9AF64D02",
+ [["Kilo Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "1DDD28CEF714F5B03B8C246937CAB51FB79103F8", "0363B449FE56350C",
+ [["Lima Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "2686AA191A278013992C72EBBE794852BE5CF886", "5F600A834F31EAE8",
+ [["Mallory", "demo key", ""],
+ ["Mike Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "5AB9D6D7BAA1C95B3BAA3D9425B00FD430CEC684", "4C1D63308B70E472",
+ [["November Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "43929E89F8F79381678CAE515F6356BA6D9732AC", "FF0785712681619F",
+ [["Oscar Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "6FAA9C201E5E26DCBAEC39FD5D15E01D3FF13206", "2764E18263330D9C",
+ [["Papa test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "A7969DA1C3297AA96D49843F1C67EC133C661C84", "6CDCFC44A029ACF4",
+ [["Quebec Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "38FBE1E4BF6A5E1242C8F6A13BDBEDB1777FBED3", "9FAB805A11D102EA",
+ [["Romeo Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "045B2334ADD69FC221076841A5E67F7FA3AE3EA1", "93B88B0F0F1B50B4",
+ [["Sierra Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "ECAC774F4EEEB0620767044A58CB9A4C85A81F38", "97B60E01101C0402",
+ [["Tango Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "0DBCAD3F08843B9557C6C4D4A94C0F75653244D6", "93079B915522BDB9",
+ [["Uniform Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "E8143C489C8D41124DC40D0B47AF4B6961F04784", "04071FB807287134",
+ [["Victor Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "E8D6C90B683B0982BD557A99DEF0F7B8EC67DBDE", "D7FBB421FD6E27F6",
+ [["Whisky Test", "demo key", "[email protected]"]], 3, check_whisky
+ ],
+ [
+ "04C1DF62EFA0EBB00519B06A8979A6C5567FB34A", "5CC6F87F41E408BE",
+ [["XRay Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "ED9B316F78644A58D042655A9EEF34CD4B11B25F", "5ADFD255F7B080AD",
+ [["Yankee Test", "demo key", "[email protected]"]], 1
+ ],
+ [
+ "23FD347A419429BACCD5E72D6BC4778054ACD246", "EF9DC276A172C881",
+ [["Zulu Test", "demo key", "[email protected]"]], 1
+ ],
]
+
def check_global(key, uids, n_subkeys):
assert not key.revoked, "Key unexpectedly revoked"
assert not key.expired, "Key unexpectedly expired"
@@ -107,25 +162,25 @@ def check_global(key, uids, n_subkeys):
assert key.can_sign, "Key unexpectedly unusable for signing"
assert key.can_certify, "Key unexpectedly unusable for certifications"
assert not key.secret, "Key unexpectedly secret"
- assert not key.protocol != gpg.constants.protocol.OpenPGP, \
- "Key has unexpected protocol: {}".format(key.protocol)
- assert not key.issuer_serial, \
- "Key unexpectedly carries issuer serial: {}".format(key.issuer_serial)
- assert not key.issuer_name, \
- "Key unexpectedly carries issuer name: {}".format(key.issuer_name)
- assert not key.chain_id, \
- "Key unexpectedly carries chain ID: {}".format(key.chain_id)
+ assert not key.protocol != gpg.constants.protocol.OpenPGP,
+ "Key has unexpected protocol: {}".format(key.protocol)
+ assert not key.issuer_serial,
+ "Key unexpectedly carries issuer serial: {}".format(key.issuer_serial)
+ assert not key.issuer_name,
+ "Key unexpectedly carries issuer name: {}".format(key.issuer_name)
+ assert not key.chain_id,
+ "Key unexpectedly carries chain ID: {}".format(key.chain_id)
# Only key Alfa is trusted
- assert key.uids[0].name == 'Alfa Test' \
- or key.owner_trust == gpg.constants.validity.UNKNOWN, \
- "Key has unexpected owner trust: {}".format(key.owner_trust)
- assert key.uids[0].name != 'Alfa Test' \
- or key.owner_trust == gpg.constants.validity.ULTIMATE, \
- "Key has unexpected owner trust: {}".format(key.owner_trust)
+ assert key.uids[0].name == 'Alfa Test' or
+ key.owner_trust == gpg.constants.validity.UNKNOWN,
+ "Key has unexpected owner trust: {}".format(key.owner_trust)
+ assert key.uids[0].name != 'Alfa Test' or key.owner_trust == gpg.constants.
+ validity.ULTIMATE, "Key has unexpected owner trust: {}".
+ format(key.owner_trust)
- assert len(key.subkeys) - 1 == n_subkeys, \
- "Key `{}' has unexpected number of subkeys".format(uids[0][0])
+ assert len(key.subkeys) - 1 == n_subkeys,
+ "Key `{}' has unexpected number of subkeys".format(uids[0][0])
def check_subkey(fpr, which, subkey):
@@ -152,18 +207,19 @@ def check_subkey(fpr, which, subkey):
assert not subkey.secret, which + " key unexpectedly secret"
assert not subkey.is_cardkey, "Public key marked as card key"
assert not subkey.card_number, "Public key with card number set"
- assert not subkey.pubkey_algo != (gpg.constants.pk.DSA if which == "Primary"
- else gpg.constants.pk.ELG_E), \
- which + " key has unexpected public key algo: {}".\
- format(subkey.pubkey_algo)
- assert subkey.length == 1024, \
- which + " key has unexpected length: {}".format(subkey.length)
- assert fpr.endswith(subkey.keyid), \
- which + " key has unexpected key ID: {}".format(subkey.keyid)
- assert which == "Secondary" or subkey.fpr == fpr, \
- which + " key has unexpected fingerprint: {}".format(subkey.fpr)
- assert not subkey.expires, \
- which + " key unexpectedly expires: {}".format(subkey.expires)
+ assert not subkey.pubkey_algo !=
+ (gpg.constants.pk.DSA if which == "Primary" else gpg.constants.pk.ELG_E),
+ which + " key has unexpected public key algo: {}".format(subkey.
+ pubkey_algo)
+ assert subkey.length == 1024,
+ which + " key has unexpected length: {}".format(subkey.length)
+ assert fpr.endswith(subkey.keyid),
+ which + " key has unexpected key ID: {}".format(subkey.keyid)
+ assert which == "Secondary" or subkey.fpr == fpr,
+ which + " key has unexpected fingerprint: {}".format(subkey.fpr)
+ assert not subkey.expires,
+ which + " key unexpectedly expires: {}".format(subkey.expires)
+
def check_uid(which, ref, uid):
assert not uid.revoked, which + " user ID unexpectedly revoked"
@@ -171,20 +227,21 @@ def check_uid(which, ref, uid):
assert uid.validity == (gpg.constants.validity.UNKNOWN
if uid.name.split()[0]
not in {'Alfa', 'Alpha', 'Alice'} else
- gpg.constants.validity.ULTIMATE), \
- which + " user ID has unexpectedly validity: {}".format(uid.validity)
+ gpg.constants.validity.ULTIMATE),
+ which + " user ID has unexpectedly validity: {}".format(uid.validity)
assert not uid.signatures, which + " user ID unexpectedly signed"
- assert uid.name == ref[0], \
- "Unexpected name in {} user ID: {!r}".format(which.lower(), uid.name)
- assert uid.comment == ref[1], \
- "Unexpected comment in {} user ID: {!r}".format(which.lower(),
- uid.comment)
- assert uid.email == ref[2], \
- "Unexpected email in {} user ID: {!r}".format(which.lower(), uid.email)
+ assert uid.name == ref[0],
+ "Unexpected name in {} user ID: {!r}".format(which.lower(), uid.name)
+ assert uid.comment == ref[1],
+ "Unexpected comment in {} user ID: {!r}".format(which.lower(),
+ uid.comment)
+ assert uid.email == ref[2],
+ "Unexpected email in {} user ID: {!r}".format(which.lower(), uid.email)
+
i = 0
c.op_keylist_start(None, False)
-key = c.op_keylist_next ()
+key = c.op_keylist_next()
while key:
try:
if len(keys[i]) == 4:
@@ -204,20 +261,19 @@ while key:
assert len(key.uids) == len(uids)
check_uid("First", uids[0], key.uids[0])
if len(key.uids) > 1:
- check_uid("Second", uids[1], key.uids[1])
+ check_uid("Second", uids[1], key.uids[1])
if len(key.uids) > 2:
- check_uid("Third", uids[2], key.uids[2])
+ check_uid("Third", uids[2], key.uids[2])
if misc_check:
- misc_check (uids[0][0], key)
- key = c.op_keylist_next ()
+ misc_check(uids[0][0], key)
+ key = c.op_keylist_next()
i += 1
c.op_keylist_end()
result = c.op_keylist_result()
assert not result.truncated, "Key listing unexpectedly truncated"
-
# We test for a parameter-less keylist
keyring_length = len(list(c.op_keylist_all()))
assert keyring_length > 1,\
@@ -226,13 +282,12 @@ assert keyring_length > 1,\
# Then we do want to call with a pattern, only
# i.e. without giving secret=0
alpha_keys = list(c.op_keylist_all(b"Alpha"))
-assert len(alpha_keys) == 1, "Expected only one key for 'Alpha', got %r" % len(alpha_keys)
-
+assert len(alpha_keys) == 1, "Expected only one key for 'Alpha', got %r" % len(
+ alpha_keys)
# Check negative result.
assert len(list(c.keylist("no such key in sight"))) == 0
-
for i, key in enumerate(c.keylist()):
try:
if len(keys[i]) == 4:
@@ -252,31 +307,30 @@ for i, key in enumerate(c.keylist()):
assert len(key.uids) == len(uids)
check_uid("First", uids[0], key.uids[0])
if len(key.uids) > 1:
- check_uid("Second", uids[1], key.uids[1])
+ check_uid("Second", uids[1], key.uids[1])
if len(key.uids) > 2:
- check_uid("Third", uids[2], key.uids[2])
+ check_uid("Third", uids[2], key.uids[2])
if misc_check:
- misc_check (uids[0][0], key)
-
+ misc_check(uids[0][0], key)
# check get_key()
with gpg.Context() as c:
- c.get_key(support.alpha)
- c.get_key(support.alpha, secret=True)
-
- c.get_key(support.bob)
- try:
- c.get_key(support.bob, secret=True)
- except KeyError:
- pass
- else:
- assert False, "Expected KeyError"
-
- # Legacy error
- try:
- c.get_key(support.no_such_key)
- except gpg.errors.GPGMEError:
- pass
- else:
- assert False, "Expected GPGMEError"
+ c.get_key(support.alpha)
+ c.get_key(support.alpha, secret=True)
+
+ c.get_key(support.bob)
+ try:
+ c.get_key(support.bob, secret=True)
+ except KeyError:
+ pass
+ else:
+ assert False, "Expected KeyError"
+
+ # Legacy error
+ try:
+ c.get_key(support.no_such_key)
+ except gpg.errors.GPGMEError:
+ pass
+ else:
+ assert False, "Expected GPGMEError"
diff --git a/lang/python/tests/t-protocol-assuan.py b/lang/python/tests/t-protocol-assuan.py
index 8da50351..c337c3b7 100755
--- a/lang/python/tests/t-protocol-assuan.py
+++ b/lang/python/tests/t-protocol-assuan.py
@@ -18,20 +18,21 @@
# 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 support
-_ = support # to appease pyflakes.
+_ = support # to appease pyflakes.
+
+del absolute_import, print_function, unicode_literals
with gpg.Context(protocol=gpg.constants.protocol.ASSUAN) as c:
# Do nothing.
err = c.assuan_transact('nop')
- assert err == None
+ assert err is None
err = c.assuan_transact(b'NOP')
- assert err == None
+ assert err is None
err = c.assuan_transact(['NOP'])
- assert err == None
+ assert err is None
err = c.assuan_transact('idontexist')
assert err.getsource() == gpg.errors.SOURCE_GPGAGENT
@@ -41,6 +42,7 @@ with gpg.Context(protocol=gpg.constants.protocol.ASSUAN) as c:
c.assuan_transact(['GET_CONFIRMATION', 'Hello there'])
data = []
+
def data_cb(line):
data.append(line)
@@ -57,6 +59,7 @@ with gpg.Context(protocol=gpg.constants.protocol.ASSUAN) as c:
# XXX HELP sends status lines if we could use ASSUAN_CONVEY_COMMENTS.
status = []
+
def status_cb(line, args):
status.append((line, args))
diff --git a/lang/python/tests/t-quick-key-creation.py b/lang/python/tests/t-quick-key-creation.py
index 8b7372e7..47209288 100755
--- a/lang/python/tests/t-quick-key-creation.py
+++ b/lang/python/tests/t-quick-key-creation.py
@@ -18,7 +18,6 @@
# 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
@@ -27,6 +26,8 @@ import time
import support
support.assert_gpg_version((2, 1, 2))
+del absolute_import, print_function, unicode_literals
+
alpha = "Alpha <[email protected]>"
with support.EphemeralContext() as ctx:
@@ -51,14 +52,16 @@ with support.EphemeralContext() as ctx:
res2 = ctx.create_key(alpha, force=True)
assert res.fpr != res2.fpr
-
# From here on, we use one context, and create unique UIDs
uid_counter = 0
+
+
def make_uid():
global uid_counter
uid_counter += 1
return "user{0}@invalid.example.org".format(uid_counter)
+
with support.EphemeralContext() as ctx:
# Check gpg.constants.create.NOEXPIRE...
res = ctx.create_key(make_uid(), expires=False)
@@ -77,10 +80,8 @@ with support.EphemeralContext() as ctx:
"Primary keys expiration time is off"
# Check capabilities
- for sign, encrypt, certify, authenticate in itertools.product([False, True],
- [False, True],
- [False, True],
- [False, True]):
+ for sign, encrypt, certify, authenticate in itertools.
+ product([False, True], [False, True], [False, True], [False, True]):
# Filter some out
if not (sign or encrypt or certify or authenticate):
# This triggers the default capabilities tested before.
@@ -89,9 +90,13 @@ with support.EphemeralContext() as ctx:
# The primary key always certifies.
continue
- res = ctx.create_key(make_uid(), algorithm="rsa",
- sign=sign, encrypt=encrypt, certify=certify,
- authenticate=authenticate)
+ res = ctx.create_key(
+ make_uid(),
+ algorithm="rsa",
+ sign=sign,
+ encrypt=encrypt,
+ certify=certify,
+ authenticate=authenticate)
key = ctx.get_key(res.fpr, secret=True)
assert key.fpr == res.fpr
assert len(key.subkeys) == 1, \
@@ -125,13 +130,16 @@ with support.EphemeralContext() as ctx:
recipient = make_uid()
passphrase = "streng geheim"
res = ctx.create_key(recipient, passphrase=passphrase)
- ciphertext, _, _ = ctx.encrypt(b"hello there", recipients=[ctx.get_key(res.fpr)])
+ ciphertext, _, _ = ctx.encrypt(
+ b"hello there", recipients=[ctx.get_key(res.fpr)])
cb_called = False
+
def cb(*args):
global cb_called
cb_called = True
return passphrase
+
ctx.pinentry_mode = gpg.constants.PINENTRY_MODE_LOOPBACK
ctx.set_passphrase_cb(cb)
diff --git a/lang/python/tests/t-quick-key-manipulation.py b/lang/python/tests/t-quick-key-manipulation.py
index 37e05b35..ade171e7 100755
--- a/lang/python/tests/t-quick-key-manipulation.py
+++ b/lang/python/tests/t-quick-key-manipulation.py
@@ -18,7 +18,6 @@
# 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 os
import gpg
@@ -27,6 +26,8 @@ import sys
import support
support.assert_gpg_version((2, 1, 14))
+del absolute_import, print_function, unicode_literals
+
alpha = "Alpha <[email protected]>"
bravo = "Bravo <[email protected]>"
@@ -111,9 +112,11 @@ with support.EphemeralContext() as ctx:
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)))
+ 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:
diff --git a/lang/python/tests/t-quick-key-signing.py b/lang/python/tests/t-quick-key-signing.py
index 3d648c5b..6f9b8a72 100755
--- a/lang/python/tests/t-quick-key-signing.py
+++ b/lang/python/tests/t-quick-key-signing.py
@@ -18,7 +18,6 @@
# 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
@@ -27,8 +26,11 @@ import time
import support
support.assert_gpg_version((2, 1, 1))
+del absolute_import, print_function, unicode_literals
+
with support.EphemeralContext() as ctx:
uid_counter = 0
+
def make_uid():
global uid_counter
uid_counter += 1
@@ -43,10 +45,16 @@ with support.EphemeralContext() as ctx:
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)))
+ 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}
+ 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:
@@ -76,9 +84,12 @@ with support.EphemeralContext() as ctx:
assert s.exportable
assert s.expires == 0
- check_sigs(key_b, itertools.product(uids_b, [key_b], [exportable_non_expiring]))
+ 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]))
+ 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()
@@ -89,11 +100,12 @@ with support.EphemeralContext() as ctx:
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])))
+ 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()
@@ -106,16 +118,16 @@ with support.EphemeralContext() as ctx:
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])))
+ 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])))
+ 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])))
diff --git a/lang/python/tests/t-quick-subkey-creation.py b/lang/python/tests/t-quick-subkey-creation.py
index ad4f35c6..30424c19 100755
--- a/lang/python/tests/t-quick-subkey-creation.py
+++ b/lang/python/tests/t-quick-subkey-creation.py
@@ -18,7 +18,6 @@
# 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
@@ -26,6 +25,8 @@ import time
import support
+del absolute_import, print_function, unicode_literals
+
alpha = "Alpha <[email protected]>"
bravo = "Bravo <[email protected]>"
@@ -59,16 +60,15 @@ with support.EphemeralContext() as ctx:
"subkeys expiration time is off"
# Check capabilities
- for sign, encrypt, authenticate in itertools.product([False, True],
- [False, True],
- [False, True]):
+ for sign, encrypt, authenticate in itertools.
+ product([False, True], [False, True], [False, True]):
# Filter some out
if not (sign or encrypt or authenticate):
# This triggers the default capabilities tested before.
continue
- res = ctx.create_subkey(key, sign=sign, encrypt=encrypt,
- authenticate=authenticate)
+ res = ctx.create_subkey(
+ key, sign=sign, encrypt=encrypt, authenticate=authenticate)
subkey = get_subkey(res.fpr)
assert sign == subkey.can_sign
assert encrypt == subkey.can_encrypt
@@ -92,18 +92,21 @@ with support.EphemeralContext() as ctx:
# so that we have a key with just one encryption subkey.
bravo_res = ctx.create_key(bravo, certify=True)
bravo_key = ctx.get_key(bravo_res.fpr)
- assert len(bravo_key.subkeys) == 1, "Expected one primary key and no subkeys"
+ assert len(
+ bravo_key.subkeys) == 1, "Expected one primary key and no subkeys"
passphrase = "streng geheim"
res = ctx.create_subkey(bravo_key, passphrase=passphrase)
- ciphertext, _, _ = ctx.encrypt(b"hello there",
- recipients=[ctx.get_key(bravo_res.fpr)])
+ ciphertext, _, _ = ctx.encrypt(
+ b"hello there", recipients=[ctx.get_key(bravo_res.fpr)])
cb_called = False
+
def cb(*args):
global cb_called
cb_called = True
return passphrase
+
ctx.pinentry_mode = gpg.constants.PINENTRY_MODE_LOOPBACK
ctx.set_passphrase_cb(cb)
diff --git a/lang/python/tests/t-sig-notation.py b/lang/python/tests/t-sig-notation.py
index bc8da2e6..5960f443 100755
--- a/lang/python/tests/t-sig-notation.py
+++ b/lang/python/tests/t-sig-notation.py
@@ -18,29 +18,30 @@
# 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 os
import gpg
import support
-_ = support # to appease pyflakes.
+_ = support # to appease pyflakes.
+
+del absolute_import, print_function, unicode_literals
expected_notations = {
- "laughing@me": ("Just Squeeze Me", gpg.constants.sig.notation.HUMAN_READABLE),
- "[email protected]": ("pgpmime",
- gpg.constants.sig.notation.HUMAN_READABLE
- | gpg.constants.sig.notation.CRITICAL),
+ "laughing@me": ("Just Squeeze Me",
+ gpg.constants.sig.notation.HUMAN_READABLE),
+ ("pgpmime", gpg.constants.sig.notation.HUMAN_READABLE |
+ gpg.constants.sig.notation.CRITICAL),
None: ("http://www.gnu.org/policy/", 0),
}
# GnuPG prior to 2.1.13 did not report the critical flag correctly.
with gpg.Context() as c:
version = c.engine_info.version
- have_correct_sig_data = not (version.startswith("1.")
- or version.startswith("2.0.")
- or version == "2.1.1"
- or (version.startswith("2.1.1")
- and version[5] < '3'))
+ have_correct_sig_data = not (
+ version.startswith("1.") or version.startswith("2.0.") or
+ (version.startswith("2.1.") and int(version[4:]) < 13))
+
def check_result(result):
assert len(result.signatures) == 1, "Unexpected number of signatures"
@@ -48,8 +49,8 @@ def check_result(result):
assert len(sig.notations) == len(expected_notations)
for r in sig.notations:
- assert not 'name_len' in dir(r)
- assert not 'value_len' in dir(r)
+ assert 'name_len' not in dir(r)
+ assert 'value_len' not in dir(r)
assert r.name in expected_notations
value, flags = expected_notations.pop(r.name)
@@ -63,6 +64,7 @@ def check_result(result):
assert len(expected_notations) == 0
+
source = gpg.Data("Hallo Leute\n")
signed = gpg.Data()
diff --git a/lang/python/tests/t-sign.py b/lang/python/tests/t-sign.py
index d3757294..3ad05e8e 100755
--- a/lang/python/tests/t-sign.py
+++ b/lang/python/tests/t-sign.py
@@ -18,15 +18,18 @@
# 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 os
import gpg
import support
+del absolute_import, print_function, unicode_literals
+
+
def fail(msg):
raise RuntimeError(msg)
+
def check_result(r, typ):
if r.invalid_signers:
fail("Invalid signer found: {}".format(r.invalid_signers.fpr))
@@ -43,16 +46,15 @@ def check_result(r, typ):
signature.pubkey_algo))
if signature.hash_algo != gpg.constants.md.SHA1:
- fail("Wrong hash algorithm reported: {}".format(
- signature.hash_algo))
+ fail("Wrong hash algorithm reported: {}".format(signature.hash_algo))
if signature.sig_class != 1:
- fail("Wrong signature class reported: {}".format(
- signature.sig_class))
+ fail("Wrong signature class reported: {}".format(signature.sig_class))
if signature.fpr != "A0FF4590BB6122EDEF6E3C542D727CC768697734":
fail("Wrong fingerprint reported: {}".format(signature.fpr))
+
c = gpg.Context()
c.set_textmode(True)
c.set_armor(True)
diff --git a/lang/python/tests/t-signers.py b/lang/python/tests/t-signers.py
index 5864ee5f..119ab773 100755
--- a/lang/python/tests/t-signers.py
+++ b/lang/python/tests/t-signers.py
@@ -18,14 +18,17 @@
# 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 support
+del absolute_import, print_function, unicode_literals
+
+
def fail(msg):
raise RuntimeError(msg)
+
def check_result(r, typ):
if r.invalid_signers:
fail("Invalid signer found: {}".format(r.invalid_signers.fpr))
@@ -53,6 +56,7 @@ def check_result(r, typ):
"23FD347A419429BACCD5E72D6BC4778054ACD246"):
fail("Wrong fingerprint reported: {}".format(signature.fpr))
+
c = gpg.Context()
c.set_textmode(True)
c.set_armor(True)
diff --git a/lang/python/tests/t-trustlist.py b/lang/python/tests/t-trustlist.py
index 89524bb5..ffa0b96d 100755
--- a/lang/python/tests/t-trustlist.py
+++ b/lang/python/tests/t-trustlist.py
@@ -18,18 +18,21 @@
# 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 support
-_ = support # to appease pyflakes.
+_ = support # to appease pyflakes.
+
+del absolute_import, print_function, unicode_literals
c = gpg.Context()
+
def dump_item(item):
- print("l={} k={} t={} o={} v={} u={}".format(
- item.level, item.keyid, item.type, item.owner_trust,
- item.validity, item.name))
+ print("l={} k={} t={} o={} v={} u={}".format(item.level, item.keyid,
+ item.type, item.owner_trust,
+ item.validity, item.name))
+
c.op_trustlist_start("alice", 0)
while True:
diff --git a/lang/python/tests/t-verify.py b/lang/python/tests/t-verify.py
index 320dae66..70a6c1cb 100755
--- a/lang/python/tests/t-verify.py
+++ b/lang/python/tests/t-verify.py
@@ -18,16 +18,17 @@
# 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 sys
import os
import gpg
import support
-_ = support # to appease pyflakes.
+_ = support # to appease pyflakes.
+
+del absolute_import, print_function, unicode_literals
test_text1 = b"Just GNU it!\n"
-test_text1f= b"Just GNU it?\n"
+test_text1f = b"Just GNU it?\n"
test_sig1 = b"""-----BEGIN PGP SIGNATURE-----
iN0EABECAJ0FAjoS+i9FFIAAAAAAAwA5YmFyw7bDpMO8w58gZGFzIHdhcmVuIFVt
@@ -60,6 +61,7 @@ UqVooWlGXHwNw/xg/fVzt9VNbtjtJ/fhUqYo0/LyCGEA
-----END PGP MESSAGE-----
"""
+
def check_result(result, summary, validity, fpr, status, notation):
assert len(result.signatures) == 1, "Unexpected number of signatures"
sig = result.signatures[0]
@@ -76,14 +78,16 @@ def check_result(result, summary, validity, fpr, status, notation):
if sys.version_info[0] < 3 else
b"\xc3\xb6\xc3\xa4\xc3\xbc\xc3\x9f".decode() +
" das waren Umlaute und jetzt ein prozent%-Zeichen"),
- "foobar.1": "this is a notation data with 2 lines",
- None: "http://www.gu.org/policy/",
+ "foobar.1":
+ "this is a notation data with 2 lines",
+ None:
+ "http://www.gu.org/policy/",
}
assert len(sig.notations) == len(expected_notations)
for r in sig.notations:
- assert not 'name_len' in dir(r)
- assert not 'value_len' in dir(r)
+ assert 'name_len' not in dir(r)
+ assert 'value_len' not in dir(r)
assert r.name in expected_notations
assert r.value == expected_notations[r.name], \
"Expected {!r}, got {!r}".format(expected_notations[r.name],
@@ -96,7 +100,9 @@ def check_result(result, summary, validity, fpr, status, notation):
assert sig.validity == validity, \
"Unexpected signature validity: {}, want: {}".format(
sig.validity, validity)
- assert gpg.errors.GPGMEError(sig.validity_reason).getcode() == gpg.errors.NO_ERROR
+ assert gpg.errors.GPGMEError(
+ sig.validity_reason).getcode() == gpg.errors.NO_ERROR
+
c = gpg.Context()
c.set_armor(True)
@@ -108,9 +114,8 @@ c.op_verify(sig, text, None)
result = c.op_verify_result()
check_result(result, gpg.constants.sigsum.VALID | gpg.constants.sigsum.GREEN,
gpg.constants.validity.FULL,
- "A0FF4590BB6122EDEF6E3C542D727CC768697734",
- gpg.errors.NO_ERROR, True)
-
+ "A0FF4590BB6122EDEF6E3C542D727CC768697734", gpg.errors.NO_ERROR,
+ True)
# Checking a manipulated message.
text = gpg.Data(test_text1f)
@@ -127,8 +132,8 @@ c.op_verify(sig, None, text)
result = c.op_verify_result()
check_result(result, gpg.constants.sigsum.VALID | gpg.constants.sigsum.GREEN,
gpg.constants.validity.FULL,
- "A0FF4590BB6122EDEF6E3C542D727CC768697734",
- gpg.errors.NO_ERROR, False)
+ "A0FF4590BB6122EDEF6E3C542D727CC768697734", gpg.errors.NO_ERROR,
+ False)
# Checking an invalid message.
text = gpg.Data()
@@ -141,33 +146,32 @@ except Exception as e:
else:
assert False, "Expected an error but got none."
-
# Idiomatic interface.
with gpg.Context(armor=True) as c:
# Checking a valid message.
_, result = c.verify(test_text1, test_sig1)
- check_result(result, gpg.constants.sigsum.VALID | gpg.constants.sigsum.GREEN,
- gpg.constants.validity.FULL,
- "A0FF4590BB6122EDEF6E3C542D727CC768697734",
- gpg.errors.NO_ERROR, True)
+ check_result(
+ result, gpg.constants.sigsum.VALID | gpg.constants.sigsum.GREEN,
+ gpg.constants.validity.FULL,
+ "A0FF4590BB6122EDEF6E3C542D727CC768697734", gpg.errors.NO_ERROR, True)
# Checking a manipulated message.
try:
c.verify(test_text1f, test_sig1)
except gpg.errors.BadSignatures as e:
check_result(e.result, gpg.constants.sigsum.RED,
- gpg.constants.validity.UNKNOWN,
- "2D727CC768697734", gpg.errors.BAD_SIGNATURE, False)
+ gpg.constants.validity.UNKNOWN, "2D727CC768697734",
+ gpg.errors.BAD_SIGNATURE, False)
else:
assert False, "Expected an error but got none."
# Checking a normal signature.
sig = gpg.Data(test_sig2)
data, result = c.verify(test_sig2)
- check_result(result, gpg.constants.sigsum.VALID | gpg.constants.sigsum.GREEN,
- gpg.constants.validity.FULL,
- "A0FF4590BB6122EDEF6E3C542D727CC768697734",
- gpg.errors.NO_ERROR, False)
+ check_result(
+ result, gpg.constants.sigsum.VALID | gpg.constants.sigsum.GREEN,
+ gpg.constants.validity.FULL,
+ "A0FF4590BB6122EDEF6E3C542D727CC768697734", gpg.errors.NO_ERROR, False)
assert data == test_text1
# Checking an invalid message.
diff --git a/lang/python/tests/t-wait.py b/lang/python/tests/t-wait.py
index 31013011..907f4504 100755
--- a/lang/python/tests/t-wait.py
+++ b/lang/python/tests/t-wait.py
@@ -18,12 +18,13 @@
# 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 time
import gpg
import support
-_ = support # to appease pyflakes.
+_ = support # to appease pyflakes.
+
+del absolute_import, print_function, unicode_literals
c = gpg.Context()
c.set_armor(True)
diff --git a/lang/python/tests/t-wrapper.py b/lang/python/tests/t-wrapper.py
index 08a320d2..020e71e5 100755
--- a/lang/python/tests/t-wrapper.py
+++ b/lang/python/tests/t-wrapper.py
@@ -19,9 +19,9 @@
import gpg
import support
-_ = support # to appease pyflakes.
+_ = support # to appease pyflakes.
d0 = gpg.Data()
-d0.seek # trigger on-demand-wrapping
+d0.seek # trigger on-demand-wrapping
assert d0.seek == d0.seek, "Generated wrapper functions are not cached"
assert hasattr(gpg.Data, 'seek'), "Generated wrapper functions are not shared"
diff --git a/lang/python/version.py.in b/lang/python/version.py.in
index 1a1baf08..ad76edab 100644
--- a/lang/python/version.py.in
+++ b/lang/python/version.py.in
@@ -1,4 +1,6 @@
-# Copyright (C) 2016 g10 Code GmbH
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2016-2018 g10 Code GmbH
# Copyright (C) 2015 Ben McGinnes <[email protected]>
# Copyright (C) 2004 Igor Belyi <[email protected]>
#
@@ -17,10 +19,11 @@
# 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
+del absolute_import, print_function
+
productname = 'gpg'
versionstr = "@VERSION@"
gpgme_versionstr = gpgme.GPGME_VERSION
@@ -32,8 +35,8 @@ minor = versionlist[1]
patch = versionlist[2]
copyright = """\
-Copyright (C) 2016 g10 Code GmbH
-Copyright (C) 2015 Ben McGinnes
+Copyright (C) 2016-2018 g10 Code GmbH
+Copyright (C) 2015 Benjamin D. McGinnes
Copyright (C) 2014-2015 Martin Albrecht
Copyright (C) 2004-2008 Igor Belyi
Copyright (C) 2002 John Goerzen"""
@@ -44,8 +47,8 @@ author_email = "[email protected]"
description = "Python support for GPGME GnuPG cryptography library"
homepage = "https://gnupg.org"
-license = """Copyright (C) 2016 g10 Code GmbH
-Copyright (C) 2015 Ben McGinnes <[email protected]>
+license = """Copyright (C) 2016-2018 g10 Code GmbH
+Copyright (C) 2015 Benjamin D. McGinnes <[email protected]>
Copyright (C) 2014, 2015 Martin Albrecht <[email protected]>
Copyright (C) 2004, 2008 Igor Belyi <[email protected]>
Copyright (C) 2002 John Goerzen <[email protected]>