0c1239b9858d93324a19e47ffc544bd885c90b98
[gpgme.git] / lang / python / doc / src / gpgme-python-howto
1 # -*- mode: org -*-
2 #+TITLE: GNU Privacy Guard (GnuPG) Made Easy Python Bindings HOWTO (English)
3 #+AUTHOR: Ben McGinnes
4 #+LATEX_COMPILER: xelatex
5 #+LATEX_CLASS: article
6 #+LATEX_CLASS_OPTIONS: [12pt]
7 #+LATEX_HEADER: \usepackage{xltxtra}
8 #+LATEX_HEADER: \usepackage[margin=1in]{geometry}
9 #+LATEX_HEADER: \setmainfont[Ligatures={Common}]{Times New Roman}
10 #+LATEX_HEADER: \author{Ben McGinnes <ben@gnupg.org>}
11
12
13 * Introduction
14   :PROPERTIES:
15   :CUSTOM_ID: intro
16   :END:
17
18 | Version:        | 0.1.4                                    |
19 | GPGME Version:  | 1.12.1                                   |
20 | Author:         | Ben McGinnes <ben@gnupg.org>             |
21 | Author GPG Key: | DB4724E6FA4286C92B4E55C4321E4E2373590E5D |
22 | Language:       | Australian English, British English      |
23 | Language codes: | en-AU, en-GB, en                         |
24
25 This document provides basic instruction in how to use the GPGME
26 Python bindings to programmatically leverage the GPGME library.
27
28
29 ** Python 2 versus Python 3
30    :PROPERTIES:
31    :CUSTOM_ID: py2-vs-py3
32    :END:
33
34 Though the GPGME Python bindings themselves provide support for both
35 Python 2 and 3, the focus is unequivocally on Python 3 and
36 specifically from Python 3.4 and above.  As a consequence all the
37 examples and instructions in this guide use Python 3 code.
38
39 Much of it will work with Python 2, but much of it also deals with
40 Python 3 byte literals, particularly when reading and writing data.
41 Developers concentrating on Python 2.7, and possibly even 2.6, will
42 need to make the appropriate modifications to support the older string
43 and unicode types as opposed to bytes.
44
45 There are multiple reasons for concentrating on Python 3; some of
46 which relate to the immediate integration of these bindings, some of
47 which relate to longer term plans for both GPGME and the python
48 bindings and some of which relate to the impending EOL period for
49 Python 2.7.  Essentially, though, there is little value in tying the
50 bindings to a version of the language which is a dead end and the
51 advantages offered by Python 3 over Python 2 make handling the data
52 types with which GPGME deals considerably easier.
53
54
55 ** Examples
56    :PROPERTIES:
57    :CUSTOM_ID: howto-python3-examples
58    :END:
59
60 All of the examples found in this document can be found as Python 3
61 scripts in the =lang/python/examples/howto= directory.
62
63
64 ** Unofficial Drafts
65    :PROPERTIES:
66    :CUSTOM_ID: unofficial-drafts
67    :END:
68
69 In addition to shipping with each release of GPGME, there is a section
70 on locations to read or download [[#draft-editions][draft editions]] of this document from
71 at the end of it.  These are unofficial versions produced in between
72 major releases.
73
74
75 ** What's New
76    :PROPERTIES:
77    :CUSTOM_ID: new-stuff
78    :END:
79
80 Full details of what is new are now available in the [[file:what-is-new.org][What's New]] file
81 and archives of the preceding /What's New/ sections are available in
82 the [[file:what-was-new][What Was New]] file.
83
84
85 *** New in GPGME 1·13·0
86     :PROPERTIES:
87     :CUSTOM_ID: new-stuff-1-13-0
88     :END:
89
90 See the [[file:what-is-new#new-stuff-1-13-0][What's New]] document for what is new in version 1.13.0.
91
92
93 *** New in GPGME 1·12·0
94     :PROPERTIES:
95     :CUSTOM_ID: new-stuff-1-12-0
96     :END:
97
98 See the [[file:what-was-new#new-stuff-1-12-0][What Was New]] document for what was new in version 1.12.0.
99
100
101 * GPGME Concepts
102   :PROPERTIES:
103   :CUSTOM_ID: gpgme-concepts
104   :END:
105
106
107 ** A C API
108    :PROPERTIES:
109    :CUSTOM_ID: gpgme-c-api
110    :END:
111
112 Unlike many modern APIs with which programmers will be more familiar
113 with these days, the GPGME API is a C API.  The API is intended for
114 use by C coders who would be able to access its features by including
115 the =gpgme.h= header file with their own C source code and then access
116 its functions just as they would any other C headers.
117
118 This is a very effective method of gaining complete access to the API
119 and in the most efficient manner possible.  It does, however, have the
120 drawback that it cannot be directly used by other languages without
121 some means of providing an interface to those languages.  This is
122 where the need for bindings in various languages stems.
123
124
125 ** Python bindings
126    :PROPERTIES:
127    :CUSTOM_ID: gpgme-python-bindings
128    :END:
129
130 The Python bindings for GPGME provide a higher level means of
131 accessing the complete feature set of GPGME itself.  It also provides
132 a more pythonic means of calling these API functions.
133
134 The bindings are generated dynamically with SWIG and the copy of
135 =gpgme.h= generated when GPGME is compiled.
136
137 This means that a version of the Python bindings is fundamentally tied
138 to the exact same version of GPGME used to generate that copy of
139 =gpgme.h=.
140
141
142 ** Difference between the Python bindings and other GnuPG Python packages
143    :PROPERTIES:
144    :CUSTOM_ID: gpgme-python-bindings-diffs
145    :END:
146
147 There have been numerous attempts to add GnuPG support to Python over
148 the years.  Some of the most well known are listed here, along with
149 what differentiates them.
150
151
152 *** The python-gnupg package maintained by Vinay Sajip
153     :PROPERTIES:
154     :CUSTOM_ID: diffs-python-gnupg
155     :END:
156
157 This is arguably the most popular means of integrating GPG with
158 Python.  The package utilises the =subprocess= module to implement
159 wrappers for the =gpg= and =gpg2= executables normally invoked on the
160 command line (=gpg.exe= and =gpg2.exe= on Windows).
161
162 The popularity of this package stemmed from its ease of use and
163 capability in providing the most commonly required features.
164
165 Unfortunately it has been beset by a number of security issues in the
166 past; most of which stemmed from using unsafe methods of accessing the
167 command line via the =subprocess= calls.  While some effort has been
168 made over the last two to three years (as of 2018) to mitigate this,
169 particularly by no longer providing shell access through those
170 subprocess calls, the wrapper is still somewhat limited in the scope
171 of its GnuPG features coverage.
172
173 The python-gnupg package is available under the MIT license.
174
175
176 *** The gnupg package created and maintained by Isis Lovecruft
177     :PROPERTIES:
178     :CUSTOM_ID: diffs-isis-gnupg
179     :END:
180
181 In 2015 Isis Lovecruft from the Tor Project forked and then
182 re-implemented the python-gnupg package as just gnupg.  This new
183 package also relied on subprocess to call the =gpg= or =gpg2=
184 binaries, but did so somewhat more securely.
185
186 The naming and version numbering selected for this package, however,
187 resulted in conflicts with the original python-gnupg and since its
188 functions were called in a different manner to python-gnupg, the
189 release of this package also resulted in a great deal of consternation
190 when people installed what they thought was an upgrade that
191 subsequently broke the code relying on it.
192
193 The gnupg package is available under the GNU General Public License
194 version 3.0 (or any later version).
195
196
197 *** The PyME package maintained by Martin Albrecht
198     :PROPERTIES:
199     :CUSTOM_ID: diffs-pyme
200     :END:
201
202 This package is the origin of these bindings, though they are somewhat
203 different now.  For details of when and how the PyME package was
204 folded back into GPGME itself see the [[file:short-history.org][Short History]] document.[fn:1]
205
206 The PyME package was first released in 2002 and was also the first
207 attempt to implement a low level binding to GPGME.  In doing so it
208 provided access to considerably more functionality than either the
209 =python-gnupg= or =gnupg= packages.
210
211 The PyME package is only available for Python 2.6 and 2.7.
212
213 Porting the PyME package to Python 3.4 in 2015 is what resulted in it
214 being folded into the GPGME project and the current bindings are the
215 end result of that effort.
216
217 The PyME package is available under the same dual licensing as GPGME
218 itself: the GNU General Public License version 2.0 (or any later
219 version) and the GNU Lesser General Public License version 2.1 (or any
220 later version).
221
222
223 * GPGME Python bindings installation
224   :PROPERTIES:
225   :CUSTOM_ID: gpgme-python-install
226   :END:
227
228
229 ** No PyPI
230    :PROPERTIES:
231    :CUSTOM_ID: do-not-use-pypi
232    :END:
233
234 Most third-party Python packages and modules are available and
235 distributed through the Python Package Installer, known as PyPI.
236
237 Due to the nature of what these bindings are and how they work, it is
238 infeasible to install the GPGME Python bindings in the same way.
239
240 This is because the bindings use SWIG to dynamically generate C
241 bindings against =gpgme.h= and =gpgme.h= is generated from
242 =gpgme.h.in= at compile time when GPGME is built from source.  Thus to
243 include a package in PyPI which actually built correctly would require
244 either statically built libraries for every architecture bundled with
245 it or a full implementation of C for each architecture.
246
247 See the additional notes regarding [[#snafu-cffi][CFFI and SWIG]] at the end of this
248 section for further details.
249
250
251 ** Requirements
252    :PROPERTIES:
253    :CUSTOM_ID: gpgme-python-requirements
254    :END:
255
256 The GPGME Python bindings only have three requirements:
257
258 1. A suitable version of Python 2 or Python 3.  With Python 2 that
259    means CPython 2.7 and with Python 3 that means CPython 3.4 or
260    higher.
261 2. [[https://www.swig.org][SWIG]].
262 3. GPGME itself.  Which also means that all of GPGME's dependencies
263    must be installed too.
264
265
266 *** Recommended Additions
267    :PROPERTIES:
268    :CUSTOM_ID: gpgme-python-recommendations
269    :END:
270
271 Though none of the following are absolute requirements, they are all
272 recommended for use with the Python bindings.  In some cases these
273 recommendations refer to which version(s) of CPython to use the
274 bindings with, while others refer to third party modules which provide
275 a significant advantage in some way.
276
277 1. If possible, use Python 3 instead of 2.
278 2. Favour a more recent version of Python since even 3.4 is due to
279    reach EOL soon.  In production systems and services, Python 3.6
280    should be robust enough to be relied on.
281 3. If possible add the following Python modules which are not part of
282    the standard library: [[http://docs.python-requests.org/en/latest/index.html][Requests]], [[https://cython.org/][Cython]], [[https://pendulum.eustace.io/][Pendulum]] and [[https://github.com/Selfnet/hkp4py][hkp4py]].
283
284 Chances are quite high that at least the first one and maybe two of
285 those will already be installed.
286
287 Note that, as with Cython, some of advanced use case scenarios will
288 bring with them additional requirements.  Most of these will be fairly
289 well known and commonly installed ones, however, which are in many
290 cases likely to have already been installed on many systems or be
291 familiar to Python programmers.
292
293
294 ** Installation
295    :PROPERTIES:
296    :CUSTOM_ID: installation
297    :END:
298
299 Installing the Python bindings is effectively achieved by compiling
300 and installing GPGME itself.
301
302 Once SWIG is installed with Python and all the dependencies for GPGME
303 are installed you only need to confirm that the version(s) of Python
304 you want the bindings installed for are in your =$PATH=.
305
306 By default GPGME will attempt to install the bindings for the most
307 recent or highest version number of Python 2 and Python 3 it detects
308 in =$PATH=.  It specifically checks for the =python= and =python3=
309 executables first and then checks for specific version numbers.
310
311 For Python 2 it checks for these executables in this order: =python=,
312 =python2= and =python2.7=.
313
314 For Python 3 it checks for these executables in this order: =python3=,
315  =python3.7=, =python3.6=, =python3.5= and =python3.4=.[fn:2]
316
317 On systems where =python= is actually =python3= and not =python2= it
318 may be possible that =python2= may be overlooked, but there have been
319 no reports of that actually occurring as yet.
320
321 In the three months or so since the release of Python 3.7.0 there has
322 been extensive testing and work with these bindings with no issues
323 specifically relating to the new version of Python or any of the new
324 features of either the language or the bindings.  This has also been
325 the case with Python 3.7.1rc1.  With that in mind and given the
326 release of Python 3.7.1 is scheduled for around the same time as GPGME
327 1.12.0, the order of preferred Python versions has been changed to
328 move Python 3.7 ahead of Python 3.6.
329
330
331 *** Installing GPGME
332     :PROPERTIES:
333     :CUSTOM_ID: install-gpgme
334     :END:
335
336 See the GPGME =README= file for details of how to install GPGME from
337 source.
338
339
340 ** Known Issues
341    :PROPERTIES:
342    :CUSTOM_ID: snafu
343    :END:
344
345 There are a few known issues with the current build process and the
346 Python bindings.  For the most part these are easily addressed should
347 they be encountered.
348
349
350 *** Breaking Builds
351     :PROPERTIES:
352     :CUSTOM_ID: snafu-a-swig-of-this-builds-character
353     :END:
354
355 Occasionally when installing GPGME with the Python bindings included
356 it may be observed that the =make= portion of that process induces a
357 large very number of warnings and, eventually errors which end that
358 part of the build process.  Yet following that with =make check= and
359 =make install= appears to work seamlessly.
360
361 The cause of this is related to the way SWIG needs to be called to
362 dynamically generate the C bindings for GPGME in the first place.  So
363 the entire process will always produce =lang/python/python2-gpg/= and
364 =lang/python/python3-gpg/= directories.  These should contain the
365 build output generated during compilation, including the complete
366 bindings and module installed into =site-packages=.
367
368 Occasionally the errors in the early part or some other conflict
369 (e.g. not installing as */root/* or */su/*) may result in nothing
370 being installed to the relevant =site-packages= directory and the
371 build directory missing a lot of expected files.  Even when this
372 occurs, the solution is actually quite simple and will always work.
373
374 That solution is simply to run the following commands as either the
375 *root* user or prepended with =sudo -H=[fn:3] in the =lang/python/=
376 directory:
377
378 #+BEGIN_SRC shell
379   /path/to/pythonX.Y setup.py build
380   /path/to/pythonX.Y setup.py build
381   /path/to/pythonX.Y setup.py install
382 #+END_SRC
383
384 Yes, the build command does need to be run twice.  Yes, you still need
385 to run the potentially failing or incomplete steps during the
386 =configure=, =make= and =make install= steps with installing GPGME.
387 This is because those steps generate a lot of essential files needed,
388 both by and in order to create, the bindings (including both the
389 =setup.py= and =gpgme.h= files).
390
391
392 **** IMPORTANT Note
393      :PROPERTIES:
394      :CUSTOM_ID: snafu-swig-build-note
395      :END:
396
397 If specifying a selected number of languages to create bindings for,
398 try to leave Python last.  Currently the majority of the other
399 language bindings are also preceding Python of either version when
400 listed alphabetically and so that just happens by default currently.
401
402 If Python is set to precede one of the other languages then it is
403 possible that the errors described here may interrupt the build
404 process before generating bindings for those other languages.  In
405 these cases it may be preferable to configure all preferred language
406 bindings separately with alternative =configure= steps for GPGME using
407 the =--enable-languages=$LANGUAGE= option.
408
409
410 *** Reinstalling Responsibly
411     :PROPERTIES:
412     :CUSTOM_ID: snafu-lessons-for-the-lazy
413     :END:
414
415 Regardless of whether you're installing for one version of Python or
416 several, there will come a point where reinstallation is required.
417 With most Python module installations, the installed files go into the
418 relevant site-packages directory and are then forgotten about.  Then
419 the module is upgraded, the new files are copied over the old and
420 that's the end of the matter.
421
422 While the same is true of these bindings, there have been intermittent
423 issues observed on some platforms which have benefited significantly
424 from removing all the previous installations of the bindings before
425 installing the updated versions.
426
427 Removing the previous version(s) is simply a matter of changing to the
428 relevant =site-packages= directory for the version of Python in
429 question and removing the =gpg/= directory and any accompanying
430 egg-info files for that module.
431
432 In most cases this will require root or administration privileges on
433 the system, but the same is true of installing the module in the first
434 place.
435
436
437 *** Multiple installations
438     :PROPERTIES:
439     :CUSTOM_ID: snafu-the-full-monty
440     :END:
441
442 For a variety of reasons it may be either necessary or just preferable
443 to install the bindings to alternative installed Python versions which
444 meet the requirements of these bindings.
445
446 On POSIX systems this will generally be most simply achieved by
447 running the manual installation commands (build, build, install) as
448 described in the previous section for each Python installation the
449 bindings need to be installed to.
450
451 As per the SWIG documentation: the compilers, libraries and runtime
452 used to build GPGME and the Python Bindings *must* match those used to
453 compile Python itself, including the version number(s) (at least going
454 by major version numbers and probably minor numbers too).
455
456 On most POSIX systems, including OS X, this will very likely be the
457 case in most, if not all, cases.
458
459 Note that from GPGME [[https://dev.gnupg.org/rMff6ff616aea6f59b7f2ce1176492850ecdf3851e][1.12.1]] the default installation installs to each
460 version of Python it can find first.  That is that it will currently
461 install for the first copies of Python versions 2.7, 3.4, 3.5, 3.6,
462 3.7 and 3.8 (dev branch) that it finds.  Usually this will be in the
463 same prefix as GPGME itself, but is dictated by the =$PATH= when the
464 installation is performed.  The above instructions can still be
465 performed on other python installations which the installer does not
466 find, including alternative prefixes.
467
468
469
470 *** Won't Work With Windows
471     :PROPERTIES:
472     :CUSTOM_ID: snafu-runtime-not-funtime
473     :END:
474
475 There are semi-regular reports of Windows users having considerable
476 difficulty in installing and using the Python bindings at all.  Very
477 often, possibly even always, these reports come from Cygwin users
478 and/or MinGW users and/or Msys2 users.  Though not all of them have
479 been confirmed, it appears that these reports have also come from
480 people who installed Python using the Windows installer files from the
481 [[https://python.org][Python website]] (i.e. mostly MSI installers, sometimes self-extracting
482 =.exe= files).
483
484 The Windows versions of Python are not built using Cygwin, MinGW or
485 Msys2; they're built using Microsoft Visual Studio.  Furthermore the
486 version used is /considerably/ more advanced than the version which
487 MinGW obtained a small number of files from many years ago in order to
488 be able to compile anything at all.  Not only that, but there are
489 changes to the version of Visual Studio between some micro releases,
490 though that is is particularly the case with Python 2.7, since it has
491 been kept around far longer than it should have been.
492
493 There are two theoretical solutions to this issue:
494
495  1. Compile and install the GnuPG stack, including GPGME and the
496     Python bindings using the same version of Microsoft Visual Studio
497     used by the Python Foundation to compile the version of Python
498     installed.
499
500     If there are multiple versions of Python then this will need to be
501     done with each different version of Visual Studio used for those
502     versions of Python.
503
504  2. Compile and install Python using the same tools used by choice,
505     such as MinGW or Msys2.
506
507 Do *not* use the official Windows installer for Python unless
508 following the first method.
509
510 In this type of situation it may even be for the best to accept that
511 there are less limitations on permissive software than free software
512 and simply opt to use a recent version of the Community Edition of
513 Microsoft Visual Studio to compile and build all of it, no matter
514 what.
515
516 Investigations into the extent or the limitations of this issue are
517 ongoing.
518
519 The following table lists the version of Microsoft Visual Studio which
520 needs to be used when compiling GPGME and the Python bindings with
521 each version of the CPython binary released [[https://www.python.org/downloads/windows/][for Windows]]:
522
523 | CPython | Microsoft product name | runtime filename |
524 |  2.7.6  |   Visual Studio 2008   |   MSVCR90.DLL    |
525 |  3.4.0  |   Visual Studio 2010   |   MSVCR100.DLL   |
526 |  3.5.0  |   Visual Studio 2015   |   *see below*    |
527 |  3.6.0  |   Visual Studio 2015   |   *see below*    |
528 |  3.7.0  |   Visual Studio 2017*  |   *see below*    |
529
530 It is important to note that MingW and Msys2 ship with the Visual C
531 runtime from Microsoft Visual Studio 2005 and are thus *incompatible*
532 with all the versions of CPython which can be used with the GPGME
533 Python bindings.
534
535 It is also important to note that from CPython 3.5 onwards, the Python
536 Foundation has adopted the reworking of the Visual C runtime which was
537 performed for Visual Studio 2015 and aimed at resolving many of these
538 kinds of issues.  Much greater detail on these issues and the correct
539 file(s) to link to are available from Matthew Brett's invaluable page,
540 [[https://matthew-brett.github.io/pydagogue/python_msvc.html][Using Microsoft Visual C with Python]].  It is also worth reading the
541 Microsoft Developer Network blog post on [[http://blogs.msdn.com/b/vcblog/archive/2015/03/03/introducing-the-universal-crt.aspx][the universal CRT]] and Steve
542 Dower's blog posts on Python extensions ([[http://stevedower.id.au/blog/building-for-python-3-5][part 1]] and [[http://stevedower.id.au/blog/building-for-python-3-5-part-two][part 2]]).
543
544 The second of those two posts by Steve Dower contains the details of
545 specific configuration options required for compiling anything to be
546 used with official CPython releases.  In addition to those
547 configuration and compiler settings to use, the versions of Visual
548 Studio prior to Visual Studio 2015 did not support 64-bit systems by
549 default.  So compiling a 64-bit version of these bindings for a 64-bit
550 version of CPython 2.7 or 3.4 requires additional work.
551
552 In addition to the blog posts, the [[https://wiki.python.org/moin/WindowsCompilers][Windows compilers]] wiki page on the
553 CPython wiki is another essential reference on the relevant versions
554 of Visual Studio to use and the degree of compatibility with CPython
555 releases.
556
557 Eventually someone will ask why there isn't an installable binary for
558 Windows, which the GPGME of the licenses do not preclude as long as
559 the source code is available in conjunction with such a release.
560
561 The sheer number of versions of Visual Studio in conjunction with
562 differing configuration options depending on the target Windows
563 version and whether the architecture is 64-bit or 32-bit makes it
564 difficult to provide a correct binary installer for Windows users.  At
565 the bare minimum doing so would require the GnuPG project compile ten
566 different versions of the bindings with each release; both 32-bit and
567 64-bit versions for CPython 2.7 and 3.4, with 64-bit versions for both
568 x86-64 (i.e. Intel and AMD) and ARM architectures for CPython 3.5,
569 3.6, 3.7 and later releases.  That's the bare *minimum*, it'd probably
570 be higher.
571
572 Additionally, with only a binary installation used in conjunction with
573 the CPython installer from =python.org= the advanced options available
574 which utilise [[#cython][Cython]] will not be able to be used at all.  Cython
575 depends on being able to compile the C code it generates and that too
576 would need to utilise a matching runtime to both the installed version
577 of CPython and these bindings in order to work with the bindings.
578
579 Considering all of that, what do we recommend?
580
581  1. Use a recent version of CPython; at least 3.5, but ideally 3.6 or
582     later.
583
584  2. Use Visual Studio 2015 or the standalone build tools for Visual
585     Studio 2017 (or later).
586
587  3. Compile both CPython and GPGME with these bindings using the tools
588     selected in step 2.
589
590  4. Ignore MingW, Msys2 and the official CPython binary installers.
591
592  5. Be thankful the answer to this question wasn't simply to say
593     something like, “install Linux” or “install FreeBSD” (or even
594     Apple's OS X).
595
596
597 *** CFFI is the Best™ and GPGME should use it instead of SWIG
598     :PROPERTIES:
599     :CUSTOM_ID: snafu-cffi
600     :END:
601
602 There are many reasons for favouring [[https://cffi.readthedocs.io/en/latest/overview.html][CFFI]] and proponents of it are
603 quite happy to repeat these things as if all it would take to switch
604 from SWIG to CFFI is repeating that list as if it were a new concept.
605
606 The fact is that there are things which Python's CFFI implementation
607 cannot handle in the GPGME C code.  Beyond that there are features of
608 SWIG which are simply not available with CFFI at all.  SWIG generates
609 the bindings to Python using the =gpgme.h= file, but that file is not
610 a single version shipped with each release, it too is generated when
611 GPGME is compiled.
612
613 CFFI is currently unable to adapt to such a potentially mutable
614 codebase.  If there were some means of applying SWIG's dynamic code
615 generation to produce the Python/CFFI API modes of accessing the GPGME
616 libraries (or the source source code directly), but such a thing does
617 not exist yet either and it currently appears that work is needed in
618 at least one of CFFI's dependencies before any of this can be
619 addressed.
620
621 So if you're a massive fan of CFFI; that's great, but if you want this
622 project to switch to CFFI then rather than just insisting that it
623 should, I'd suggest you volunteer to bring CFFI up to the level this
624 project needs.
625
626 If you're actually seriously considering doing so, then I'd suggest
627 taking the =gpgme-tool.c= file in the GPGME =src/= directory and
628 getting that to work with any of the CFFI API methods (not the ABI
629 methods, they'll work with pretty much anything).  When you start
630 running into trouble with "ifdefs" then you'll know what sort of
631 things are lacking.  That doesn't even take into account the amount of
632 work saved via SWIG's code generation techniques either.
633
634
635 *** Virtualised Environments
636     :PROPERTIES:
637     :CUSTOM_ID: snafu-venv
638     :END:
639
640 It is fairly common practice amongst Python developers to, as much as
641 possible, use packages like virtualenv to keep various things that are
642 to be installed from interfering with each other.  Given how much of
643 the GPGME bindings is often at odds with the usual pythonic way of
644 doing things, it stands to reason that this would be called into
645 question too.
646
647 As it happens the answer as to whether or not the bindings can be used
648 with virtualenv, the answer is both yes and no.
649
650 In general we recommend installing to the relevant path and matching
651 prefix of GPGME itself.  Which means that when GPGME, and ideally the
652 rest of the GnuPG stack, is installed to a prefix like =/usr/local= or
653 =/opt/local= then the bindings would need to be installed to the main
654 Python installation and not a virtualised abstraction.  Attempts to
655 separate the two in the past have been known to cause weird and
656 intermittent errors ranging from minor annoyances to complete failures
657 in the build process.
658
659 As a consequence we only recommend building with and installing to the
660 main Python installations within the same prefix as GPGME is installed
661 to or which are found by GPGME's configuration stage immediately prior
662 to running the make commands.  Which is exactly what the compiling and
663 installing process of GPGME does by default.
664
665 Once that is done, however, it appears that a copy of the compiled
666 module may be installed into a virtualenv of the same major and minor
667 version matching the build.  Alternatively it is possible to utilise a
668 =sites.pth= file in the =site-packages/= directory of a virtualenv
669 installation, which links back to the system installations
670 corresponding directory in order to import anything installed system
671 wide.  This may or may not be appropriate on a case by case basis.
672
673 Though extensive testing of either of these options is not yet
674 complete, preliminary testing of them indicates that both are viable
675 as long as the main installation is complete.  Which means that
676 certain other options normally restricted to virtual environments are
677 also available, including integration with pythonic test suites
678 (e.g. [[https://docs.pytest.org/en/latest/index.html][pytest]]) and other large projects.
679
680 That said, it is worth reiterating the warning regarding non-standard
681 installations.  If one were to attempt to install the bindings only to
682 a virtual environment without somehow also including the full GnuPG
683 stack (or enough of it as to include GPGME) then it is highly likely
684 that errors would be encountered at some point and more than a little
685 likely that the build process itself would break.
686
687 If a degree of separation from the main operating system is still
688 required in spite of these warnings, then consider other forms of
689 virtualisation.  Either a virtual machine (e.g. [[https://www.virtualbox.org/][VirtualBox]]), a
690 hardware emulation layer (e.g. [[https://www.qemu.org/][QEMU]]) or an application container
691 (e.g. [[https://www.docker.com/why-docker][Docker]]).
692
693 Finally it should be noted that the limited tests conducted thus far
694 have been using the =virtualenv= command in a new directory to create
695 the virtual python environment.  As opposed to the standard =python3
696 -m venv= and it is possible that this will make a difference depending
697 on the system and version of Python in use.  Another option is to run
698 the command =python3 -m virtualenv /path/to/install/virtual/thingy=
699 instead.
700
701
702 * Fundamentals
703   :PROPERTIES:
704   :CUSTOM_ID: howto-fund-a-mental
705   :END:
706
707 Before we can get to the fun stuff, there are a few matters regarding
708 GPGME's design which hold true whether you're dealing with the C code
709 directly or these Python bindings.
710
711
712 ** No REST
713    :PROPERTIES:
714    :CUSTOM_ID: no-rest-for-the-wicked
715    :END:
716
717 The first part of which is or will be fairly blatantly obvious upon
718 viewing the first example, but it's worth reiterating anyway.  That
719 being that this API is /*not*/ a REST API.  Nor indeed could it ever
720 be one.
721
722 Most, if not all, Python programmers (and not just Python programmers)
723 know how easy it is to work with a RESTful API.  In fact they've
724 become so popular that many other APIs attempt to emulate REST-like
725 behaviour as much as they are able.  Right down to the use of JSON
726 formatted output to facilitate the use of their API without having to
727 retrain developers.
728
729 This API does not do that.  It would not be able to do that and also
730 provide access to the entire C API on which it's built.  It does,
731 however, provide a very pythonic interface on top of the direct
732 bindings and it's this pythonic layer that this HOWTO deals with.
733
734
735 ** Context
736    :PROPERTIES:
737    :CUSTOM_ID: howto-get-context
738    :END:
739
740 One of the reasons which prevents this API from being RESTful is that
741 most operations require more than one instruction to the API to
742 perform the task.  Sure, there are certain functions which can be
743 performed simultaneously, particularly if the result known or strongly
744 anticipated (e.g. selecting and encrypting to a key known to be in the
745 public keybox).
746
747 There are many more, however, which cannot be manipulated so readily:
748 they must be performed in a specific sequence and the result of one
749 operation has a direct bearing on the outcome of subsequent
750 operations.  Not merely by generating an error either.
751
752 When dealing with this type of persistent state on the web, full of
753 both the RESTful and REST-like, it's most commonly referred to as a
754 session.  In GPGME, however, it is called a context and every
755 operation type has one.
756
757
758 * Working with keys
759   :PROPERTIES:
760   :CUSTOM_ID: howto-keys
761   :END:
762
763
764 ** Key selection
765    :PROPERTIES:
766    :CUSTOM_ID: howto-keys-selection
767    :END:
768
769 Selecting keys to encrypt to or to sign with will be a common
770 occurrence when working with GPGMe and the means available for doing
771 so are quite simple.
772
773 They do depend on utilising a Context; however once the data is
774 recorded in another variable, that Context does not need to be the
775 same one which subsequent operations are performed.
776
777 The easiest way to select a specific key is by searching for that
778 key's key ID or fingerprint, preferably the full fingerprint without
779 any spaces in it.  A long key ID will probably be okay, but is not
780 advised and short key IDs are already a problem with some being
781 generated to match specific patterns.  It does not matter whether the
782 pattern is upper or lower case.
783
784 So this is the best method:
785
786 #+BEGIN_SRC python -i
787 import gpg
788
789 k = gpg.Context().keylist(pattern="258E88DCBD3CD44D8E7AB43F6ECB6AF0DEADBEEF")
790 keys = list(k)
791 #+END_SRC
792
793 This is passable and very likely to be common:
794
795 #+BEGIN_SRC python -i
796 import gpg
797
798 k = gpg.Context().keylist(pattern="0x6ECB6AF0DEADBEEF")
799 keys = list(k)
800 #+END_SRC
801
802 And this is a really bad idea:
803
804 #+BEGIN_SRC python -i
805 import gpg
806
807 k = gpg.Context().keylist(pattern="0xDEADBEEF")
808 keys = list(k)
809 #+END_SRC
810
811 Alternatively it may be that the intention is to create a list of keys
812 which all match a particular search string.  For instance all the
813 addresses at a particular domain, like this:
814
815 #+BEGIN_SRC python -i
816 import gpg
817
818 ncsc = gpg.Context().keylist(pattern="ncsc.mil")
819 nsa = list(ncsc)
820 #+END_SRC
821
822
823 *** Counting keys
824     :PROPERTIES:
825     :CUSTOM_ID: howto-keys-counting
826     :END:
827
828 Counting the number of keys in your public keybox (=pubring.kbx=), the
829 format which has superseded the old keyring format (=pubring.gpg= and
830 =secring.gpg=), or the number of secret keys is a very simple task.
831
832 #+BEGIN_SRC python -i
833 import gpg
834
835 c = gpg.Context()
836 seckeys = c.keylist(pattern=None, secret=True)
837 pubkeys = c.keylist(pattern=None, secret=False)
838
839 seclist = list(seckeys)
840 secnum = len(seclist)
841
842 publist = list(pubkeys)
843 pubnum = len(publist)
844
845 print("""
846   Number of secret keys:  {0}
847   Number of public keys:  {1}
848 """.format(secnum, pubnum))
849 #+END_SRC
850
851 NOTE: The [[#cython][Cython]] introduction in the [[#advanced-use][Advanced and Experimental]]
852 section uses this same key counting code with Cython to demonstrate
853 some areas where Cython can improve performance even with the
854 bindings.  Users with large public keyrings or keyboxes, for instance,
855 should consider these options if they are comfortable with using
856 Cython.
857
858
859 ** Get key
860    :PROPERTIES:
861    :CUSTOM_ID: howto-get-key
862    :END:
863
864 An alternative method of getting a single key via its fingerprint is
865 available directly within a Context with =Context().get_key=.  This is
866 the preferred method of selecting a key in order to modify it, sign or
867 certify it and for obtaining relevant data about a single key as a
868 part of other functions; when verifying a signature made by that key,
869 for instance.
870
871 By default this method will select public keys, but it can select
872 secret keys as well.
873
874 This first example demonstrates selecting the current key of Werner
875 Koch, which is due to expire at the end of 2018:
876
877 #+BEGIN_SRC python -i
878 import gpg
879
880 fingerprint = "80615870F5BAD690333686D0F2AD85AC1E42B367"
881 key = gpg.Context().get_key(fingerprint)
882 #+END_SRC
883
884 Whereas this example demonstrates selecting the author's current key
885 with the =secret= key word argument set to =True=:
886
887 #+BEGIN_SRC python -i
888 import gpg
889
890 fingerprint = "DB4724E6FA4286C92B4E55C4321E4E2373590E5D"
891 key = gpg.Context().get_key(fingerprint, secret=True)
892 #+END_SRC
893
894 It is, of course, quite possible to select expired, disabled and
895 revoked keys with this function, but only to effectively display
896 information about those keys.
897
898 It is also possible to use both unicode or string literals and byte
899 literals with the fingerprint when getting a key in this way.
900
901
902 ** Importing keys
903    :PROPERTIES:
904    :CUSTOM_ID: howto-import-key
905    :END:
906
907 Importing keys is possible with the =key_import()= method and takes
908 one argument which is a bytes literal object containing either the
909 binary or ASCII armoured key data for one or more keys.
910
911 The following example retrieves one or more keys from the SKS
912 keyservers via the web using the requests module.  Since requests
913 returns the content as a bytes literal object, we can then use that
914 directly to import the resulting data into our keybox.
915
916 #+BEGIN_SRC python -i
917 import gpg
918 import os.path
919 import requests
920
921 c = gpg.Context()
922 url = "https://sks-keyservers.net/pks/lookup"
923 pattern = input("Enter the pattern to search for key or user IDs: ")
924 payload = {"op": "get", "search": pattern}
925
926 r = requests.get(url, verify=True, params=payload)
927 result = c.key_import(r.content)
928
929 if result is not None and hasattr(result, "considered") is False:
930     print(result)
931 elif result is not None and hasattr(result, "considered") is True:
932     num_keys = len(result.imports)
933     new_revs = result.new_revocations
934     new_sigs = result.new_signatures
935     new_subs = result.new_sub_keys
936     new_uids = result.new_user_ids
937     new_scrt = result.secret_imported
938     nochange = result.unchanged
939     print("""
940   The total number of keys considered for import was:  {0}
941
942      Number of keys revoked:  {1}
943    Number of new signatures:  {2}
944       Number of new subkeys:  {3}
945      Number of new user IDs:  {4}
946   Number of new secret keys:  {5}
947    Number of unchanged keys:  {6}
948
949   The key IDs for all considered keys were:
950 """.format(num_keys, new_revs, new_sigs, new_subs, new_uids, new_scrt,
951            nochange))
952     for i in range(num_keys):
953         print("{0}\n".format(result.imports[i].fpr))
954 else:
955     pass
956 #+END_SRC
957
958 NOTE: When searching for a key ID of any length or a fingerprint
959 (without spaces), the SKS servers require the the leading =0x=
960 indicative of hexadecimal be included.  Also note that the old short
961 key IDs (e.g. =0xDEADBEEF=) should no longer be used due to the
962 relative ease by which such key IDs can be reproduced, as demonstrated
963 by the Evil32 Project in 2014 (which was subsequently exploited in
964 2016).
965
966 Testing for whether a string in any given search is or may be a
967 hexadecimal value which may be missing the leading =0x= is a simple
968 matter of using a try/except statement which attempts to convert the
969 string as hex to an integer and then back to hex; then using that to
970 search with.  Raising a ValueError simply results in treating the
971 string as a string.  This is the method and logic utilised in the
972 =import-keys-hkp.py= script (see below).
973
974
975 *** Working with ProtonMail
976     :PROPERTIES:
977     :CUSTOM_ID: import-protonmail
978     :END:
979
980 Here is a variation on the example above which checks the constrained
981 ProtonMail keyserver for ProtonMail public keys.
982
983 #+BEGIN_SRC python -i
984 import gpg
985 import requests
986 import sys
987
988 print("""
989 This script searches the ProtonMail key server for the specified key and
990 imports it.
991 """)
992
993 c = gpg.Context(armor=True)
994 url = "https://api.protonmail.ch/pks/lookup"
995 ksearch = []
996
997 if len(sys.argv) >= 2:
998     keyterm = sys.argv[1]
999 else:
1000     keyterm = input("Enter the key ID, UID or search string: ")
1001
1002 if keyterm.count("@") == 2 and keyterm.startswith("@") is True:
1003     ksearch.append(keyterm[1:])
1004     ksearch.append(keyterm[1:])
1005     ksearch.append(keyterm[1:])
1006 elif keyterm.count("@") == 1 and keyterm.startswith("@") is True:
1007     ksearch.append("{0}@protonmail.com".format(keyterm[1:]))
1008     ksearch.append("{0}@protonmail.ch".format(keyterm[1:]))
1009     ksearch.append("{0}@pm.me".format(keyterm[1:]))
1010 elif keyterm.count("@") == 0:
1011     ksearch.append("{0}@protonmail.com".format(keyterm))
1012     ksearch.append("{0}@protonmail.ch".format(keyterm))
1013     ksearch.append("{0}@pm.me".format(keyterm))
1014 elif keyterm.count("@") == 2 and keyterm.startswith("@") is False:
1015     uidlist = keyterm.split("@")
1016     for uid in uidlist:
1017         ksearch.append("{0}@protonmail.com".format(uid))
1018         ksearch.append("{0}@protonmail.ch".format(uid))
1019         ksearch.append("{0}@pm.me".format(uid))
1020 elif keyterm.count("@") > 2:
1021     uidlist = keyterm.split("@")
1022     for uid in uidlist:
1023         ksearch.append("{0}@protonmail.com".format(uid))
1024         ksearch.append("{0}@protonmail.ch".format(uid))
1025         ksearch.append("{0}@pm.me".format(uid))
1026 else:
1027     ksearch.append(keyterm)
1028
1029 for k in ksearch:
1030     payload = {"op": "get", "search": k}
1031     try:
1032         r = requests.get(url, verify=True, params=payload)
1033         if r.ok is True:
1034             result = c.key_import(r.content)
1035         elif r.ok is False:
1036             result = r.content
1037     except Exception as e:
1038         result = None
1039
1040     if result is not None and hasattr(result, "considered") is False:
1041         print("{0} for {1}".format(result.decode(), k))
1042     elif result is not None and hasattr(result, "considered") is True:
1043         num_keys = len(result.imports)
1044         new_revs = result.new_revocations
1045         new_sigs = result.new_signatures
1046         new_subs = result.new_sub_keys
1047         new_uids = result.new_user_ids
1048         new_scrt = result.secret_imported
1049         nochange = result.unchanged
1050         print("""
1051 The total number of keys considered for import was:  {0}
1052
1053 With UIDs wholely or partially matching the following string:
1054
1055         {1}
1056
1057    Number of keys revoked:  {2}
1058  Number of new signatures:  {3}
1059     Number of new subkeys:  {4}
1060    Number of new user IDs:  {5}
1061 Number of new secret keys:  {6}
1062  Number of unchanged keys:  {7}
1063
1064 The key IDs for all considered keys were:
1065 """.format(num_keys, k, new_revs, new_sigs, new_subs, new_uids, new_scrt,
1066            nochange))
1067         for i in range(num_keys):
1068             print(result.imports[i].fpr)
1069         print("")
1070     elif result is None:
1071         print(e)
1072 #+END_SRC
1073
1074 Both the above example, [[../examples/howto/pmkey-import.py][pmkey-import.py]], and a version which prompts
1075 for an alternative GnuPG home directory, [[../examples/howto/pmkey-import-alt.py][pmkey-import-alt.py]], are
1076 available with the other examples and are executable scripts.
1077
1078 Note that while the ProtonMail servers are based on the SKS servers,
1079 their server is related more to their API and is not feature complete
1080 by comparison to the servers in the SKS pool.  One notable difference
1081 being that the ProtonMail server does not permit non ProtonMail users
1082 to update their own keys, which could be a vector for attacking
1083 ProtonMail users who may not receive a key's revocation if it had been
1084 compromised.
1085
1086
1087 *** Importing with HKP for Python
1088     :PROPERTIES:
1089     :CUSTOM_ID: import-hkp4py
1090     :END:
1091
1092 Performing the same tasks with the [[https://github.com/Selfnet/hkp4py][hkp4py module]] (available via PyPI)
1093 is not too much different, but does provide a number of options of
1094 benefit to end users.  Not least of which being the ability to perform
1095 some checks on a key before importing it or not.  For instance it may
1096 be the policy of a site or project to only import keys which have not
1097 been revoked.  The hkp4py module permits such checks prior to the
1098 importing of the keys found.
1099
1100 #+BEGIN_SRC python -i
1101 import gpg
1102 import hkp4py
1103 import sys
1104
1105 c = gpg.Context()
1106 server = hkp4py.KeyServer("hkps://hkps.pool.sks-keyservers.net")
1107 results = []
1108 keys = []
1109
1110 if len(sys.argv) > 2:
1111     pattern = " ".join(sys.argv[1:])
1112 elif len(sys.argv) == 2:
1113     pattern = sys.argv[1]
1114 else:
1115     pattern = input("Enter the pattern to search for keys or user IDs: ")
1116
1117
1118 if pattern is not None:
1119     try:
1120         key = server.search(hex(int(pattern, 16)))
1121         keyed = True
1122     except ValueError as ve:
1123         key = server.search(pattern)
1124         keyed = False
1125
1126     if key is not None:
1127         keys.append(key[0])
1128         if keyed is True:
1129             try:
1130                 fob = server.search(pattern)
1131             except:
1132                 fob = None
1133             if fob is not None:
1134                 keys.append(fob[0])
1135         else:
1136             pass
1137     else:
1138         pass
1139
1140     for logrus in pattern.split():
1141         try:
1142             key = server.search(hex(int(logrus, 16)))
1143             hexed = True
1144         except ValueError as ve:
1145             key = server.search(logrus)
1146             hexed = False
1147
1148         if key is not None:
1149             keys.append(key[0])
1150             if hexed is True:
1151                 try:
1152                     fob = server.search(logrus)
1153                 except:
1154                     fob = None
1155                 if fob is not None:
1156                     keys.append(fob[0])
1157             else:
1158                 pass
1159         else:
1160             pass
1161
1162
1163 if len(keys) > 0:
1164     for key in keys:
1165         import_result = c.key_import(key.key_blob)
1166         results.append(import_result)
1167
1168 for result in results:
1169     if result is not None and hasattr(result, "considered") is False:
1170         print(result)
1171     elif result is not None and hasattr(result, "considered") is True:
1172         num_keys = len(result.imports)
1173         new_revs = result.new_revocations
1174         new_sigs = result.new_signatures
1175         new_subs = result.new_sub_keys
1176         new_uids = result.new_user_ids
1177         new_scrt = result.secret_imported
1178         nochange = result.unchanged
1179         print("""
1180 The total number of keys considered for import was:  {0}
1181
1182    Number of keys revoked:  {1}
1183  Number of new signatures:  {2}
1184     Number of new subkeys:  {3}
1185    Number of new user IDs:  {4}
1186 Number of new secret keys:  {5}
1187  Number of unchanged keys:  {6}
1188
1189 The key IDs for all considered keys were:
1190 """.format(num_keys, new_revs, new_sigs, new_subs, new_uids, new_scrt,
1191            nochange))
1192         for i in range(num_keys):
1193             print(result.imports[i].fpr)
1194         print("")
1195     else:
1196         pass
1197 #+END_SRC
1198
1199 Since the hkp4py module handles multiple keys just as effectively as
1200 one (=keys= is a list of responses per matching key), the example
1201 above is able to do a little bit more with the returned data before
1202 anything is actually imported.
1203
1204
1205 *** Importing from ProtonMail with HKP for Python
1206     :PROPERTIES:
1207     :CUSTOM_ID: import-protonmail-hkp4py
1208     :END:
1209
1210 Though this can provide certain benefits even when working with
1211 ProtonMail, the scope is somewhat constrained there due to the
1212 limitations of the ProtonMail keyserver.
1213
1214 For instance, searching the SKS keyserver pool for the term "gnupg"
1215 produces hundreds of results from any time the word appears in any
1216 part of a user ID.  Performing the same search on the ProtonMail
1217 keyserver returns zero results, even though there are at least two
1218 test accounts which include it as part of the username.
1219
1220 The cause of this discrepancy is the deliberate configuration of that
1221 server by ProtonMail to require an exact match of the full email
1222 address of the ProtonMail user whose key is being requested.
1223 Presumably this is intended to reduce breaches of privacy of their
1224 users as an email address must already be known before a key for that
1225 address can be obtained.
1226
1227
1228 **** Import from ProtonMail via HKP for Python Example no. 1
1229      :PROPERTIES:
1230      :CUSTOM_ID: import-hkp4py-pm1
1231      :END:
1232
1233 The following script is available with the rest of the examples under
1234 the somewhat less than original name, =pmkey-import-hkp.py=.
1235
1236 #+BEGIN_SRC python -i
1237 import gpg
1238 import hkp4py
1239 import os.path
1240 import sys
1241
1242 print("""
1243 This script searches the ProtonMail key server for the specified key and
1244 imports it.
1245
1246 Usage:  pmkey-import-hkp.py [search strings]
1247 """)
1248
1249 c = gpg.Context(armor=True)
1250 server = hkp4py.KeyServer("hkps://api.protonmail.ch")
1251 keyterms = []
1252 ksearch = []
1253 allkeys = []
1254 results = []
1255 paradox = []
1256 homeless = None
1257
1258 if len(sys.argv) > 2:
1259     keyterms = sys.argv[1:]
1260 elif len(sys.argv) == 2:
1261     keyterm = sys.argv[1]
1262     keyterms.append(keyterm)
1263 else:
1264     key_term = input("Enter the key ID, UID or search string: ")
1265     keyterms = key_term.split()
1266
1267 for keyterm in keyterms:
1268     if keyterm.count("@") == 2 and keyterm.startswith("@") is True:
1269         ksearch.append(keyterm[1:])
1270         ksearch.append(keyterm[1:])
1271         ksearch.append(keyterm[1:])
1272     elif keyterm.count("@") == 1 and keyterm.startswith("@") is True:
1273         ksearch.append("{0}@protonmail.com".format(keyterm[1:]))
1274         ksearch.append("{0}@protonmail.ch".format(keyterm[1:]))
1275         ksearch.append("{0}@pm.me".format(keyterm[1:]))
1276     elif keyterm.count("@") == 0:
1277         ksearch.append("{0}@protonmail.com".format(keyterm))
1278         ksearch.append("{0}@protonmail.ch".format(keyterm))
1279         ksearch.append("{0}@pm.me".format(keyterm))
1280     elif keyterm.count("@") == 2 and keyterm.startswith("@") is False:
1281         uidlist = keyterm.split("@")
1282         for uid in uidlist:
1283             ksearch.append("{0}@protonmail.com".format(uid))
1284             ksearch.append("{0}@protonmail.ch".format(uid))
1285             ksearch.append("{0}@pm.me".format(uid))
1286     elif keyterm.count("@") > 2:
1287         uidlist = keyterm.split("@")
1288         for uid in uidlist:
1289             ksearch.append("{0}@protonmail.com".format(uid))
1290             ksearch.append("{0}@protonmail.ch".format(uid))
1291             ksearch.append("{0}@pm.me".format(uid))
1292     else:
1293         ksearch.append(keyterm)
1294
1295 for k in ksearch:
1296     print("Checking for key for: {0}".format(k))
1297     try:
1298         keys = server.search(k)
1299         if isinstance(keys, list) is True:
1300             for key in keys:
1301                 allkeys.append(key)
1302                 try:
1303                     import_result = c.key_import(key.key_blob)
1304                 except Exception as e:
1305                     import_result = c.key_import(key.key)
1306         else:
1307             paradox.append(keys)
1308             import_result = None
1309     except Exception as e:
1310         import_result = None
1311     results.append(import_result)
1312
1313 for result in results:
1314     if result is not None and hasattr(result, "considered") is False:
1315         print("{0} for {1}".format(result.decode(), k))
1316     elif result is not None and hasattr(result, "considered") is True:
1317         num_keys = len(result.imports)
1318         new_revs = result.new_revocations
1319         new_sigs = result.new_signatures
1320         new_subs = result.new_sub_keys
1321         new_uids = result.new_user_ids
1322         new_scrt = result.secret_imported
1323         nochange = result.unchanged
1324         print("""
1325 The total number of keys considered for import was:  {0}
1326
1327 With UIDs wholely or partially matching the following string:
1328
1329         {1}
1330
1331    Number of keys revoked:  {2}
1332  Number of new signatures:  {3}
1333     Number of new subkeys:  {4}
1334    Number of new user IDs:  {5}
1335 Number of new secret keys:  {6}
1336  Number of unchanged keys:  {7}
1337
1338 The key IDs for all considered keys were:
1339 """.format(num_keys, k, new_revs, new_sigs, new_subs, new_uids, new_scrt,
1340            nochange))
1341         for i in range(num_keys):
1342             print(result.imports[i].fpr)
1343         print("")
1344     elif result is None:
1345         pass
1346 #+END_SRC
1347
1348
1349 **** Import from ProtonMail via HKP for Python Example no. 2
1350      :PROPERTIES:
1351      :CUSTOM_ID: import-hkp4py-pm2
1352      :END:
1353
1354 Like its counterpart above, this script can also be found with the
1355 rest of the examples, by the name pmkey-import-hkp-alt.py.
1356
1357 With this script a modicum of effort has been made to treat anything
1358 passed as a =homedir= which either does not exist or which is not a
1359 directory, as also being a pssible user ID to check for.  It's not
1360 guaranteed to pick up on all such cases, but it should cover most of
1361 them.
1362
1363 #+BEGIN_SRC python -i
1364 import gpg
1365 import hkp4py
1366 import os.path
1367 import sys
1368
1369 print("""
1370 This script searches the ProtonMail key server for the specified key and
1371 imports it.  Optionally enables specifying a different GnuPG home directory.
1372
1373 Usage:  pmkey-import-hkp.py [homedir] [search string]
1374    or:  pmkey-import-hkp.py [search string]
1375 """)
1376
1377 c = gpg.Context(armor=True)
1378 server = hkp4py.KeyServer("hkps://api.protonmail.ch")
1379 keyterms = []
1380 ksearch = []
1381 allkeys = []
1382 results = []
1383 paradox = []
1384 homeless = None
1385
1386 if len(sys.argv) > 3:
1387     homedir = sys.argv[1]
1388     keyterms = sys.argv[2:]
1389 elif len(sys.argv) == 3:
1390     homedir = sys.argv[1]
1391     keyterm = sys.argv[2]
1392     keyterms.append(keyterm)
1393 elif len(sys.argv) == 2:
1394     homedir = ""
1395     keyterm = sys.argv[1]
1396     keyterms.append(keyterm)
1397 else:
1398     keyterm = input("Enter the key ID, UID or search string: ")
1399     homedir = input("Enter the GPG configuration directory path (optional): ")
1400     keyterms.append(keyterm)
1401
1402 if len(homedir) == 0:
1403     homedir = None
1404     homeless = False
1405
1406 if homedir is not None:
1407     if homedir.startswith("~"):
1408         if os.path.exists(os.path.expanduser(homedir)) is True:
1409             if os.path.isdir(os.path.expanduser(homedir)) is True:
1410                 c.home_dir = os.path.realpath(os.path.expanduser(homedir))
1411             else:
1412                 homeless = True
1413         else:
1414             homeless = True
1415     elif os.path.exists(os.path.realpath(homedir)) is True:
1416         if os.path.isdir(os.path.realpath(homedir)) is True:
1417             c.home_dir = os.path.realpath(homedir)
1418         else:
1419             homeless = True
1420     else:
1421         homeless = True
1422
1423 # First check to see if the homedir really is a homedir and if not, treat it as
1424 # a search string.
1425 if homeless is True:
1426     keyterms.append(homedir)
1427     c.home_dir = None
1428 else:
1429     pass
1430
1431 for keyterm in keyterms:
1432     if keyterm.count("@") == 2 and keyterm.startswith("@") is True:
1433         ksearch.append(keyterm[1:])
1434         ksearch.append(keyterm[1:])
1435         ksearch.append(keyterm[1:])
1436     elif keyterm.count("@") == 1 and keyterm.startswith("@") is True:
1437         ksearch.append("{0}@protonmail.com".format(keyterm[1:]))
1438         ksearch.append("{0}@protonmail.ch".format(keyterm[1:]))
1439         ksearch.append("{0}@pm.me".format(keyterm[1:]))
1440     elif keyterm.count("@") == 0:
1441         ksearch.append("{0}@protonmail.com".format(keyterm))
1442         ksearch.append("{0}@protonmail.ch".format(keyterm))
1443         ksearch.append("{0}@pm.me".format(keyterm))
1444     elif keyterm.count("@") == 2 and keyterm.startswith("@") is False:
1445         uidlist = keyterm.split("@")
1446         for uid in uidlist:
1447             ksearch.append("{0}@protonmail.com".format(uid))
1448             ksearch.append("{0}@protonmail.ch".format(uid))
1449             ksearch.append("{0}@pm.me".format(uid))
1450     elif keyterm.count("@") > 2:
1451         uidlist = keyterm.split("@")
1452         for uid in uidlist:
1453             ksearch.append("{0}@protonmail.com".format(uid))
1454             ksearch.append("{0}@protonmail.ch".format(uid))
1455             ksearch.append("{0}@pm.me".format(uid))
1456     else:
1457         ksearch.append(keyterm)
1458
1459 for k in ksearch:
1460     print("Checking for key for: {0}".format(k))
1461     try:
1462         keys = server.search(k)
1463         if isinstance(keys, list) is True:
1464             for key in keys:
1465                 allkeys.append(key)
1466                 try:
1467                     import_result = c.key_import(key.key_blob)
1468                 except Exception as e:
1469                     import_result = c.key_import(key.key)
1470         else:
1471             paradox.append(keys)
1472             import_result = None
1473     except Exception as e:
1474         import_result = None
1475     results.append(import_result)
1476
1477 for result in results:
1478     if result is not None and hasattr(result, "considered") is False:
1479         print("{0} for {1}".format(result.decode(), k))
1480     elif result is not None and hasattr(result, "considered") is True:
1481         num_keys = len(result.imports)
1482         new_revs = result.new_revocations
1483         new_sigs = result.new_signatures
1484         new_subs = result.new_sub_keys
1485         new_uids = result.new_user_ids
1486         new_scrt = result.secret_imported
1487         nochange = result.unchanged
1488         print("""
1489 The total number of keys considered for import was:  {0}
1490
1491 With UIDs wholely or partially matching the following string:
1492
1493         {1}
1494
1495    Number of keys revoked:  {2}
1496  Number of new signatures:  {3}
1497     Number of new subkeys:  {4}
1498    Number of new user IDs:  {5}
1499 Number of new secret keys:  {6}
1500  Number of unchanged keys:  {7}
1501
1502 The key IDs for all considered keys were:
1503 """.format(num_keys, k, new_revs, new_sigs, new_subs, new_uids, new_scrt,
1504            nochange))
1505         for i in range(num_keys):
1506             print(result.imports[i].fpr)
1507         print("")
1508     elif result is None:
1509         pass
1510 #+END_SRC
1511
1512
1513 ** Exporting keys
1514    :PROPERTIES:
1515    :CUSTOM_ID: howto-export-key
1516    :END:
1517
1518 Exporting keys remains a reasonably simple task, but has been
1519 separated into three different functions for the OpenPGP cryptographic
1520 engine.  Two of those functions are for exporting public keys and the
1521 third is for exporting secret keys.
1522
1523
1524 *** Exporting public keys
1525     :PROPERTIES:
1526     :CUSTOM_ID: howto-export-public-key
1527     :END:
1528
1529 There are two methods of exporting public keys, both of which are very
1530 similar to the other.  The default method, =key_export()=, will export
1531 a public key or keys matching a specified pattern as normal.  The
1532 alternative, the =key_export_minimal()= method, will do the same thing
1533 except producing a minimised output with extra signatures and third
1534 party signatures or certifications removed.
1535
1536 #+BEGIN_SRC python -i
1537 import gpg
1538 import os.path
1539 import sys
1540
1541 print("""
1542 This script exports one or more public keys.
1543 """)
1544
1545 c = gpg.Context(armor=True)
1546
1547 if len(sys.argv) >= 4:
1548     keyfile = sys.argv[1]
1549     logrus = sys.argv[2]
1550     homedir = sys.argv[3]
1551 elif len(sys.argv) == 3:
1552     keyfile = sys.argv[1]
1553     logrus = sys.argv[2]
1554     homedir = input("Enter the GPG configuration directory path (optional): ")
1555 elif len(sys.argv) == 2:
1556     keyfile = sys.argv[1]
1557     logrus = input("Enter the UID matching the key(s) to export: ")
1558     homedir = input("Enter the GPG configuration directory path (optional): ")
1559 else:
1560     keyfile = input("Enter the path and filename to save the secret key to: ")
1561     logrus = input("Enter the UID matching the key(s) to export: ")
1562     homedir = input("Enter the GPG configuration directory path (optional): ")
1563
1564 if homedir.startswith("~"):
1565     if os.path.exists(os.path.expanduser(homedir)) is True:
1566         c.home_dir = os.path.expanduser(homedir)
1567     else:
1568         pass
1569 elif os.path.exists(homedir) is True:
1570     c.home_dir = homedir
1571 else:
1572     pass
1573
1574 try:
1575     result = c.key_export(pattern=logrus)
1576 except:
1577     result = c.key_export(pattern=None)
1578
1579 if result is not None:
1580     with open(keyfile, "wb") as f:
1581         f.write(result)
1582 else:
1583     pass
1584 #+END_SRC
1585
1586 It should be noted that the result will only return =None= when a
1587 search pattern has been entered, but has not matched any keys.  When
1588 the search pattern itself is set to =None= this triggers the exporting
1589 of the entire public keybox.
1590
1591 #+BEGIN_SRC python -i
1592 import gpg
1593 import os.path
1594 import sys
1595
1596 print("""
1597 This script exports one or more public keys in minimised form.
1598 """)
1599
1600 c = gpg.Context(armor=True)
1601
1602 if len(sys.argv) >= 4:
1603     keyfile = sys.argv[1]
1604     logrus = sys.argv[2]
1605     homedir = sys.argv[3]
1606 elif len(sys.argv) == 3:
1607     keyfile = sys.argv[1]
1608     logrus = sys.argv[2]
1609     homedir = input("Enter the GPG configuration directory path (optional): ")
1610 elif len(sys.argv) == 2:
1611     keyfile = sys.argv[1]
1612     logrus = input("Enter the UID matching the key(s) to export: ")
1613     homedir = input("Enter the GPG configuration directory path (optional): ")
1614 else:
1615     keyfile = input("Enter the path and filename to save the secret key to: ")
1616     logrus = input("Enter the UID matching the key(s) to export: ")
1617     homedir = input("Enter the GPG configuration directory path (optional): ")
1618
1619 if homedir.startswith("~"):
1620     if os.path.exists(os.path.expanduser(homedir)) is True:
1621         c.home_dir = os.path.expanduser(homedir)
1622     else:
1623         pass
1624 elif os.path.exists(homedir) is True:
1625     c.home_dir = homedir
1626 else:
1627     pass
1628
1629 try:
1630     result = c.key_export_minimal(pattern=logrus)
1631 except:
1632     result = c.key_export_minimal(pattern=None)
1633
1634 if result is not None:
1635     with open(keyfile, "wb") as f:
1636         f.write(result)
1637 else:
1638     pass
1639 #+END_SRC
1640
1641
1642 *** Exporting secret keys
1643     :PROPERTIES:
1644     :CUSTOM_ID: howto-export-secret-key
1645     :END:
1646
1647 Exporting secret keys is, functionally, very similar to exporting
1648 public keys; save for the invocation of =pinentry= via =gpg-agent= in
1649 order to securely enter the key's passphrase and authorise the export.
1650
1651 The following example exports the secret key to a file which is then
1652 set with the same permissions as the output files created by the
1653 command line secret key export options.
1654
1655 #+BEGIN_SRC python -i
1656 import gpg
1657 import os
1658 import os.path
1659 import sys
1660
1661 print("""
1662 This script exports one or more secret keys.
1663
1664 The gpg-agent and pinentry are invoked to authorise the export.
1665 """)
1666
1667 c = gpg.Context(armor=True)
1668
1669 if len(sys.argv) >= 4:
1670     keyfile = sys.argv[1]
1671     logrus = sys.argv[2]
1672     homedir = sys.argv[3]
1673 elif len(sys.argv) == 3:
1674     keyfile = sys.argv[1]
1675     logrus = sys.argv[2]
1676     homedir = input("Enter the GPG configuration directory path (optional): ")
1677 elif len(sys.argv) == 2:
1678     keyfile = sys.argv[1]
1679     logrus = input("Enter the UID matching the secret key(s) to export: ")
1680     homedir = input("Enter the GPG configuration directory path (optional): ")
1681 else:
1682     keyfile = input("Enter the path and filename to save the secret key to: ")
1683     logrus = input("Enter the UID matching the secret key(s) to export: ")
1684     homedir = input("Enter the GPG configuration directory path (optional): ")
1685
1686 if len(homedir) == 0:
1687     homedir = None
1688 elif homedir.startswith("~"):
1689     userdir = os.path.expanduser(homedir)
1690     if os.path.exists(userdir) is True:
1691         homedir = os.path.realpath(userdir)
1692     else:
1693         homedir = None
1694 else:
1695     homedir = os.path.realpath(homedir)
1696
1697 if os.path.exists(homedir) is False:
1698     homedir = None
1699 else:
1700     if os.path.isdir(homedir) is False:
1701         homedir = None
1702     else:
1703         pass
1704
1705 if homedir is not None:
1706     c.home_dir = homedir
1707 else:
1708     pass
1709
1710 try:
1711     result = c.key_export_secret(pattern=logrus)
1712 except:
1713     result = c.key_export_secret(pattern=None)
1714
1715 if result is not None:
1716     with open(keyfile, "wb") as f:
1717         f.write(result)
1718     os.chmod(keyfile, 0o600)
1719 else:
1720     pass
1721 #+END_SRC
1722
1723 Alternatively the approach of the following script can be used.  This
1724 longer example saves the exported secret key(s) in files in the GnuPG
1725 home directory, in addition to setting the file permissions as only
1726 readable and writable by the user.  It also exports the secret key(s)
1727 twice in order to output both GPG binary (=.gpg=) and ASCII armoured
1728 (=.asc=) files.
1729
1730 #+BEGIN_SRC python -i
1731 import gpg
1732 import os
1733 import os.path
1734 import subprocess
1735 import sys
1736
1737 print("""
1738 This script exports one or more secret keys as both ASCII armored and binary
1739 file formats, saved in files within the user's GPG home directory.
1740
1741 The gpg-agent and pinentry are invoked to authorise the export.
1742 """)
1743
1744 if sys.platform == "win32":
1745     gpgconfcmd = "gpgconf.exe --list-dirs homedir"
1746 else:
1747     gpgconfcmd = "gpgconf --list-dirs homedir"
1748
1749 a = gpg.Context(armor=True)
1750 b = gpg.Context()
1751 c = gpg.Context()
1752
1753 if len(sys.argv) >= 4:
1754     keyfile = sys.argv[1]
1755     logrus = sys.argv[2]
1756     homedir = sys.argv[3]
1757 elif len(sys.argv) == 3:
1758     keyfile = sys.argv[1]
1759     logrus = sys.argv[2]
1760     homedir = input("Enter the GPG configuration directory path (optional): ")
1761 elif len(sys.argv) == 2:
1762     keyfile = sys.argv[1]
1763     logrus = input("Enter the UID matching the secret key(s) to export: ")
1764     homedir = input("Enter the GPG configuration directory path (optional): ")
1765 else:
1766     keyfile = input("Enter the filename to save the secret key to: ")
1767     logrus = input("Enter the UID matching the secret key(s) to export: ")
1768     homedir = input("Enter the GPG configuration directory path (optional): ")
1769
1770 if len(homedir) == 0:
1771     homedir = None
1772 elif homedir.startswith("~"):
1773     userdir = os.path.expanduser(homedir)
1774     if os.path.exists(userdir) is True:
1775         homedir = os.path.realpath(userdir)
1776     else:
1777         homedir = None
1778 else:
1779     homedir = os.path.realpath(homedir)
1780
1781 if os.path.exists(homedir) is False:
1782     homedir = None
1783 else:
1784     if os.path.isdir(homedir) is False:
1785         homedir = None
1786     else:
1787         pass
1788
1789 if homedir is not None:
1790     c.home_dir = homedir
1791 else:
1792     pass
1793
1794 if c.home_dir is not None:
1795     if c.home_dir.endswith("/"):
1796         gpgfile = "{0}{1}.gpg".format(c.home_dir, keyfile)
1797         ascfile = "{0}{1}.asc".format(c.home_dir, keyfile)
1798     else:
1799         gpgfile = "{0}/{1}.gpg".format(c.home_dir, keyfile)
1800         ascfile = "{0}/{1}.asc".format(c.home_dir, keyfile)
1801 else:
1802     if os.path.exists(os.environ["GNUPGHOME"]) is True:
1803         hd = os.environ["GNUPGHOME"]
1804     else:
1805         try:
1806             hd = subprocess.getoutput(gpgconfcmd)
1807         except:
1808             process = subprocess.Popen(gpgconfcmd.split(),
1809                                        stdout=subprocess.PIPE)
1810             procom = process.communicate()
1811             if sys.version_info[0] == 2:
1812                 hd = procom[0].strip()
1813             else:
1814                 hd = procom[0].decode().strip()
1815     gpgfile = "{0}/{1}.gpg".format(hd, keyfile)
1816     ascfile = "{0}/{1}.asc".format(hd, keyfile)
1817
1818 try:
1819     a_result = a.key_export_secret(pattern=logrus)
1820     b_result = b.key_export_secret(pattern=logrus)
1821 except:
1822     a_result = a.key_export_secret(pattern=None)
1823     b_result = b.key_export_secret(pattern=None)
1824
1825 if a_result is not None:
1826     with open(ascfile, "wb") as f:
1827         f.write(a_result)
1828     os.chmod(ascfile, 0o600)
1829 else:
1830     pass
1831
1832 if b_result is not None:
1833     with open(gpgfile, "wb") as f:
1834         f.write(b_result)
1835     os.chmod(gpgfile, 0o600)
1836 else:
1837     pass
1838 #+END_SRC
1839
1840
1841 *** Sending public keys to the SKS Keyservers
1842     :PROPERTIES:
1843     :CUSTOM_ID: howto-send-public-key
1844     :END:
1845
1846 As with the previous section on importing keys, the =hkp4py= module
1847 adds another option with exporting keys in order to send them to the
1848 public keyservers.
1849
1850 The following example demonstrates how this may be done.
1851
1852 #+BEGIN_SRC python -i
1853 import gpg
1854 import hkp4py
1855 import os.path
1856 import sys
1857
1858 print("""
1859 This script sends one or more public keys to the SKS keyservers and is
1860 essentially a slight variation on the export-key.py script.
1861 """)
1862
1863 c = gpg.Context(armor=True)
1864 server = hkp4py.KeyServer("hkps://hkps.pool.sks-keyservers.net")
1865
1866 if len(sys.argv) > 2:
1867     logrus = " ".join(sys.argv[1:])
1868 elif len(sys.argv) == 2:
1869     logrus = sys.argv[1]
1870 else:
1871     logrus = input("Enter the UID matching the key(s) to send: ")
1872
1873 if len(logrus) > 0:
1874     try:
1875         export_result = c.key_export(pattern=logrus)
1876     except Exception as e:
1877         print(e)
1878         export_result = None
1879 else:
1880     export_result = c.key_export(pattern=None)
1881
1882 if export_result is not None:
1883     try:
1884         try:
1885             send_result = server.add(export_result)
1886         except:
1887             send_result = server.add(export_result.decode())
1888         if send_result is not None:
1889             print(send_result)
1890         else:
1891             pass
1892     except Exception as e:
1893         print(e)
1894 else:
1895     pass
1896 #+END_SRC
1897
1898 An expanded version of this script with additional functions for
1899 specifying an alternative homedir location is in the examples
1900 directory as =send-key-to-keyserver.py=.
1901
1902 The =hkp4py= module appears to handle both string and byte literal text
1903 data equally well, but the GPGME bindings deal primarily with byte
1904 literal data only and so this script sends in that format first, then
1905 tries the string literal form.
1906
1907
1908 * Basic Functions
1909   :PROPERTIES:
1910   :CUSTOM_ID: howto-the-basics
1911   :END:
1912
1913 The most frequently called features of any cryptographic library will
1914 be the most fundamental tasks for encryption software.  In this
1915 section we will look at how to programmatically encrypt data, decrypt
1916 it, sign it and verify signatures.
1917
1918
1919 ** Encryption
1920    :PROPERTIES:
1921    :CUSTOM_ID: howto-basic-encryption
1922    :END:
1923
1924 Encrypting is very straight forward.  In the first example below the
1925 message, =text=, is encrypted to a single recipient's key.  In the
1926 second example the message will be encrypted to multiple recipients.
1927
1928
1929 *** Encrypting to one key
1930     :PROPERTIES:
1931     :CUSTOM_ID: howto-basic-encryption-single
1932     :END:
1933
1934 Once the the Context is set the main issues with encrypting data is
1935 essentially reduced to key selection and the keyword arguments
1936 specified in the =gpg.Context().encrypt()= method.
1937
1938 Those keyword arguments are: =recipients=, a list of keys encrypted to
1939 (covered in greater detail in the following section); =sign=, whether
1940 or not to sign the plaintext data, see subsequent sections on signing
1941 and verifying signatures below (defaults to =True=); =sink=, to write
1942 results or partial results to a secure sink instead of returning it
1943 (defaults to =None=); =passphrase=, only used when utilising symmetric
1944 encryption (defaults to =None=); =always_trust=, used to override the
1945 trust model settings for recipient keys (defaults to =False=);
1946 =add_encrypt_to=, utilises any preconfigured =encrypt-to= or
1947 =default-key= settings in the user's =gpg.conf= file (defaults to
1948 =False=); =prepare=, prepare for encryption (defaults to =False=);
1949 =expect_sign=, prepare for signing (defaults to =False=); =compress=,
1950 compresses the plaintext prior to encryption (defaults to =True=).
1951
1952 #+BEGIN_SRC python -i
1953 import gpg
1954
1955 a_key = "0x12345678DEADBEEF"
1956 text = b"""Some text to test with.
1957
1958 Since the text in this case must be bytes, it is most likely that
1959 the input form will be a separate file which is opened with "rb"
1960 as this is the simplest method of obtaining the correct data format.
1961 """
1962
1963 c = gpg.Context(armor=True)
1964 rkey = list(c.keylist(pattern=a_key, secret=False))
1965 ciphertext, result, sign_result = c.encrypt(text, recipients=rkey, sign=False)
1966
1967 with open("secret_plans.txt.asc", "wb") as afile:
1968     afile.write(ciphertext)
1969 #+END_SRC
1970
1971 Though this is even more likely to be used like this; with the
1972 plaintext input read from a file, the recipient keys used for
1973 encryption regardless of key trust status and the encrypted output
1974 also encrypted to any preconfigured keys set in the =gpg.conf= file:
1975
1976 #+BEGIN_SRC python -i
1977 import gpg
1978
1979 a_key = "0x12345678DEADBEEF"
1980
1981 with open("secret_plans.txt", "rb") as afile:
1982     text = afile.read()
1983
1984 c = gpg.Context(armor=True)
1985 rkey = list(c.keylist(pattern=a_key, secret=False))
1986 ciphertext, result, sign_result = c.encrypt(text, recipients=rkey, sign=True,
1987                                             always_trust=True,
1988                                             add_encrypt_to=True)
1989
1990 with open("secret_plans.txt.asc", "wb") as afile:
1991     afile.write(ciphertext)
1992 #+END_SRC
1993
1994 If the =recipients= parameter is empty then the plaintext is encrypted
1995 symmetrically.  If no =passphrase= is supplied as a parameter or via a
1996 callback registered with the =Context()= then an out-of-band prompt
1997 for the passphrase via pinentry will be invoked.
1998
1999
2000 *** Encrypting to multiple keys
2001     :PROPERTIES:
2002     :CUSTOM_ID: howto-basic-encryption-multiple
2003     :END:
2004
2005 Encrypting to multiple keys essentially just expands upon the key
2006 selection process and the recipients from the previous examples.
2007
2008 The following example encrypts a message (=text=) to everyone with an
2009 email address on the =gnupg.org= domain,[fn:4] but does /not/ encrypt
2010 to a default key or other key which is configured to normally encrypt
2011 to.
2012
2013 #+BEGIN_SRC python -i
2014 import gpg
2015
2016 text = b"""Oh look, another test message.
2017
2018 The same rules apply as with the previous example and more likely
2019 than not, the message will actually be drawn from reading the
2020 contents of a file or, maybe, from entering data at an input()
2021 prompt.
2022
2023 Since the text in this case must be bytes, it is most likely that
2024 the input form will be a separate file which is opened with "rb"
2025 as this is the simplest method of obtaining the correct data
2026 format.
2027 """
2028
2029 c = gpg.Context(armor=True)
2030 rpattern = list(c.keylist(pattern="@gnupg.org", secret=False))
2031 logrus = []
2032
2033 for i in range(len(rpattern)):
2034     if rpattern[i].can_encrypt == 1:
2035         logrus.append(rpattern[i])
2036
2037 ciphertext, result, sign_result = c.encrypt(text, recipients=logrus,
2038                                             sign=False, always_trust=True)
2039
2040 with open("secret_plans.txt.asc", "wb") as afile:
2041     afile.write(ciphertext)
2042 #+END_SRC
2043
2044 All it would take to change the above example to sign the message
2045 and also encrypt the message to any configured default keys would
2046 be to change the =c.encrypt= line to this:
2047
2048 #+BEGIN_SRC python -i
2049 ciphertext, result, sign_result = c.encrypt(text, recipients=logrus,
2050                                             always_trust=True,
2051                                             add_encrypt_to=True)
2052 #+END_SRC
2053
2054 The only keyword arguments requiring modification are those for which
2055 the default values are changing.  The default value of =sign= is
2056 =True=, the default of =always_trust= is =False=, the default of
2057 =add_encrypt_to= is =False=.
2058
2059 If =always_trust= is not set to =True= and any of the recipient keys
2060 are not trusted (e.g. not signed or locally signed) then the
2061 encryption will raise an error.  It is possible to mitigate this
2062 somewhat with something more like this:
2063
2064 #+BEGIN_SRC python -i
2065 import gpg
2066
2067 with open("secret_plans.txt.asc", "rb") as afile:
2068     text = afile.read()
2069
2070 c = gpg.Context(armor=True)
2071 rpattern = list(c.keylist(pattern="@gnupg.org", secret=False))
2072 logrus = []
2073
2074 for i in range(len(rpattern)):
2075     if rpattern[i].can_encrypt == 1:
2076         logrus.append(rpattern[i])
2077
2078     try:
2079         ciphertext, result, sign_result = c.encrypt(text, recipients=logrus,
2080                                                     add_encrypt_to=True)
2081     except gpg.errors.InvalidRecipients as e:
2082         for i in range(len(e.recipients)):
2083             for n in range(len(logrus)):
2084                 if logrus[n].fpr == e.recipients[i].fpr:
2085                     logrus.remove(logrus[n])
2086                 else:
2087                     pass
2088         try:
2089             ciphertext, result, sign_result = c.encrypt(text,
2090                                                         recipients=logrus,
2091                                                         add_encrypt_to=True)
2092             with open("secret_plans.txt.asc", "wb") as afile:
2093                 afile.write(ciphertext)
2094         except:
2095             pass
2096 #+END_SRC
2097
2098 This will attempt to encrypt to all the keys searched for, then remove
2099 invalid recipients if it fails and try again.
2100
2101
2102 ** Decryption
2103    :PROPERTIES:
2104    :CUSTOM_ID: howto-basic-decryption
2105    :END:
2106
2107 Decrypting something encrypted to a key in one's secret keyring is
2108 fairly straight forward.
2109
2110 In this example code, however, preconfiguring either =gpg.Context()=
2111 or =gpg.core.Context()= as =c= is unnecessary because there is no need
2112 to modify the Context prior to conducting the decryption and since the
2113 Context is only used once, setting it to =c= simply adds lines for no
2114 gain.
2115
2116 #+BEGIN_SRC python -i
2117 import gpg
2118
2119 ciphertext = input("Enter path and filename of encrypted file: ")
2120 newfile = input("Enter path and filename of file to save decrypted data to: ")
2121
2122 with open(ciphertext, "rb") as cfile:
2123     try:
2124         plaintext, result, verify_result = gpg.Context().decrypt(cfile)
2125     except gpg.errors.GPGMEError as e:
2126         plaintext = None
2127         print(e)
2128
2129 if plaintext is not None:
2130     with open(newfile, "wb") as nfile:
2131             nfile.write(plaintext)
2132     else:
2133         pass
2134 #+END_SRC
2135
2136 The data available in =plaintext= in this example is the decrypted
2137 content as a byte object, the recipient key IDs and algorithms in
2138 =result= and the results of verifying any signatures of the data in
2139 =verify_result=.
2140
2141 If =gpg.Context().decrypt(cfile, verify=False)= is called instead,
2142 then =verify_result= will be returned as =None= and the rest remains
2143 as described here.
2144
2145
2146 ** Signing text and files
2147    :PROPERTIES:
2148    :CUSTOM_ID: howto-basic-signing
2149    :END:
2150
2151 The following sections demonstrate how to specify keys to sign with.
2152
2153
2154 *** Signing key selection
2155     :PROPERTIES:
2156     :CUSTOM_ID: howto-basic-signing-signers
2157     :END:
2158
2159 By default GPGME and the Python bindings will use the default key
2160 configured for the user invoking the GPGME API.  If there is no
2161 default key specified and there is more than one secret key available
2162 it may be necessary to specify the key or keys with which to sign
2163 messages and files.
2164
2165 #+BEGIN_SRC python -i
2166 import gpg
2167
2168 logrus = input("Enter the email address or string to match signing keys to: ")
2169 hancock = gpg.Context().keylist(pattern=logrus, secret=True)
2170 sig_src = list(hancock)
2171 #+END_SRC
2172
2173 The signing examples in the following sections include the explicitly
2174 designated =signers= parameter in two of the five examples; once where
2175 the resulting signature would be ASCII armoured and once where it
2176 would not be armoured.
2177
2178 While it would be possible to enter a key ID or fingerprint here to
2179 match a specific key, it is not possible to enter two fingerprints and
2180 match two keys since the patten expects a string, bytes or None and
2181 not a list.  A string with two fingerprints won't match any single
2182 key.
2183
2184
2185 *** Normal or default signing messages or files
2186     :PROPERTIES:
2187     :CUSTOM_ID: howto-basic-signing-normal
2188     :END:
2189
2190 The normal or default signing process is essentially the same as is
2191 most often invoked when also encrypting a message or file.  So when
2192 the encryption component is not utilised, the result is to produce an
2193 encoded and signed output which may or may not be ASCII armoured and
2194 which may or may not also be compressed.
2195
2196 By default compression will be used unless GnuPG detects that the
2197 plaintext is already compressed.  ASCII armouring will be determined
2198 according to the value of =gpg.Context().armor=.
2199
2200 The compression algorithm is selected in much the same way as the
2201 symmetric encryption algorithm or the hash digest algorithm is when
2202 multiple keys are involved; from the preferences saved into the key
2203 itself or by comparison with the preferences with all other keys
2204 involved.
2205
2206 #+BEGIN_SRC python -i
2207 import gpg
2208
2209 text0 = """Declaration of ... something.
2210
2211 """
2212 text = text0.encode()
2213
2214 c = gpg.Context(armor=True, signers=sig_src)
2215 signed_data, result = c.sign(text, mode=gpg.constants.sig.mode.NORMAL)
2216
2217 with open("/path/to/statement.txt.asc", "w") as afile:
2218     afile.write(signed_data.decode())
2219 #+END_SRC
2220
2221 Though everything in this example is accurate, it is more likely that
2222 reading the input data from another file and writing the result to a
2223 new file will be performed more like the way it is done in the next
2224 example.  Even if the output format is ASCII armoured.
2225
2226 #+BEGIN_SRC python -i
2227 import gpg
2228
2229 with open("/path/to/statement.txt", "rb") as tfile:
2230     text = tfile.read()
2231
2232 c = gpg.Context()
2233 signed_data, result = c.sign(text, mode=gpg.constants.sig.mode.NORMAL)
2234
2235 with open("/path/to/statement.txt.sig", "wb") as afile:
2236     afile.write(signed_data)
2237 #+END_SRC
2238
2239
2240 *** Detached signing messages and files
2241     :PROPERTIES:
2242     :CUSTOM_ID: howto-basic-signing-detached
2243     :END:
2244
2245 Detached signatures will often be needed in programmatic uses of
2246 GPGME, either for signing files (e.g. tarballs of code releases) or as
2247 a component of message signing (e.g. PGP/MIME encoded email).
2248
2249 #+BEGIN_SRC python -i
2250 import gpg
2251
2252 text0 = """Declaration of ... something.
2253
2254 """
2255 text = text0.encode()
2256
2257 c = gpg.Context(armor=True)
2258 signed_data, result = c.sign(text, mode=gpg.constants.sig.mode.DETACH)
2259
2260 with open("/path/to/statement.txt.asc", "w") as afile:
2261     afile.write(signed_data.decode())
2262 #+END_SRC
2263
2264 As with normal signatures, detached signatures are best handled as
2265 byte literals, even when the output is ASCII armoured.
2266
2267 #+BEGIN_SRC python -i
2268 import gpg
2269
2270 with open("/path/to/statement.txt", "rb") as tfile:
2271     text = tfile.read()
2272
2273 c = gpg.Context(signers=sig_src)
2274 signed_data, result = c.sign(text, mode=gpg.constants.sig.mode.DETACH)
2275
2276 with open("/path/to/statement.txt.sig", "wb") as afile:
2277     afile.write(signed_data)
2278 #+END_SRC
2279
2280
2281 *** Clearsigning messages or text
2282     :PROPERTIES:
2283     :CUSTOM_ID: howto-basic-signing-clear
2284     :END:
2285
2286 Though PGP/in-line messages are no longer encouraged in favour of
2287 PGP/MIME, there is still sometimes value in utilising in-line
2288 signatures.  This is where clear-signed messages or text is of value.
2289
2290 #+BEGIN_SRC python -i
2291 import gpg
2292
2293 text0 = """Declaration of ... something.
2294
2295 """
2296 text = text0.encode()
2297
2298 c = gpg.Context()
2299 signed_data, result = c.sign(text, mode=gpg.constants.sig.mode.CLEAR)
2300
2301 with open("/path/to/statement.txt.asc", "w") as afile:
2302     afile.write(signed_data.decode())
2303 #+END_SRC
2304
2305 In spite of the appearance of a clear-signed message, the data handled
2306 by GPGME in signing it must still be byte literals.
2307
2308 #+BEGIN_SRC python -i
2309 import gpg
2310
2311 with open("/path/to/statement.txt", "rb") as tfile:
2312     text = tfile.read()
2313
2314 c = gpg.Context()
2315 signed_data, result = c.sign(text, mode=gpg.constants.sig.mode.CLEAR)
2316
2317 with open("/path/to/statement.txt.asc", "wb") as afile:
2318     afile.write(signed_data)
2319 #+END_SRC
2320
2321
2322 ** Signature verification
2323    :PROPERTIES:
2324    :CUSTOM_ID: howto-basic-verification
2325    :END:
2326
2327 Essentially there are two principal methods of verification of a
2328 signature.  The first of these is for use with the normal or default
2329 signing method and for clear-signed messages.  The second is for use
2330 with files and data with detached signatures.
2331
2332 The following example is intended for use with the default signing
2333 method where the file was not ASCII armoured:
2334
2335 #+BEGIN_SRC python -i
2336 import gpg
2337 import time
2338
2339 filename = "statement.txt"
2340 gpg_file = "statement.txt.gpg"
2341
2342 c = gpg.Context()
2343
2344 try:
2345     data, result = c.verify(open(gpg_file))
2346     verified = True
2347 except gpg.errors.BadSignatures as e:
2348     verified = False
2349     print(e)
2350
2351 if verified is True:
2352     for i in range(len(result.signatures)):
2353         sign = result.signatures[i]
2354         print("""Good signature from:
2355 {0}
2356 with key {1}
2357 made at {2}
2358 """.format(c.get_key(sign.fpr).uids[0].uid, sign.fpr,
2359            time.ctime(sign.timestamp)))
2360 else:
2361     pass
2362 #+END_SRC
2363
2364 Whereas this next example, which is almost identical would work with
2365 normal ASCII armoured files and with clear-signed files:
2366
2367 #+BEGIN_SRC python -i
2368 import gpg
2369 import time
2370
2371 filename = "statement.txt"
2372 asc_file = "statement.txt.asc"
2373
2374 c = gpg.Context()
2375
2376 try:
2377     data, result = c.verify(open(asc_file))
2378     verified = True
2379 except gpg.errors.BadSignatures as e:
2380     verified = False
2381     print(e)
2382
2383 if verified is True:
2384     for i in range(len(result.signatures)):
2385         sign = result.signatures[i]
2386         print("""Good signature from:
2387 {0}
2388 with key {1}
2389 made at {2}
2390 """.format(c.get_key(sign.fpr).uids[0].uid, sign.fpr,
2391            time.ctime(sign.timestamp)))
2392 else:
2393     pass
2394 #+END_SRC
2395
2396 In both of the previous examples it is also possible to compare the
2397 original data that was signed against the signed data in =data= to see
2398 if it matches with something like this:
2399
2400 #+BEGIN_SRC python -i
2401 with open(filename, "rb") as afile:
2402     text = afile.read()
2403
2404 if text == data:
2405     print("Good signature.")
2406 else:
2407     pass
2408 #+END_SRC
2409
2410 The following two examples, however, deal with detached signatures.
2411 With his method of verification the data that was signed does not get
2412 returned since it is already being explicitly referenced in the first
2413 argument of =c.verify=.  So =data= is =None= and only the information
2414 in =result= is available.
2415
2416 #+BEGIN_SRC python -i
2417 import gpg
2418 import time
2419
2420 filename = "statement.txt"
2421 sig_file = "statement.txt.sig"
2422
2423 c = gpg.Context()
2424
2425 try:
2426     data, result = c.verify(open(filename), open(sig_file))
2427     verified = True
2428 except gpg.errors.BadSignatures as e:
2429     verified = False
2430     print(e)
2431
2432 if verified is True:
2433     for i in range(len(result.signatures)):
2434         sign = result.signatures[i]
2435         print("""Good signature from:
2436 {0}
2437 with key {1}
2438 made at {2}
2439 """.format(c.get_key(sign.fpr).uids[0].uid, sign.fpr,
2440            time.ctime(sign.timestamp)))
2441 else:
2442     pass
2443 #+END_SRC
2444
2445 #+BEGIN_SRC python -i
2446 import gpg
2447 import time
2448
2449 filename = "statement.txt"
2450 asc_file = "statement.txt.asc"
2451
2452 c = gpg.Context()
2453
2454 try:
2455     data, result = c.verify(open(filename), open(asc_file))
2456     verified = True
2457 except gpg.errors.BadSignatures as e:
2458     verified = False
2459     print(e)
2460
2461 if verified is True:
2462     for i in range(len(result.signatures)):
2463         sign = result.signatures[i]
2464         print("""Good signature from:
2465 {0}
2466 with key {1}
2467 made at {2}
2468 """.format(c.get_key(sign.fpr).uids[0].uid, sign.fpr,
2469            time.ctime(sign.timestamp)))
2470 else:
2471     pass
2472 #+END_SRC
2473
2474
2475 * Creating keys and subkeys
2476   :PROPERTIES:
2477   :CUSTOM_ID: key-generation
2478   :END:
2479
2480 The one thing, aside from GnuPG itself, that GPGME depends on, of
2481 course, is the keys themselves.  So it is necessary to be able to
2482 generate them and modify them by adding subkeys, revoking or disabling
2483 them, sometimes deleting them and doing the same for user IDs.
2484
2485 In the following examples a key will be created for the world's
2486 greatest secret agent, Danger Mouse.  Since Danger Mouse is a secret
2487 agent he needs to be able to protect information to =SECRET= level
2488 clearance, so his keys will be 3072-bit keys.
2489
2490 The pre-configured =gpg.conf= file which sets cipher, digest and other
2491 preferences contains the following configuration parameters:
2492
2493 #+BEGIN_SRC conf
2494   expert
2495   allow-freeform-uid
2496   allow-secret-key-import
2497   trust-model tofu+pgp
2498   tofu-default-policy unknown
2499   enable-large-rsa
2500   enable-dsa2
2501   cert-digest-algo SHA512
2502   default-preference-list TWOFISH CAMELLIA256 AES256 CAMELLIA192 AES192 CAMELLIA128 AES BLOWFISH IDEA CAST5 3DES SHA512 SHA384 SHA256 SHA224 RIPEMD160 SHA1 ZLIB BZIP2 ZIP Uncompressed
2503   personal-cipher-preferences TWOFISH CAMELLIA256 AES256 CAMELLIA192 AES192 CAMELLIA128 AES BLOWFISH IDEA CAST5 3DES
2504   personal-digest-preferences SHA512 SHA384 SHA256 SHA224 RIPEMD160 SHA1
2505   personal-compress-preferences ZLIB BZIP2 ZIP Uncompressed
2506 #+END_SRC
2507
2508
2509 ** Primary key
2510    :PROPERTIES:
2511    :CUSTOM_ID: keygen-primary
2512    :END:
2513
2514 Generating a primary key uses the =create_key= method in a Context.
2515 It contains multiple arguments and keyword arguments, including:
2516 =userid=, =algorithm=, =expires_in=, =expires=, =sign=, =encrypt=,
2517 =certify=, =authenticate=, =passphrase= and =force=.  The defaults for
2518 all of those except =userid=, =algorithm=, =expires_in=, =expires= and
2519 =passphrase= is =False=.  The defaults for =algorithm= and
2520 =passphrase= is =None=.  The default for =expires_in= is =0=.  The
2521 default for =expires= is =True=.  There is no default for =userid=.
2522
2523 If =passphrase= is left as =None= then the key will not be generated
2524 with a passphrase, if =passphrase= is set to a string then that will
2525 be the passphrase and if =passphrase= is set to =True= then gpg-agent
2526 will launch pinentry to prompt for a passphrase.  For the sake of
2527 convenience, these examples will keep =passphrase= set to =None=.
2528
2529 #+BEGIN_SRC python -i
2530 import gpg
2531
2532 c = gpg.Context()
2533
2534 c.home_dir = "~/.gnupg-dm"
2535 userid = "Danger Mouse <dm@secret.example.net>"
2536
2537 dmkey = c.create_key(userid, algorithm="rsa3072", expires_in=31536000,
2538                      sign=True, certify=True)
2539 #+END_SRC
2540
2541 One thing to note here is the use of setting the =c.home_dir=
2542 parameter.  This enables generating the key or keys in a different
2543 location.  In this case to keep the new key data created for this
2544 example in a separate location rather than adding it to existing and
2545 active key store data.  As with the default directory, =~/.gnupg=, any
2546 temporary or separate directory needs the permissions set to only
2547 permit access by the directory owner.  On posix systems this means
2548 setting the directory permissions to 700.
2549
2550 The =temp-homedir-config.py= script in the HOWTO examples directory
2551 will create an alternative homedir with these configuration options
2552 already set and the correct directory and file permissions.
2553
2554 The successful generation of the key can be confirmed via the returned
2555 =GenkeyResult= object, which includes the following data:
2556
2557 #+BEGIN_SRC python -i
2558 print("""
2559  Fingerprint:  {0}
2560  Primary Key:  {1}
2561   Public Key:  {2}
2562   Secret Key:  {3}
2563  Sub Key:  {4}
2564 User IDs:  {5}
2565 """.format(dmkey.fpr, dmkey.primary, dmkey.pubkey, dmkey.seckey, dmkey.sub,
2566            dmkey.uid))
2567 #+END_SRC
2568
2569 Alternatively the information can be confirmed using the command line
2570 program:
2571
2572 #+BEGIN_SRC shell
2573   bash-4.4$ gpg --homedir ~/.gnupg-dm -K
2574   ~/.gnupg-dm/pubring.kbx
2575   ----------------------
2576   sec   rsa3072 2018-03-15 [SC] [expires: 2019-03-15]
2577         177B7C25DB99745EE2EE13ED026D2F19E99E63AA
2578   uid           [ultimate] Danger Mouse <dm@secret.example.net>
2579
2580   bash-4.4$
2581 #+END_SRC
2582
2583 As with generating keys manually, to preconfigure expanded preferences
2584 for the cipher, digest and compression algorithms, the =gpg.conf= file
2585 must contain those details in the home directory in which the new key
2586 is being generated.  I used a cut down version of my own =gpg.conf=
2587 file in order to be able to generate this:
2588
2589 #+BEGIN_SRC shell
2590   bash-4.4$ gpg --homedir ~/.gnupg-dm --edit-key 177B7C25DB99745EE2EE13ED026D2F19E99E63AA showpref quit
2591   Secret key is available.
2592
2593   sec  rsa3072/026D2F19E99E63AA
2594        created: 2018-03-15  expires: 2019-03-15  usage: SC
2595        trust: ultimate      validity: ultimate
2596   [ultimate] (1). Danger Mouse <dm@secret.example.net>
2597
2598   [ultimate] (1). Danger Mouse <dm@secret.example.net>
2599        Cipher: TWOFISH, CAMELLIA256, AES256, CAMELLIA192, AES192, CAMELLIA128, AES, BLOWFISH, IDEA, CAST5, 3DES
2600        Digest: SHA512, SHA384, SHA256, SHA224, RIPEMD160, SHA1
2601        Compression: ZLIB, BZIP2, ZIP, Uncompressed
2602        Features: MDC, Keyserver no-modify
2603
2604   bash-4.4$
2605 #+END_SRC
2606
2607
2608 ** Subkeys
2609    :PROPERTIES:
2610    :CUSTOM_ID: keygen-subkeys
2611    :END:
2612
2613 Adding subkeys to a primary key is fairly similar to creating the
2614 primary key with the =create_subkey= method.  Most of the arguments
2615 are the same, but not quite all.  Instead of the =userid= argument
2616 there is now a =key= argument for selecting which primary key to add
2617 the subkey to.
2618
2619 In the following example an encryption subkey will be added to the
2620 primary key.  Since Danger Mouse is a security conscious secret agent,
2621 this subkey will only be valid for about six months, half the length
2622 of the primary key.
2623
2624 #+BEGIN_SRC python -i
2625 import gpg
2626
2627 c = gpg.Context()
2628 c.home_dir = "~/.gnupg-dm"
2629
2630 key = c.get_key(dmkey.fpr, secret=True)
2631 dmsub = c.create_subkey(key, algorithm="rsa3072", expires_in=15768000,
2632                         encrypt=True)
2633 #+END_SRC
2634
2635 As with the primary key, the results here can be checked with:
2636
2637 #+BEGIN_SRC python -i
2638 print("""
2639  Fingerprint:  {0}
2640  Primary Key:  {1}
2641   Public Key:  {2}
2642   Secret Key:  {3}
2643  Sub Key:  {4}
2644 User IDs:  {5}
2645 """.format(dmsub.fpr, dmsub.primary, dmsub.pubkey, dmsub.seckey, dmsub.sub,
2646            dmsub.uid))
2647 #+END_SRC
2648
2649 As well as on the command line with:
2650
2651 #+BEGIN_SRC shell
2652   bash-4.4$ gpg --homedir ~/.gnupg-dm -K
2653   ~/.gnupg-dm/pubring.kbx
2654   ----------------------
2655   sec   rsa3072 2018-03-15 [SC] [expires: 2019-03-15]
2656         177B7C25DB99745EE2EE13ED026D2F19E99E63AA
2657   uid           [ultimate] Danger Mouse <dm@secret.example.net>
2658   ssb   rsa3072 2018-03-15 [E] [expires: 2018-09-13]
2659
2660   bash-4.4$
2661 #+END_SRC
2662
2663
2664 ** User IDs
2665    :PROPERTIES:
2666    :CUSTOM_ID: keygen-uids
2667    :END:
2668
2669
2670 *** Adding User IDs
2671     :PROPERTIES:
2672     :CUSTOM_ID: keygen-uids-add
2673     :END:
2674
2675 By comparison to creating primary keys and subkeys, adding a new user
2676 ID to an existing key is much simpler.  The method used to do this is
2677 =key_add_uid= and the only arguments it takes are for the =key= and
2678 the new =uid=.
2679
2680 #+BEGIN_SRC python -i
2681 import gpg
2682
2683 c = gpg.Context()
2684 c.home_dir = "~/.gnupg-dm"
2685
2686 dmfpr = "177B7C25DB99745EE2EE13ED026D2F19E99E63AA"
2687 key = c.get_key(dmfpr, secret=True)
2688 uid = "Danger Mouse <danger.mouse@secret.example.net>"
2689
2690 c.key_add_uid(key, uid)
2691 #+END_SRC
2692
2693 Unsurprisingly the result of this is:
2694
2695 #+BEGIN_SRC shell
2696   bash-4.4$ gpg --homedir ~/.gnupg-dm -K
2697   ~/.gnupg-dm/pubring.kbx
2698   ----------------------
2699   sec   rsa3072 2018-03-15 [SC] [expires: 2019-03-15]
2700         177B7C25DB99745EE2EE13ED026D2F19E99E63AA
2701   uid           [ultimate] Danger Mouse <danger.mouse@secret.example.net>
2702   uid           [ultimate] Danger Mouse <dm@secret.example.net>
2703   ssb   rsa3072 2018-03-15 [E] [expires: 2018-09-13]
2704
2705   bash-4.4$
2706 #+END_SRC
2707
2708
2709 *** Revoking User IDs
2710     :PROPERTIES:
2711     :CUSTOM_ID: keygen-uids-revoke
2712     :END:
2713
2714 Revoking a user ID is a fairly similar process, except that it uses
2715 the =key_revoke_uid= method.
2716
2717 #+BEGIN_SRC python -i
2718 import gpg
2719
2720 c = gpg.Context()
2721 c.home_dir = "~/.gnupg-dm"
2722
2723 dmfpr = "177B7C25DB99745EE2EE13ED026D2F19E99E63AA"
2724 key = c.get_key(dmfpr, secret=True)
2725 uid = "Danger Mouse <danger.mouse@secret.example.net>"
2726
2727 c.key_revoke_uid(key, uid)
2728 #+END_SRC
2729
2730
2731 ** Key certification
2732    :PROPERTIES:
2733    :CUSTOM_ID: key-sign
2734    :END:
2735
2736 Since key certification is more frequently referred to as key signing,
2737 the method used to perform this function is =key_sign=.
2738
2739 The =key_sign= method takes four arguments: =key=, =uids=,
2740 =expires_in= and =local=.  The default value of =uids= is =None= and
2741 which results in all user IDs being selected.  The default value of
2742 both =expires_in= and =local= is =False=; which results in the
2743 signature never expiring and being able to be exported.
2744
2745 The =key= is the key being signed rather than the key doing the
2746 signing.  To change the key doing the signing refer to the signing key
2747 selection above for signing messages and files.
2748
2749 If the =uids= value is not =None= then it must either be a string to
2750 match a single user ID or a list of strings to match multiple user
2751 IDs.  In this case the matching of those strings must be precise and
2752 it is case sensitive.
2753
2754 To sign Danger Mouse's key for just the initial user ID with a
2755 signature which will last a little over a month, do this:
2756
2757 #+BEGIN_SRC python -i
2758 import gpg
2759
2760 c = gpg.Context()
2761 uid = "Danger Mouse <dm@secret.example.net>"
2762
2763 dmfpr = "177B7C25DB99745EE2EE13ED026D2F19E99E63AA"
2764 key = c.get_key(dmfpr, secret=True)
2765 c.key_sign(key, uids=uid, expires_in=2764800)
2766 #+END_SRC
2767
2768
2769 *** Verifying key certifications
2770     :PROPERTIES:
2771     :CUSTOM_ID: key-sign-verify
2772     :END:
2773
2774 #+BEGIN_SRC python -i
2775 import gpg
2776 import time
2777
2778 c = gpg.Context()
2779 dmfpr = "177B7C25DB99745EE2EE13ED026D2F19E99E63AA"
2780 keys = list(c.keylist(pattern=dmuid, mode=gpg.constants.keylist.mode.SIGS))
2781 key = keys[0]
2782
2783 for user in key.uids:
2784     for sig in user.signatures:
2785         print("0x{0}".format(sig.keyid), "", time.ctime(sig.timestamp), "",
2786               sig.uid)
2787 #+END_SRC
2788
2789 Which for Danger Mouse displays the following:
2790
2791 #+BEGIN_EXAMPLE
2792   0x92E3F6115435C65A  Thu Mar 15 13:17:44 2018  Danger Mouse <dm@secret.example.net>
2793   0x321E4E2373590E5D  Mon Nov 26 12:46:05 2018  Ben McGinnes <ben@adversary.org>
2794 #+END_EXAMPLE
2795
2796 The two key signatures listed are for the self-certification of Danger
2797 Mouse's key made when the key was created in March, 2018; and the
2798 second is a signature made by the author and set to expire at the end
2799 of the year.  Note that the second signature was made with the
2800 following code (including the preceding code to display the output of
2801 the certifications or key signatures):
2802
2803 #+BEGIN_SRC python -i
2804 import gpg
2805 import math
2806 import pendulum
2807 import time
2808
2809 hd = "/home/dm/.gnupg"
2810 c = gpg.Context()
2811 d = gpg.Context(home_dir=hd)
2812 dmfpr = "177B7C25DB99745EE2EE13ED026D2F19E99E63AA"
2813 dmuid = "Danger Mouse <dm@secret.example.net>"
2814 dkeys = list(c.keylist(pattern=dmuid))
2815 dmkey = dkeys[0]
2816
2817 c.key_import(d.key_export(pattern=None))
2818
2819 tp = pendulum.period(pendulum.now(tz="local"), pendulum.datetime(2019, 1, 1))
2820 ts = tp.total_seconds()
2821 total_secs = math.ceil(ts)
2822 c.key_sign(dmkey, uids=dmuid, expires_in=total_secs)
2823
2824 d.key_import(c.key_export(pattern=dmuid))
2825 keys = list(c.keylist(pattern=dmuid, mode=gpg.constants.keylist.mode.SIGS))
2826 key = keys[0]
2827
2828 for user in key.uids:
2829     for sig in user.signatures:
2830         print("0x{0}".format(sig.keyid), "", time.ctime(sig.timestamp), "",
2831               sig.uid)
2832 #+END_SRC
2833
2834 Note that this final code block includes the use of a module which is
2835 /not/ part of Python's standard library, the [[https://pendulum.eustace.io/][pendulum module]].  Unlike
2836 the standard datetime module, pendulum makes working with dates and
2837 times significantly easier in Python; just as the requests module
2838 makes working with HTTP and HTTPS easier than the builtin modules do.
2839
2840 Though neither requests nor pendulum are required modules for using
2841 the GPGME Python bindings, they are both highly recommended more
2842 generally.
2843
2844
2845 * Advanced or Experimental Use Cases
2846   :PROPERTIES:
2847   :CUSTOM_ID: advanced-use
2848   :END:
2849
2850
2851 ** C plus Python plus SWIG plus Cython
2852    :PROPERTIES:
2853    :CUSTOM_ID: cython
2854    :END:
2855
2856 In spite of the apparent incongruence of using Python bindings to a C
2857 interface only to generate more C from the Python; it is in fact quite
2858 possible to use the GPGME bindings with [[http://docs.cython.org/en/latest/index.html][Cython]].  Though in many cases
2859 the benefits may not be obvious since the most computationally
2860 intensive work never leaves the level of the C code with which GPGME
2861 itself is interacting with.
2862
2863 Nevertheless, there are some situations where the benefits are
2864 demonstrable.  One of the better and easier examples being the one of
2865 the early examples in this HOWTO, the [[#howto-keys-counting][key counting]] code.  Running that
2866 example as an executable Python script, =keycount.py= (available in
2867 the =examples/howto/= directory), will take a noticeable amount of time
2868 to run on most systems where the public keybox or keyring contains a
2869 few thousand public keys.
2870
2871 Earlier in the evening, prior to starting this section, I ran that
2872 script on my laptop; as I tend to do periodically and timed it using
2873 =time= utility, with the following results:
2874
2875 #+BEGIN_SRC shell
2876   bash-4.4$ time keycount.py
2877
2878   Number of secret keys:  23
2879   Number of public keys:  12112
2880
2881
2882   real  11m52.945s
2883   user  0m0.913s
2884   sys   0m0.752s
2885
2886   bash-4.4$
2887 #+END_SRC
2888
2889 Sometime after that I imported another key and followed it with a
2890 little test of Cython.  This test was kept fairly basic, essentially
2891 lifting the material from the [[http://docs.cython.org/en/latest/src/tutorial/cython_tutorial.html][Cython Basic Tutorial]] to demonstrate
2892 compiling Python code to C.  The first step was to take the example
2893 key counting code quoted previously, essentially from the importing of
2894 the =gpg= module to the end of the script:
2895
2896 #+BEGIN_SRC python -i
2897 import gpg
2898
2899 c = gpg.Context()
2900 seckeys = c.keylist(pattern=None, secret=True)
2901 pubkeys = c.keylist(pattern=None, secret=False)
2902
2903 seclist = list(seckeys)
2904 secnum = len(seclist)
2905
2906 publist = list(pubkeys)
2907 pubnum = len(publist)
2908
2909 print("""
2910     Number of secret keys:  {0}
2911     Number of public keys:  {1}
2912
2913 """.format(secnum, pubnum))
2914 #+END_SRC
2915
2916 Save that into a file called =keycount.pyx= and then create a
2917 =setup.py= file which contains this:
2918
2919 #+BEGIN_SRC python -i
2920 from distutils.core import setup
2921 from Cython.Build import cythonize
2922
2923 setup(
2924     ext_modules = cythonize("keycount.pyx")
2925 )
2926 #+END_SRC
2927
2928 Compile it:
2929
2930 #+BEGIN_SRC shell
2931   bash-4.4$ python setup.py build_ext --inplace
2932   bash-4.4$
2933 #+END_SRC
2934
2935 Then run it in a similar manner to =keycount.py=:
2936
2937 #+BEGIN_SRC shell
2938   bash-4.4$ time python3.7 -c "import keycount"
2939
2940   Number of secret keys:  23
2941   Number of public keys:  12113
2942
2943
2944   real  6m47.905s
2945   user  0m0.785s
2946   sys   0m0.331s
2947
2948   bash-4.4$
2949 #+END_SRC
2950
2951 Cython turned =keycount.pyx= into an 81KB =keycount.o= file in the
2952 =build/= directory, a 24KB =keycount.cpython-37m-darwin.so= file to be
2953 imported into Python 3.7 and a 113KB =keycount.c= generated C source
2954 code file of nearly three thousand lines.  Quite a bit bigger than the
2955 314 bytes of the =keycount.pyx= file or the full 1,452 bytes of the
2956 full executable =keycount.py= example script.
2957
2958 On the other hand it ran in nearly half the time; taking 6 minutes and
2959 47.905 seconds to run.  As opposed to the 11 minutes and 52.945 seconds
2960 which the CPython script alone took.
2961
2962 The =keycount.pyx= and =setup.py= files used to generate this example
2963 have been added to the =examples/howto/advanced/cython/= directory
2964 The example versions include some additional options to annotate the
2965 existing code and to detect Cython's use.  The latter comes from the
2966 [[http://docs.cython.org/en/latest/src/tutorial/pure.html#magic-attributes-within-the-pxd][Magic Attributes]] section of the Cython documentation.
2967
2968
2969 * Miscellaneous extras and work-arounds
2970   :PROPERTIES:
2971   :CUSTOM_ID: cheats-and-hacks
2972   :END:
2973
2974 Most of the things in the following sections are here simply because
2975 there was no better place to put them, even though some are only
2976 peripherally related to the GPGME Python bindings.  Some are also
2977 workarounds for functions not integrated with GPGME as yet.  This is
2978 especially true of the first of these, dealing with [[#group-lines][group lines]].
2979
2980
2981 ** Group lines
2982    :PROPERTIES:
2983    :CUSTOM_ID: group-lines
2984    :END:
2985
2986 There is not yet an easy way to access groups configured in the
2987 gpg.conf file from within GPGME.  As a consequence these central
2988 groupings of keys cannot be shared amongst multiple programs, such as
2989 MUAs readily.
2990
2991 The following code, however, provides a work-around for obtaining this
2992 information in Python.
2993
2994 #+BEGIN_SRC python -i
2995 import subprocess
2996 import sys
2997
2998 if sys.platform == "win32":
2999     gpgconfcmd = "gpgconf.exe --list-options gpg"
3000 else:
3001     gpgconfcmd = "gpgconf --list-options gpg"
3002
3003 try:
3004     lines = subprocess.getoutput(gpgconfcmd).splitlines()
3005 except:
3006     process = subprocess.Popen(gpgconfcmd.split(), stdout=subprocess.PIPE)
3007     procom = process.communicate()
3008     if sys.version_info[0] == 2:
3009         lines = procom[0].splitlines()
3010     else:
3011         lines = procom[0].decode().splitlines()
3012
3013 for i in range(len(lines)):
3014     if lines[i].startswith("group") is True:
3015         line = lines[i]
3016     else:
3017         pass
3018
3019 groups = line.split(":")[-1].replace('"', '').split(',')
3020
3021 group_lines = []
3022 group_lists = []
3023
3024 for i in range(len(groups)):
3025     group_lines.append(groups[i].split("="))
3026     group_lists.append(groups[i].split("="))
3027
3028 for i in range(len(group_lists)):
3029     group_lists[i][1] = group_lists[i][1].split()
3030 #+END_SRC
3031
3032 The result of that code is that =group_lines= is a list of lists where
3033 =group_lines[i][0]= is the name of the group and =group_lines[i][1]=
3034 is the key IDs of the group as a string.
3035
3036 The =group_lists= result is very similar in that it is a list of
3037 lists.  The first part, =group_lists[i][0]= matches
3038 =group_lines[i][0]= as the name of the group, but =group_lists[i][1]=
3039 is the key IDs of the group as a list.
3040
3041 A demonstration of using the =groups.py= module is also available in
3042 the form of the executable =mutt-groups.py= script.  This second
3043 script reads all the group entries in a user's =gpg.conf= file and
3044 converts them into crypt-hooks suitable for use with the Mutt and
3045 Neomutt mail clients.
3046
3047
3048 ** Keyserver access for Python
3049    :PROPERTIES:
3050    :CUSTOM_ID: hkp4py
3051    :END:
3052
3053 The [[https://github.com/Selfnet/hkp4py][hkp4py]] module by Marcel Fest was originally a port of the old
3054 [[https://github.com/dgladkov/python-hkp][python-hkp]] module from Python 2 to Python 3 and updated to use the
3055 [[http://docs.python-requests.org/en/latest/index.html][requests]] module instead.  It has since been modified to provide
3056 support for Python 2.7 as well and is available via PyPI.
3057
3058 Since it rewrites the =hkp= protocol prefix as =http= and =hkps= as
3059 =https=, the module is able to be used even with servers which do not
3060 support the full scope of keyserver functions.[fn:5]  It also works quite
3061 readily when incorporated into a [[#cython][Cython]] generated and compiled version
3062 of any code.
3063
3064
3065 *** Key import format
3066     :PROPERTIES:
3067     :CUSTOM_ID: hkp4py-strings
3068     :END:
3069
3070 The hkp4py module returns key data via requests as string literals
3071 (=r.text=) instead of byte literals (=r.content=).  This means that
3072 the retrurned key data must be encoded to UTF-8 when importing that
3073 key material using a =gpg.Context().key_import()= method.
3074
3075 For this reason an alternative method has been added to the =search=
3076 function of =hkp4py.KeyServer()= which returns the key in the correct
3077 format as expected by =key_import=.  When importing using this module,
3078 it is now possible to import with this:
3079
3080 #+BEGIN_SRC python -i
3081 for key in keys:
3082     if key.revoked is False:
3083         gpg.Context().key_import(key.key_blob)
3084     else:
3085         pass
3086 #+END_SRC
3087
3088 Without that recent addition it would have been necessary to encode
3089 the contents of each =hkp4py.KeyServer().search()[i].key= in
3090 =hkp4py.KeyServer().search()= before trying to import it.
3091
3092 An example of this is included in the [[#howto-import-key][Importing Keys]] section of this
3093 HOWTO and the corresponding executable version of that example is
3094 available in the =lang/python/examples/howto= directory as normal; the
3095 executable version is the =import-keys-hkp.py= file.
3096
3097
3098 ** GPGME version checking
3099    :PROPERTIES:
3100    :CUSTOM_ID: gpgme-version-check
3101    :END:
3102
3103 For various reasons it may be necessary to check which version of
3104 GPGME the bindings have been built against; including whether a
3105 minimum required version of GPGME is in use.
3106
3107 For the most part the =gpg.version.versionstr= and
3108 =gpg.version.versionlist= methods have been quite sufficient.  The
3109 former returns the same string as =gpgme-config --version=, while the
3110 latter returns the major, minor and patch values in a list.
3111
3112 To check if the installed bindings have actually been built against
3113 the current installed libgpgme version, this check can be performed:
3114
3115 #+BEGIN_SRC python -i
3116 import gpg
3117 import subprocess
3118 import sys
3119
3120 gpgme_version_call = subprocess.Popen(["gpgme-config", "--version"],
3121                                       stdout=subprocess.PIPE,
3122                                       stderr=subprocess.PIPE)
3123 gpgme_version_str = gpgme_version_call.communicate()
3124
3125 if sys.version_info[0] == 2:
3126     gpgme_version = gpgme_version_str[0].strip()
3127 elif sys.version_info[0] >= 3:
3128     gpgme_version = gpgme_version_str[0].decode().strip()
3129 else:
3130     gpgme_version = None
3131
3132 if gpgme_version is not None:
3133     if gpgme_version == gpg.version.versionstr:
3134         print("The GPGME Python bindings match libgpgme.")
3135     else:
3136         print("The GPGME Python bindings do NOT match libgpgme.")
3137 else:
3138     print("Upgrade Python and reinstall the GPGME Python bindings.")
3139 #+END_SRC
3140
3141 For many developers, however, the preferred checking means checking
3142 for a minimum version or point release.  This is now readily available
3143 via the =gpg.version.versionintlist= method (added in version
3144 =1.12.1-beta79=).  It is also now possible to easily check whether the
3145 installed GPGME Python bindings were built from a development or beta
3146 branch of the GPGME source code.
3147
3148 The following code demonstrates how both of those methods may be used:
3149
3150 #+BEGIN_SRC python -i
3151 import gpg
3152
3153 try:
3154     if gpg.version.is_beta is True:
3155         print("The installed GPGME Python bindings were built from beta code.")
3156     else:
3157         print("The installed GPGME Python bindings are a released version.")
3158 except Exception as e:
3159     print(e)
3160
3161 try:
3162     if gpg.version.versionintlist[0] == 1:
3163         if gpg.version.versionintlist[1] == 12:
3164             if gpg.version.versionintlist[2] == 1:
3165                 print("This is the minimum version for using versionintlist.")
3166             elif gpg.version.versionintlist[2] > 1:
3167                 print("The versionintlist method is available.")
3168             else:
3169                 pass
3170         elif gpg.version.versionintlist[1] > 12:
3171             print("The versionintlist method is available.")
3172         else:
3173             pass
3174     elif gpg.version.versionintlist[0] > 1:
3175         print("The versionintlist method is available.")
3176     else:
3177         pass
3178 except Exception as e:
3179     print(e)
3180 #+END_SRC
3181
3182 The points where =pass= is used in the above example will most likely
3183 also produce an =Exception= error since those results should only
3184 occur in versions which do not have the =gpgme.version.is_beta= and
3185 =gpgme.version.versionintlist= methods available.
3186
3187
3188 * Copyright and Licensing
3189   :PROPERTIES:
3190   :CUSTOM_ID: copyright-and-license
3191   :END:
3192
3193
3194 ** Copyright
3195    :PROPERTIES:
3196    :CUSTOM_ID: copyright
3197    :END:
3198
3199 Copyright © The GnuPG Project, 2018.
3200
3201 Copyright (C) The GnuPG Project, 2018.
3202
3203
3204 ** Draft Editions of this HOWTO
3205    :PROPERTIES:
3206    :CUSTOM_ID: draft-editions
3207    :END:
3208
3209 Draft editions of this HOWTO may be periodically available directly
3210 from the author at any of the following URLs:
3211
3212 - [[https://files.au.adversary.org/crypto/gpgme-python-howto.html][GPGME Python Bindings HOWTO draft (XHTML single file, AWS S3 SSL)]]
3213 - [[http://files.au.adversary.org/crypto/gpgme-python-howto.html][GPGME Python Bindings HOWTO draft (XHTML single file, AWS S3 no SSL)]]
3214 - [[https://files.au.adversary.org/crypto/gpgme-python-howto-split/index.html][GPGME Python Bindings HOWTO draft (XHTML multiple files, AWS S3 SSL)]]
3215 - [[http://files.au.adversary.org/crypto/gpgme-python-howto/index.html][GPGME Python Bindings HOWTO draft (XHTML multiple files, AWS S3 no SSL)]]
3216
3217 All of these draft versions except for one have been generated from
3218 this document via GNU Emacs [[https://orgmode.org/][Org mode]] and [[https://www.gnu.org/software/texinfo/][GNU Texinfo]].  Though it is
3219 likely that the specific [[https://files.au.adversary.org/crypto/gpgme-python-howto][file]] [[http://files.au.adversary.org/crypto/gpgme-python-howto.org][version]] used will be on the same server
3220 with the generated output formats.
3221
3222 The GNU Texinfo and reStructured Text versions ship with the software,
3223 while the GNU Emacs Info verseion is generated from the Texinfo
3224 version using GNU Texinfo or GNU Makeinfo.  The Texinfo format is
3225 generated from the original Org mode source file in Org mode itself
3226 either within GNU Emacs or via the command line by invoking Emacs in
3227 batch mode:
3228
3229 #+BEGIN_SRC shell
3230   emacs gpgme-python-howto.org --batch -f org-texinfo-export-to-texinfo --kill
3231   emacs gpgme-python-howto --batch -f org-texinfo-export-to-texinfo --kill
3232 #+END_SRC
3233
3234 The reStructuredText format is also generated from the Org-mode source
3235 file, except it is generated using [[https://pandoc.org][Pandoc]] with either of the following
3236 commands:
3237
3238 #+BEGIN_SRC shell
3239   pandoc -f org -t rst+smart -o gpgme-python-howto.rst gpgme-python-howto.org
3240   pandoc -f org -t rst+smart -o gpgme-python-howto.rst gpgme-python-howto
3241 #+END_SRC
3242
3243 In addition to these there is a significantly less frequently updated
3244 version as a HTML [[https://files.au.adversary.org/crypto/gpgme-python/dita/webhelp/index.html][WebHelp site]] (AWS S3 SSL); generated from DITA XML
3245 source files, which can be found in [[https://dev.gnupg.org/source/gpgme/browse/ben%252Fhowto-dita/][an alternative branch]] of the GPGME
3246 git repository.
3247
3248 Various generated output formats may occasionally be found in
3249 subdirectories of the [[https://s3.amazonaws.com/files.au.adversary.org/crypto/gpgme-python][gpgme-python]] directory.  In particular within
3250 the [[https://s3.amazonaws.com/files.au.adversary.org/crypto/gpgme-python/dita][DITA]], [[https://s3.amazonaws.com/files.au.adversary.org/crypto/gpgme-python/rst][reStructuredText]] and [[https://s3.amazonaws.com/files.au.adversary.org/crypto/gpgme-python/texinfo][Texinfo]] subdirectories.  The =rst=
3251 directory contains output files generated with Sphix and may include a
3252 considerable number of its possible output formats.
3253
3254 These draft editions are not official documents and the version of
3255 documentation in the master branch or which ships with released
3256 versions is the only official documentation.  Nevertheless, these
3257 draft editions may occasionally be of use by providing more accessible
3258 web versions which are updated between releases.  They are provided on
3259 the understanding that they may contain errors or may contain content
3260 subject to change prior to an official release.
3261
3262
3263 ** License GPL compatible
3264    :PROPERTIES:
3265    :CUSTOM_ID: license
3266    :END:
3267
3268 This file is free software; as a special exception the author gives
3269 unlimited permission to copy and/or distribute it, with or without
3270 modifications, as long as this notice is preserved.
3271
3272 This file is distributed in the hope that it will be useful, but
3273 WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
3274 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
3275 PURPOSE.
3276
3277
3278 * Footnotes
3279
3280 [fn:1] =short-history= and/or =short-history.html=.
3281
3282 [fn:2] With no issues reported specific to Python 3.7, the release of
3283 Python 3.7.1 at around the same time as GPGME 1.12.0 and the testing
3284 with Python 3.7.1rc1, there is no reason to delay moving 3.7 ahead of
3285 3.6 now.  Production environments with more conservative requirements
3286 will always enforce their own policies anyway and installation to each
3287 supported minor release is quite possible too.
3288
3289 [fn:3] Yes, even if you use virtualenv with everything you do in
3290 Python.  If you want to install this module as just your user account
3291 then you will need to manually configure, compile and install the
3292 /entire/ GnuPG stack as that user as well.  This includes libraries
3293 which are not often installed that way.  It can be done and there are
3294 circumstances under which it is worthwhile, but generally only on
3295 POSIX systems which utilise single user mode (some even require it).
3296
3297 [fn:4] You probably don't really want to do this.  Searching the
3298 keyservers for "gnupg.org" produces over 400 results, the majority of
3299 which aren't actually at the gnupg.org domain, but just included a
3300 comment regarding the project in their key somewhere.
3301
3302 [fn:5] Such as with ProtonMail servers.  This also means that
3303 restricted servers which only advertise either HTTP or HTTPS end
3304 points and not HKP or HKPS end points must still be identified as as
3305 HKP or HKPS within the Python Code.  The =hkp4py= module will rewrite
3306 these appropriately when the connection is made to the server.