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