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

isis at torproject.org isis at torproject.org
Sun Mar 16 19:04:58 UTC 2014


commit 67e14a5d367f8c82c806f7c64f9e1c28293f7c5a
Author: Isis Lovecruft <isis at torproject.org>
Date:   Tue Mar 11 20:25:20 2014 +0000

    Add crypto.getRSAKey() function.
    
     * ADD a function for getting/creating RSA keypairs.
     * ADDS a dependency on pyCrypto.
---
 lib/bridgedb/crypto.py |   72 ++++++++++++++++++++++++++++++++++++++++++++++++
 requirements.txt       |    1 +
 2 files changed, 73 insertions(+)

diff --git a/lib/bridgedb/crypto.py b/lib/bridgedb/crypto.py
index f5a95ec..dc2523b 100644
--- a/lib/bridgedb/crypto.py
+++ b/lib/bridgedb/crypto.py
@@ -36,11 +36,18 @@ import os
 
 import OpenSSL.rand
 
+from Crypto.Cipher import PKCS1_OAEP
+from Crypto.PublicKey import RSA
+
 
 #: The hash digest to use for HMACs.
 DIGESTMOD = hashlib.sha1
 
 
+class RSAKeyGenerationError(Exception):
+    """Raised when there was an error creating an RSA keypair."""
+
+
 def writeKeyToFile(key, filename):
     """Write **key** to **filename**, with ``0400`` permissions.
 
@@ -60,6 +67,71 @@ def writeKeyToFile(key, filename):
     os.fsync(fd)
     os.close(fd)
 
+def getRSAKey(filename, bits=2048):
+    """Load the RSA key stored in **filename**, or create and save a new key.
+
+    >>> from bridgedb import crypto
+    >>> keyfile = 'doctest_getRSAKey'
+    >>> message = "The secret words are Squeamish Ossifrage."
+    >>> keypair = crypto.getRSAKey(keyfile, bits=2048)
+    >>> (secretkey, publickey) = keypair
+    >>> encrypted = publickey.encrypt(message)
+    >>> assert encrypted != message
+    >>> decrypted = secretkey.decrypt(encrypted)
+    >>> assert message == decrypted
+
+
+    If **filename** already exists, it is assumed to contain a PEM-encoded RSA
+    private key, which will be read from the file. (The parameters of a
+    private RSA key contain the public exponent and public modulus, which
+    together comprise the public key ― ergo having two separate keyfiles is
+    assumed unnecessary.)
+
+    If **filename** doesn't exist, a new RSA keypair will be created, and the
+    private key will be stored in **filename**, using :func:`writeKeyToFile`.
+
+    Once the private key is either loaded or created, the public key is
+    extracted from it. Both keys are then input into PKCS#1 RSAES-OAEP cipher
+    schemes (see `RFC 3447 §7.1`__) in order to introduce padding, and then
+    returned.
+
+    .. __: https://tools.ietf.org/html/rfc3447#section-7.1
+
+    :param str filename: The filename to which the secret parameters of the
+        RSA key are stored in.
+    :param int bits: If no key is found within the file, create a new key with
+        this bitlength and store it in **filename**.
+    :rtype: tuple of ``Crypto.Cipher.PKCS1_OAEP.PKCS1OAEP_Cipher``
+    :returns: A 2-tuple of ``(privatekey, publickey)``, which are PKCS#1
+        RSAES-OAEP padded and encoded private and public keys, forming an RSA
+        keypair.
+    """
+    filename = os.path.extsep.join([filename, 'sec'])
+    keyfile = os.path.join(os.getcwd(), filename)
+
+    try:
+        fh = open(keyfile, 'rb')
+    except IOError:
+        logging.info("Generating %d-bit RSA keypair..." % bits)
+        secretKey = RSA.generate(bits, e=65537)
+
+        # Store a PEM copy of the secret key (which contains the parameters
+        # necessary to create the corresponding public key):
+        secretKeyPEM = secretKey.exportKey("PEM")
+        writeKeyToFile(secretKeyPEM, keyfile)
+    else:
+        logging.info("Secret RSA keyfile %r found. Loading..." % filename)
+        secretKey = RSA.importKey(fh.read())
+        fh.close()
+
+    publicKey = secretKey.publickey()
+
+    # Add PKCS#1 OAEP padding to the secret and public keys:
+    sk = PKCS1_OAEP.new(secretKey)
+    pk = PKCS1_OAEP.new(publicKey)
+
+    return (sk, pk)
+
 def getKey(filename):
     """Load the key stored in ``filename``, or create a new key.
 
diff --git a/requirements.txt b/requirements.txt
index 72a322c..2d1be77 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,6 +4,7 @@ Mako==0.8.1
 MarkupSafe==0.18
 Twisted==13.1.0
 https://ipaddr-py.googlecode.com/files/ipaddr-2.1.10.tar.gz#sha1=c608450b077b19773d4f1b5f1ef88b26f6650ce0#egg=ipaddr-2.1.10-py2.7
+pycrypto==2.6.1
 pyOpenSSL==0.13.1
 pygeoip==0.2.7
 pygpgme==0.3





More information about the tor-commits mailing list