doc: python bindings howto

* Replaced the single encryption methods with one main way (i.e. cut
  the low level stuff involving SEEK_SET instructions).
This commit is contained in:
Ben McGinnes 2018-03-19 10:00:44 +11:00
parent 4811ff7b6c
commit 64c5886132

View File

@ -436,6 +436,7 @@
section we will look at how to programmatically encrypt data, section we will look at how to programmatically encrypt data,
decrypt it, sign it and verify signatures. decrypt it, sign it and verify signatures.
** Encryption ** Encryption
:PROPERTIES: :PROPERTIES:
:CUSTOM_ID: howto-basic-encryption :CUSTOM_ID: howto-basic-encryption
@ -446,85 +447,35 @@
the second example the message will be encrypted to multiple the second example the message will be encrypted to multiple
recipients. recipients.
*** Encrypting to one key *** Encrypting to one key
:PROPERTIES: :PROPERTIES:
:CUSTOM_ID: howto-basic-encryption-single :CUSTOM_ID: howto-basic-encryption-single
:END: :END:
The text is then encapsulated in a GPGME Data object as =plain= and Once the the Context is set the main issues with encrypting data
the =cipher= object is created with another Data object. Then we is essentially reduced to key selection and the keyword arguments
create the Context as =c= and set it to use the ASCII armoured specified in the =gpg.Context().encrypt()= method.
OpenPGP format. In later examples there will be alternative
methods of setting the OpenPGP output to be ASCII armoured.
Next we prepare a keylist object in our Context and follow it with Those keyword arguments are: =recipients=, a list of keys
specifying the recipients as =r=. Note that the configuration in encrypted to (covered in greater detail in the following section);
one's =gpg.conf= file is honoured, so if you have the options set =sign=, whether or not to sign the plaintext data, see subsequent
to encrypt to one key or to a default key, that will be included sections on signing and verifying signatures below (defaults to
with this operation. =True=); =sink=, to write results or partial results to a secure
sink instead of returning it (defaults to =None=); =passphrase=,
This is followed by a quick check to be sure that the recipient is only used when utilising symmetric encryption (defaults to
actually selected and that the key is available. Assuming it is, =None=); =always_trust=, used to override the trust model settings
the encryption can proceed, but if not a message will print stating for recipient keys (defaults to =False=); =add_encrypt_to=,
the key was not found. utilises any preconfigured =encrypt-to= or =default-key= settings
in the user's =gpg.conf= file (defaults to =False=); =prepare=,
The encryption operation is invoked within the Context with the prepare for encryption (defaults to =False=); =expect_sign=,
=c.op_encrypt= function, loading the recipients (=r=), the message prepare for signing (defaults to =False=); =compress=, compresses
(=plain=) and the =cipher=. The =cipher.seek= uses =os.SEEK_SET= the plaintext prior to encryption (defaults to =True=).
to set the data to the correct byte format for GPGME to use it.
At this point we no longer need the plaintext material, so we
delete both the =text= and the =plain= objects. Then we write the
encrypted data out to a file, =secret_plans.txt.asc=.
#+begin_src python
import gpg
import os
rkey = "0x12345678DEADBEEF"
text = """
Some plain text to test with. Obtained from any input source Python can read.
It makes no difference whether it is string or bytes, but the bindings always
produce byte output data. Which is useful to know when writing out either the
encrypted or decrypted results.
"""
plain = gpg.core.Data(text)
cipher = gpg.core.Data()
c = gpg.core.Context()
c.set_armor(1)
c.op_keylist_start(rkey, 0)
r = c.op_keylist_next()
if r == None:
print("""The key for user "{0}" was not found""".format(rkey))
else:
try:
c.op_encrypt([r], 1, plain, cipher)
cipher.seek(0, os.SEEK_SET)
with open("secret_plans.txt.asc", "wb") as afile:
afile.write(cipher.read())
except gpg.errors.GPGMEError as ex:
print(ex.getstring())
#+end_src
*** Encrypting to one key using the second method
:PROPERTIES:
:CUSTOM_ID: howto-basic-encryption-monogamous
:END:
This example re-creates the first encryption example except it
uses the same =encrypt= method used in the subsequent examples
instead of the =op_encrypt= method. This means that, unlike the
=op_encrypt= method, it /must/ use byte literal input data.
#+begin_src python #+begin_src python
import gpg import gpg
rkey = "0x12345678DEADBEEF" a_key = "0x12345678DEADBEEF"
text = b"""Some text to test with. text = b"""Some text to test with.
Since the text in this case must be bytes, it is most likely that Since the text in this case must be bytes, it is most likely that
@ -534,56 +485,51 @@
""" """
c = gpg.Context(armor=True) c = gpg.Context(armor=True)
rpattern = list(c.keylist(pattern=rkey, secret=False)) rkey = list(c.keylist(pattern=a_key, secret=False))
logrus = [] ciphertext, result, sign_result = c.encrypt(text, recipients=rkey, sign=False)
for i in range(len(rpattern)):
if rpattern[i].can_encrypt == 1:
logrus.append(rpattern[i])
cipher = c.encrypt(text, recipients=logrus, sign=False, always_trust=True)
with open("secret_plans.txt.asc", "wb") as afile: with open("secret_plans.txt.asc", "wb") as afile:
afile.write(cipher[0]) afile.write(ciphertext)
#+end_src #+end_src
With one or two exceptions, this method will probably prove to be Though this is even more likely to be used like this; with the
easier to implement than the first method and thus it is the plaintext input read from a file, the recipient keys used for
recommended encryption method. Though it is even more likely to encryption regardless of key trust status and the encrypted output
be used like this: also encrypted to any preconfigured keys set in the =gpg.conf=
file:
#+begin_src python #+begin_src python
import gpg import gpg
rkey = "0x12345678DEADBEEF" a_key = "0x12345678DEADBEEF"
afile = open("secret_plans.txt", "rb") with open("secret_plans.txt", "rb") as afile:
text = afile.read() text = afile.read()
afile.close()
c = gpg.Context(armor=True) c = gpg.Context(armor=True)
rpattern = list(c.keylist(pattern=rkey, secret=False)) rkey = list(c.keylist(pattern=a_key, secret=False))
logrus = [] ciphertext, result, sign_result = c.encrypt(text, recipients=rkey,
sign=True, always_trust=True,
for i in range(len(rpattern)): add_encrypt_to=True)
if rpattern[i].can_encrypt == 1:
logrus.append(rpattern[i])
cipher = c.encrypt(text, recipients=logrus, sign=False, always_trust=True)
with open("secret_plans.txt.asc", "wb") as afile: with open("secret_plans.txt.asc", "wb") as afile:
afile.write(cipher[0]) afile.write(ciphertext)
#+end_src #+end_src
If the =recipients= paramater is empty then the plaintext is
encrypted symmetrically. If no =passphrase= is supplied as a
parameter or via a callback registered with the =Context()= then
an out-of-band prompt for the passphrase via pinentry will be
invoked.
*** Encrypting to multiple keys *** Encrypting to multiple keys
:PROPERTIES: :PROPERTIES:
:CUSTOM_ID: howto-basic-encryption-multiple :CUSTOM_ID: howto-basic-encryption-multiple
:END: :END:
Encrypting to multiple keys, in addition to a default key or a key Encrypting to multiple keys essentially just expands upon the key
configured to always encrypt to, is a little different and uses a selection process and the recipients from the previous examples.
slightly different call to the =op_encrypt= call demonstrated in the
previous section.
The following example encrypts a message (=text=) to everyone with The following example encrypts a message (=text=) to everyone with
an email address on the =gnupg.org= domain,[fn:3] but does /not/ encrypt an email address on the =gnupg.org= domain,[fn:3] but does /not/ encrypt
@ -711,7 +657,8 @@
:CUSTOM_ID: howto-basic-signing :CUSTOM_ID: howto-basic-signing
:END: :END:
The following sections demonstrate how to specify The following sections demonstrate how to specify keys to sign with.
*** Signing key selection *** Signing key selection
:PROPERTIES: :PROPERTIES:
@ -743,6 +690,7 @@
bytes or None and not a list. A string with two fingerprints bytes or None and not a list. A string with two fingerprints
won't match any single key. won't match any single key.
*** Normal or default signing messages or files *** Normal or default signing messages or files
:PROPERTIES: :PROPERTIES:
:CUSTOM_ID: howto-basic-signing-normal :CUSTOM_ID: howto-basic-signing-normal
@ -798,6 +746,7 @@
afile.write(signed[0]) afile.write(signed[0])
#+end_src #+end_src
*** Detached signing messages and files *** Detached signing messages and files
:PROPERTIES: :PROPERTIES:
:CUSTOM_ID: howto-basic-signing-detached :CUSTOM_ID: howto-basic-signing-detached
@ -840,6 +789,7 @@
afile.write(signed[0]) afile.write(signed[0])
#+end_src #+end_src
*** Clearsigning messages or text *** Clearsigning messages or text
:PROPERTIES: :PROPERTIES:
:CUSTOM_ID: howto-basic-signing-clear :CUSTOM_ID: howto-basic-signing-clear
@ -1307,6 +1257,7 @@
:CUSTOM_ID: cheats-and-hacks :CUSTOM_ID: cheats-and-hacks
:END: :END:
** Group lines ** Group lines
:PROPERTIES: :PROPERTIES:
:CUSTOM_ID: group-lines :CUSTOM_ID: group-lines
@ -1357,6 +1308,7 @@
:CUSTOM_ID: copyright-and-license :CUSTOM_ID: copyright-and-license
:END: :END:
** Copyright (C) The GnuPG Project, 2018 ** Copyright (C) The GnuPG Project, 2018
:PROPERTIES: :PROPERTIES:
:CUSTOM_ID: copyright :CUSTOM_ID: copyright
@ -1364,6 +1316,7 @@
Copyright © The GnuPG Project, 2018. Copyright © The GnuPG Project, 2018.
** License GPL compatible ** License GPL compatible
:PROPERTIES: :PROPERTIES:
:CUSTOM_ID: license :CUSTOM_ID: license