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