[tor-commits] [bridgedb/master] Add crypto.initializeGnuPG() function.

isis at torproject.org isis at torproject.org
Sat Mar 21 02:03:02 UTC 2015


commit a4fc36eabb00bb47114b8379319d3897a63427ac
Author: Isis Lovecruft <isis at torproject.org>
Date:   Sun Feb 22 10:58:04 2015 +0000

    Add crypto.initializeGnuPG() function.
    
    This switches BridgeDB to using python-gnupg rather than pygpgme.
    
     * FIXES #10385 https://bugs.torproject.org/10385
---
 lib/bridgedb/crypto.py |   79 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 79 insertions(+)

diff --git a/lib/bridgedb/crypto.py b/lib/bridgedb/crypto.py
index a16e45a..6bb5c7d 100644
--- a/lib/bridgedb/crypto.py
+++ b/lib/bridgedb/crypto.py
@@ -41,6 +41,7 @@ Module Overview
 from __future__ import absolute_import
 from __future__ import unicode_literals
 
+import gnupg
 import hashlib
 import hmac
 import io
@@ -273,14 +274,92 @@ def removePKCS1Padding(message):
 
     return unpadded
 
+def initializeGnuPG(config):
+    """Initialize a GnuPG interface and test our configured keys.
+
+    .. note:: This function uses python-gnupg_.
+
+    :type config: :class:`bridgedb.persistent.Conf`
+    :param config: The loaded config file.
+    :rtype: 2-tuple
+    :returns: If ``EMAIL_GPG_SIGNING_ENABLED`` isn't ``True``, or we couldn't
+        initialize GnuPG and make a successful test signature with the
+        specified key, then a 2-tuple of ``None`` is returned.  Otherwise, the
+        first item in the tuple is a :class:`gnupg.GPG` interface_ with the
+        GnuPG homedir set to the ``EMAIL_GPG_HOMEDIR`` option and the signing
+        key specified by the ``EMAIL_GPG_SIGNING_KEY_FINGERPRINT`` option in
+        bridgedb.conf set as the default key. The second item in the tuple is
+        a signing function with the passphrase (as specified in either
+        ``EMAIL_GPG_PASSPHRASE`` or ``EMAIL_GPG_PASSPHRASE_FILE``) already
+        set.
+
+    .. _python-gnupg: https://pypi.python.org/pypi/gnupg/
+    .. _interface: https://python-gnupg.readthedocs.org/en/latest/gnupg.html#gnupg-module
+    """
+    ret = (None, None)
 
+    if not config.EMAIL_GPG_SIGNING_ENABLED:
+        return ret
 
+    homedir = config.EMAIL_GPG_HOMEDIR
+    primary = config.EMAIL_GPG_PRIMARY_KEY_FINGERPRINT
+    passphrase = config.EMAIL_GPG_PASSPHRASE
+    passFile = config.EMAIL_GPG_PASSPHRASE_FILE
 
+    logging.info("Using %s as our GnuPG home directory..." % homedir)
+    gpg = gnupg.GPG(homedir=homedir)
+    logging.info("Initialized GnuPG interface using %s binary with version %s."
+                 % (gpg.binary, gpg.binary_version))
 
+    primarySK = None
+    primaryPK = None
+    secrets = gpg.list_keys(secret=True)
+    publics = gpg.list_keys()
 
+    if not secrets:
+        logging.warn("No secret keys found in %s!" % gpg.secring)
+        return ret
 
+    primarySK = filter(lambda key: key['fingerprint'] == primary, secrets)
+    primaryPK = filter(lambda key: key['fingerprint'] == primary, publics)
 
+    if primarySK and primaryPK:
+        logging.info("Found GnuPG primary key with fingerprint: %s" % primary)
+        for sub in primaryPK[0]['subkeys']:
+            logging.info("  Subkey: %s  Usage: %s" % (sub[0], sub[1].upper()))
+    else:
+        logging.warn("GnuPG key %s could not be found in %s!" % (primary, gpg.secring))
+        return ret
+
+    if passphrase:
+        logging.info("Read GnuPG passphrase from config.")
+    elif passFile:
+        try:
+            with open(passFile) as fh:
+                passphrase = fh.read()
+        except (IOError, OSError):
+            logging.error("Could not open GnuPG passphrase file: %s!" % passFile)
+        else:
+            logging.info("Read GnuPG passphrase from file: %s" % passFile)
 
+    def gpgSignMessage(message):
+        """Sign **message** with the default key specified by
+        ``EMAIL_GPG_PRIMARY_KEY_FINGERPRINT``.
+
+        :param str message: A message to sign.
+        :rtype: str or ``None``.
+        :returns: A string containing the clearsigned message, or ``None`` if
+            the signing failed.
+        """
+        sig = gpg.sign(message, default_key=primary, passphrase=passphrase)
+        if sig and sig.data:
+            return sig.data
+
+    logging.debug("Testing signature created with GnuPG key...")
+    sig = gpgSignMessage("Testing 1 2 3")
+    if sig:
+        logging.info("Test signature with GnuPG key %s okay:\n%s" % (primary, sig))
+        return (gpg, gpgSignMessage)
 
 
 class SSLVerifyingContextFactory(ssl.CertificateOptions):





More information about the tor-commits mailing list