doc: python bindings howto
authorBen McGinnes <ben@adversary.org>
Sun, 18 Mar 2018 23:39:53 +0000 (10:39 +1100)
committerBen McGinnes <ben@adversary.org>
Sun, 18 Mar 2018 23:39:53 +0000 (10:39 +1100)
* deconstructing multi-recipient encryption.

lang/python/docs/GPGMEpythonHOWTOen.org

index a960830..f5192f4 100644 (file)
@@ -22,6 +22,7 @@
   This document provides basic instruction in how to use the GPGME
   Python bindings to programmatically leverage the GPGME library.
 
+
 ** Python 2 versus Python 3
    :PROPERTIES:
    :CUSTOM_ID: py2-vs-py3
@@ -53,6 +54,7 @@
   :CUSTOM_ID: gpgme-concepts
   :END:
 
+
 ** A C API
    :PROPERTIES:
    :CUSTOM_ID: gpgme-c-api
@@ -72,6 +74,7 @@
    languages.  This is where the need for bindings in various
    languages stems.
 
+
 ** Python bindings
    :PROPERTIES:
    :CUSTOM_ID: gpgme-python-bindings
@@ -88,6 +91,7 @@
    tied to the exact same version of GPGME used to generate that copy
    of =gpgme.h=.
 
+
 ** Difference between the Python bindings and other GnuPG Python packages
    :PROPERTIES:
    :CUSTOM_ID: gpgme-python-bindings-diffs
    over the years.  Some of the most well known are listed here, along
    with what differentiates them.
 
+
 *** The python-gnupg package maintained by Vinay Sajip
     :PROPERTIES:
     :CUSTOM_ID: diffs-python-gnupg
 
     The python-gnupg package is available under the MIT license.
 
+
 *** The gnupg package created and maintained by Isis Lovecruft
     :PROPERTIES:
     :CUSTOM_ID: diffs-isis-gnupg
     The gnupg package is available under the GNU General Public
     License version 3.0 (or later).
 
+
 *** The PyME package maintained by Martin Albrecht
     :PROPERTIES:
     :CUSTOM_ID: diffs-pyme
   :CUSTOM_ID: gpgme-python-install
   :END:
 
+
 ** No PyPI
    :PROPERTIES:
    :CUSTOM_ID: do-not-use-pypi
    bundled with it or a full implementation of C for each
    architecture.
 
+
 ** Requirements
    :PROPERTIES:
    :CUSTOM_ID: gpgme-python-requirements
    3. GPGME itself.  Which also means that all of GPGME's dependencies
       must be installed too.
 
+
 ** Installation
    :PROPERTIES:
    :CUSTOM_ID: installation
    For Python 3 it checks for these executables in this order:
    =python3=, =python3.6=, =python3.5= and =python3.4=.
 
+
 *** Installing GPGME
     :PROPERTIES:
     :CUSTOM_ID: install-gpgme
   regarding GPGME's design which hold true whether you're dealing with
   the C code directly or these Python bindings.
 
+
 ** No REST
    :PROPERTIES:
    :CUSTOM_ID: no-rest-for-the-wicked
    direct bindings and it's this pythonic layer with which this HOWTO
    deals with.
 
+
 ** Context
    :PROPERTIES:
    :CUSTOM_ID: howto-get-context
   :CUSTOM_ID: howto-keys
   :END:
 
+
 ** Key selection
    :PROPERTIES:
    :CUSTOM_ID: howto-keys-selection
          if rpattern[i].can_encrypt == 1:
              logrus.append(rpattern[i])
 
-      cipher = c.encrypt(text, recipients=logrus, sign=False, always_trust=True)
+      ciphertext, result, sign_result = c.encrypt(text, recipients=logrus, sign=False,
+                                                  always_trust=True)
 
       with open("secret_plans.txt.asc", "wb") as afile:
-          afile.write(cipher[0])
+          afile.write(ciphertext)
     #+end_src
 
     All it would take to change the above example to sign the message
     be to change the =c.encrypt= line to this:
 
     #+begin_src python
-      cipher = c.encrypt(text, recipients=logrus, always_trust=True,
-                        add_encrypt_to=True)
+      ciphertext, result, sign_result = c.encrypt(text, recipients=logrus,
+                                                 always_trust=True,
+                                                 add_encrypt_to=True)
     #+end_src
 
     The only keyword arguments requiring modification are those for
              logrus.append(rpattern[i])
 
       try:
-         cipher = c.encrypt(text, recipients=logrus, add_encrypt_to=True)
+         ciphertext, result, sign_result = c.encrypt(text, recipients=logrus, add_encrypt_to=True)
       except gpg.errors.InvalidRecipients as e:
          for i in range(len(e.recipients)):
              for n in range(len(logrus)):
                   else:
                       pass
          try:
-             cipher = c.encrypt(text, recipients=logrus, add_encrypt_to=True)
+             ciphertext, result, sign_result = c.encrypt(text, recipients=logrus, add_encrypt_to=True)
          except:
              pass
 
       with open("secret_plans.txt.asc", "wb") as afile:
-          afile.write(cipher[0])
+          afile.write(ciphertext)
     #+end_src
 
     This will attempt to encrypt to all the keys searched for, then
      text = text0.encode()
 
      c = gpg.Context(armor=True, signers=sig_src)
-     signed = c.sign(text, mode=0)
+     signed = c.sign(text, mode=gpg.constants.sig.mode.NORMAL)
 
      with open("/path/to/statement.txt.asc", "w") as afile:
          for line in signed[0]:
          text = tfile.read()
 
      c = gpg.Context()
-     signed = c.sign(text, mode=0)
+     signed = c.sign(text, mode=gpg.constants.sig.mode.NORMAL)
 
      with open("/path/to/statement.txt.sig", "wb") as afile:
          afile.write(signed[0])
       text = text0.encode()
 
       c = gpg.Context(armor=True)
-      signed = c.sign(text, mode=1)
+      signed = c.sign(text, mode=gpg.constants.sig.mode.DETACH)
 
       with open("/path/to/statement.txt.asc", "w") as afile:
           for line in signed[0].splitlines():
           text = tfile.read()
 
       c = gpg.Context(signers=sig_src)
-      signed = c.sign(text, mode=1)
+      signed = c.sign(text, mode=gpg.constants.sig.mode.DETACH)
 
       with open("/path/to/statement.txt.sig", "wb") as afile:
           afile.write(signed[0])
       text = text0.encode()
 
       c = gpg.Context()
-      signed = c.sign(text, mode=2)
+      signed = c.sign(text, mode=gpg.constants.sig.mode.CLEAR)
 
       with open("/path/to/statement.txt.asc", "w") as afile:
           for line in signed[0].splitlines():
           text = tfile.read()
 
       c = gpg.Context()
-      signed = c.sign(text, mode=2)
+      signed = c.sign(text, mode=gpg.constants.sig.mode.CLEAR)
 
       with open("/path/to/statement.txt.asc", "wb") as afile:
           afile.write(signed[0])
      c = gpg.Context()
 
      try:
-        verified = c.verify(open(gpg_file))
+        data, result = c.verify(open(gpg_file))
+        verified = True
      except gpg.errors.BadSignatures as e:
-        verified = None
+        verified = False
         print(e)
 
-     if verified is not None:
-        for i in range(len(verified[1].signatures)):
-            sign = verified[1].signatures[i]
+     if verified is True:
+        for i in range(len(result.signatures)):
+            sign = result.signatures[i]
             print("""Good signature from:
      {0}
      with key {1}
      c = gpg.Context()
 
      try:
-        verified = c.verify(open(asc_file))
+        data, result = c.verify(open(asc_file))
+        verified = True
      except gpg.errors.BadSignatures as e:
-        verified = None
+        verified = False
         print(e)
 
-     if verified is not None:
-        for i in range(len(verified[1].signatures)):
-            sign = verified[1].signatures[i]
+     if verified is True:
+        for i in range(len(result.signatures)):
+            sign = result.signatures[i]
             print("""Good signature from:
      {0}
      with key {1}
    #+end_src
 
    In both of the previous examples it is also possible to compare the
-   original data that was signed against the signed data in
-   =verified[0]= to see if it matches with something like this:
+   original data that was signed against the signed data in =data= to
+   see if it matches with something like this:
 
    #+begin_src python
-     afile = open(filename, "rb")
-     text = afile.read()
-     afile.close()
+     with open(filename, "rb") as afile:
+         text = afile.read()
 
-     if text == verified[0]:
+     if text == data:
         print("Good signature.")
      else:
         pass
    The following two examples, however, deal with detached signatures.
    With his method of verification the data that was signed does not
    get returned since it is already being explicitly referenced in the
-   first argument of =c.verify=.  So =verified[0]= is None and only
-   the data in =verified[1]= is available.
+   first argument of =c.verify=.  So =data= is =None= and only the
+   information in =result= is available.
 
    #+begin_src python
      import gpg
      c = gpg.Context()
 
      try:
-        verified = c.verify(open(filename), open(sig_file))
+        data, result = c.verify(open(filename), open(sig_file))
+        verified = True
      except gpg.errors.BadSignatures as e:
-        verified = None
+        verified = False
         print(e)
 
-     if verified is not None:
-        for i in range(len(verified[1].signatures)):
-            sign = verified[1].signatures[i]
+     if verified is True:
+        for i in range(len(result.signatures)):
+            sign = result.signatures[i]
             print("""Good signature from:
      {0}
      with key {1}
      c = gpg.Context()
 
      try:
-        verified = c.verify(open(filename), open(asc_file))
+        data, result = c.verify(open(filename), open(asc_file))
+        verified = True
      except gpg.errors.BadSignatures as e:
-        verified = None
+        verified = False
         print(e)
 
      if verified is not None:
-        for i in range(len(verified[1].signatures)):
-            sign = verified[1].signatures[i]
+        for i in range(len(result.signatures)):
+            sign = result.signatures[i]
             print("""Good signature from:
      {0}
      with key {1}
     allow-secret-key-import
     trust-model tofu+pgp
     tofu-default-policy unknown
-    # no-auto-check-trustdb
     enable-large-rsa
     enable-dsa2
-    # no-emit-version
-    # no-comments
     # cert-digest-algo SHA256
     cert-digest-algo SHA512
     default-preference-list TWOFISH CAMELLIA256 AES256 CAMELLIA192 AES192 CAMELLIA128 AES BLOWFISH IDEA CAST5 3DES SHA512 SHA384 SHA256 SHA224 RIPEMD160 SHA1 ZLIB BZIP2 ZIP Uncompressed