aboutsummaryrefslogtreecommitdiffstats
path: root/lang/python/docs/GPGMEpythonHOWTOen.org
diff options
context:
space:
mode:
Diffstat (limited to 'lang/python/docs/GPGMEpythonHOWTOen.org')
-rw-r--r--lang/python/docs/GPGMEpythonHOWTOen.org298
1 files changed, 289 insertions, 9 deletions
diff --git a/lang/python/docs/GPGMEpythonHOWTOen.org b/lang/python/docs/GPGMEpythonHOWTOen.org
index a712ec27..84ba6a35 100644
--- a/lang/python/docs/GPGMEpythonHOWTOen.org
+++ b/lang/python/docs/GPGMEpythonHOWTOen.org
@@ -15,7 +15,7 @@
:CUSTOM_ID: intro
:END:
-| Version: | 0.1.3 |
+| Version: | 0.1.4 |
| Author: | Ben McGinnes <[email protected]> |
| Author GPG Key: | DB4724E6FA4286C92B4E55C4321E4E2373590E5D |
| Language: | Australian English, British English |
@@ -207,6 +207,9 @@ 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.
+See the additional notes regarding CFFI and SWIG at the end of this
+section for further details.
+
** Requirements
:PROPERTIES:
@@ -217,7 +220,7 @@ 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.
+2. [[https://www.swig.org][SWIG]].
3. GPGME itself. Which also means that all of GPGME's dependencies
must be installed too.
@@ -243,7 +246,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=, =python3.4= and =python3.7=.[fn:4]
+=python3.6=, =python3.5=, =python3.4= and =python3.7=.[fn:3]
*** Installing GPGME
@@ -255,6 +258,181 @@ See the GPGME =README= file for details of how to install GPGME from
source.
+** Known Issues
+ :PROPERTIES:
+ :CUSTOM_ID: snafu
+ :END:
+
+There are a few known issues with the current build process and the
+Python bindings. For the most part these are easily addressed should
+they be encountered.
+
+
+*** Breaking Builds
+ :PROPERTIES:
+ :CUSTOM_ID: snafu-a-swig-of-this-builds-character
+ :END:
+
+Occasionally when installing GPGME with the Python bindings included
+it may be observed that the =make= portion of that process induces a
+large very number of warnings and, eventually errors which end that
+part of the build process. Yet following that with =make check= and
+=make install= appears to work seamlessly.
+
+The cause of this is related to the way SWIG needs to be called to
+dynamically generate the C bindings for GPGME in the first place. So
+the entire process will always produce =lang/python/python2-gpg/= and
+=lang/python/python3-gpg/= directories. These should contain the
+build output generated during compilation, including the complete
+bindings and module installed into =site-packages=.
+
+Occasionally the errors in the early part or some other conflict
+(e.g. not installing as */root/* or */su/*) may result in nothing
+being installed to the relevant =site-packages= directory and the
+build directory missing a lot of expected files. Even when this
+occurs, the solution is actually quite simple and will always work.
+
+That solution is simply to run the following commands as either the
+*root* user or prepended with =sudo -H=[fn:4] in the =lang/python/=
+directory:
+
+#+BEGIN_SRC shell
+ /path/to/pythonX.Y setup.py build
+ /path/to/pythonX.Y setup.py build
+ /path/to/pythonX.Y setup.py install
+#+END_SRC
+
+Yes, the build command does need to be run twice. Yes, you still need
+to run the potentially failing or incomplete steps during the
+=configure=, =make= and =make install= steps with installing GPGME.
+This is because those steps generate a lot of essential files needed,
+both by and in order to create, the bindings (including both the
+=setup.py= and =gpgme.h= files).
+
+
+**** IMPORTANT Note
+ :PROPERTIES:
+ :CUSTOM_ID: snafu-swig-build-note
+ :END:
+
+If specifying a selected number of languages to create bindings for,
+always leave Python last. Currently the other languages are also
+preceding Python of either version when listed alphabetically and so
+that just happens by default currently. If Python is set to precede
+one of the other languages then it is possible that the errors
+described here may interrupt the build process before generating
+bindings for those other languages.
+
+
+*** Multiple installations
+ :PROPERTIES:
+ :CUSTOM_ID: snafu-the-full-monty
+ :END:
+
+For a veriety of reasons it may be either necessary or just preferable
+to install the bindings to alternative installed Python versions which
+meet the requirements of these bindings.
+
+On POSIX systens this will generally be most simply achieved by
+running the manual installation commands (build, build, install) as
+described in the previous section for each Python installation the
+bindings need to be installed to.
+
+As per the SWIG documentation: the compilers, libraries and runtime
+used to build GPGME and the Python Bindings *must* match those used to
+compile Python itself, including the version number(s) (at least going
+by major version numbers and probably minor numbers too).
+
+On most POSIX systems, including OS X, this will very likely be the
+case in most, if not all, cases.
+
+
+*** Won't Work With Windows
+ :PROPERTIES:
+ :CUSTOM_ID: snafu-runtime-not-funtime
+ :END:
+
+There are semi-regular reports of Windows users having considerable
+difficulty in installing and using the Python bindings at all. Very
+often, possibly even always, these reports come from Cygwin users
+and/or MinGW users and/or Msys2 users. Though not all of them have
+been confirmed, it appears that these reports have also come from
+people who installed Python using the Windows installer files from the
+[[https://python.org][Python website]] (i.e. mostly MSI installers, sometimes self-extracting
+=.exe= files).
+
+The Windows versions of Python are not built using Cygwin, MinGW or
+Msys2; they're built using Microsoft Visual Studio. Furthermore the
+version used is /considerably/ more advanced than the version which
+MinGWobtained a small number of files from many years ago in order to
+be able to compile anything at all. Not only that, but there are
+changes to the version of Visual Studio between some micro releases,
+though that is is particularly the case with Python 2.7, since it has
+been kept around far longer than it should have been.
+
+There are two theoretical solutions to this issue:
+
+ 1. Compile and install the GnuPG stack, including GPGME and the
+ Python bibdings using the same version of Microsoft Visual Studio
+ used by the Python Foundation to compile the version of Python
+ installed.
+
+ If there are multiple versions of Python then this will need to be
+ done with each different version of Visual Studio used.
+
+ 2. Compile and install Python using the same tools used by choice,
+ such as MinGW or Msys2.
+
+Do *NOT* use the official Windows installer for Python unless
+following the first method.
+
+In this type of situation it may even be for the best to accept that
+there are less limitations on permissive software than free software
+and simply opt to use a recent version of the Community Edition of
+Microsoft Visual Studio to compile and build all of it, no matter
+what.
+
+Investigations into the extent or the limitations of this issue are
+ongoing.
+
+
+*** I don't like SWIG, Use CFFI instead
+ :PROPERTIES:
+ :CUSTOM_ID: snafu-foad
+ :END:
+
+There are many reasons for favouring [[https://cffi.readthedocs.io/en/latest/overview.html][CFFI]] and proponents of it are
+quite happy to repeat these things as if all it would take to switch
+from SWIG to CFFI is repeating that list as if it were a new concept.
+
+The fact is that there are things which Python's CFFI implementation
+cannot handle in the GPGME C code. Beyond that there are features of
+SWIG which are simply not available with CFFI at all. SWIG generates
+the bindings to Python using the =gpgme.h= file, but that file is not
+a single version shipped with each release, it too is generated when
+GPGME is compiled.
+
+CFFI is currently unable to adapt to such a potentially mutable
+codebase. If there were some means of applying SWIG's dynamic code
+generation to produce CFFI API modes of accessing the GPGME libraries
+(or the source source code directly), but such a thing does not exist
+yet either and it currently appears that work is needed in at least
+one of CFFI's dependencies before any of this can be addressed.
+
+So if you're a massive fan of CFFI; that's great, but if you want this
+project to switch to CFFI then rather than just insisting that it
+should, I'd suggest you volunteer to bring CFFI up to the level this
+project needs.
+
+If you're actually seriously considering doing so, then I'd suggest
+taking the =gpgme-tool.c= file in the GPGME =src/= directory and
+getting that to work with any of the CFFI API methods (not the ABI
+methods, they'll work with pretty much anything). When you start
+running into trouble with "ifdefs" then you'll know what sort of
+things are lacking. That doesn't even take into account the amount of
+work saved via SWIG's code generation techniques either.
+
+
* Fundamentals
:PROPERTIES:
:CUSTOM_ID: howto-fund-a-mental
@@ -513,6 +691,100 @@ relative ease by which such key IDs can be reproduced, as demonstrated
by the Evil32 Project in 2014 (which was subsequently exploited in
2016).
+Here is a variation on the above which checks the constrained
+ProtonMail keyserver for ProtonMail public keys.
+
+#+BEGIN_SRC python -i
+import gpg
+import requests
+import sys
+
+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)
+#+END_SRC
+
** Exporting keys
:PROPERTIES:
@@ -906,7 +1178,7 @@ 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
+email address on the =gnupg.org= domain,[fn:5] but does /not/ encrypt
to a default key or other key which is configured to normally encrypt
to.
@@ -1756,11 +2028,19 @@ PURPOSE.
[fn:2] The =lang/python/docs/= directory in the GPGME source.
-[fn:3] You probably don't really want to do this. Searching the
+[fn:3] 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.
+
+[fn:4] Yes, even if you use virtualenv with everything you do in
+Python. If you want to install this module as just your user account
+then you will need to manually configure, compile and install the
+/entire/ GnuPG stack as that user as well. This includes libraries
+which are not often installed that way. It can be done and there are
+circumstances under which it is worthwhile, but generally only on
+POSIX systems which utilise single user mode (some even require it).
+
+[fn:5] You probably don't really want to do this. Searching the
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.