[CalendarServer-changes] [9832] CalendarServer/branches/users/cdaboo/ischedule-dkim

source_changes at macosforge.org source_changes at macosforge.org
Fri Sep 21 13:13:56 PDT 2012


Revision: 9832
          http://trac.calendarserver.org//changeset/9832
Author:   cdaboo at apple.com
Date:     2012-09-21 13:13:55 -0700 (Fri, 21 Sep 2012)
Log Message:
-----------
Switch to PyCrypto.

Modified Paths:
--------------
    CalendarServer/branches/users/cdaboo/ischedule-dkim/calendarserver/tools/dkimtool.py
    CalendarServer/branches/users/cdaboo/ischedule-dkim/twistedcaldav/scheduling/ischedule/dkim.py

Modified: CalendarServer/branches/users/cdaboo/ischedule-dkim/calendarserver/tools/dkimtool.py
===================================================================
--- CalendarServer/branches/users/cdaboo/ischedule-dkim/calendarserver/tools/dkimtool.py	2012-09-21 17:16:16 UTC (rev 9831)
+++ CalendarServer/branches/users/cdaboo/ischedule-dkim/calendarserver/tools/dkimtool.py	2012-09-21 20:13:55 UTC (rev 9832)
@@ -15,8 +15,9 @@
 # limitations under the License.
 ##
 
+from Crypto.PublicKey import RSA
 from StringIO import StringIO
-from subprocess import PIPE
+from twext.web2.client.http import ClientRequest
 from twext.web2.http_headers import Headers
 from twext.web2.stream import MemoryStream
 from twisted.internet import reactor
@@ -24,45 +25,34 @@
 from twisted.python.usage import Options
 from twistedcaldav.scheduling.ischedule.dkim import RSA256, DKIMRequest, \
     PublicKeyLookup, DKIMVerifier, DKIMVerificationError
-import subprocess
 import sys
-from twext.web2.client.http import ClientRequest
-import rsa
 
 
 def _doKeyGeneration(options):
-    child = subprocess.Popen(
-        args=["openssl", "genrsa", str(options["key-size"]), ],
-        stdout=PIPE,
-    )
-    output, status = child.communicate()
-    if status:
-        print "Failed to generate private key"
-        sys.exit(1)
-    output = output.strip()
+
+    key = RSA.generate(options["key-size"])
+    output = key.exportKey()
+    lineBreak = False
     if options["key"]:
         open(options["key"], "w").write(output)
     else:
         print output
+        lineBreak = True
 
-    child = subprocess.Popen(
-        args=["openssl", "rsa", "-pubout", ],
-        stdout=PIPE,
-        stdin=PIPE,
-    )
-    output, status = child.communicate(output)
-    if status:
-        print "Failed to generate public key"
-        sys.exit(1)
-    output = output.strip()
+    output = key.publickey().exportKey()
     if options["pub-key"]:
         open(options["pub-key"], "w").write(output)
     else:
+        if lineBreak:
+            print
         print output
+        lineBreak = True
 
     if options["txt"]:
         output = "".join(output.splitlines()[1:-1])
         txt = "v=DKIM1; p=%s" % (output,)
+        if lineBreak:
+            print
         print txt
 
 
@@ -96,8 +86,13 @@
         sign_headers,
         True,
         True,
+        False,
         int(options["expire"]),
     )
+    if options["fake-time"]:
+        dkim.time = "100"
+        dkim.expire = "200"
+        dkim.message_id = "1"
     yield dkim.sign()
 
     s = StringIO()
@@ -122,6 +117,8 @@
         lookup = None
 
     dkim = DKIMVerifier(request, lookup)
+    if options["fake-time"]:
+        dkim.time = 100
 
     try:
         yield dkim.verify()
@@ -181,7 +178,7 @@
         """
         Do the key lookup using the actual lookup method.
         """
-        return rsa.PublicKey.load_pkcs1(open(self.pubkeyfile).read())
+        return RSA.importKey(open(self.pubkeyfile).read())
 
 
 
@@ -209,6 +206,8 @@
     --pub-key FILE     Public key file to create [stdout]
     --key-size SIZE    Key size [1024]
     --txt              Also generate the public key TXT record
+    --fake-time        Use fake t=, x= values when signing and also
+                       ignore expiration on verification
 
     # Request
     --request FILE      An HTTP request to sign
@@ -225,8 +224,14 @@
                         q= lookup
 
 Description:
-    This utility is for testing DKIM signed HTTP request.
+    This utility is for testing DKIM signed HTTP requests. Key operations are:
 
+    --key-gen: generate a private/public RSA key.
+
+    --request: sign an HTTP request.
+
+    --verify:  verify a signed HTTP request.
+
 """
 
 
@@ -241,6 +246,7 @@
         ['verbose', 'v', "Verbose logging."],
         ['key-gen', 'g', "Generate private/public key files"],
         ['txt', 't', "Also generate the public key TXT record"],
+        ['fake-time', 'f', "Fake time values for signing/verification"],
     ]
 
     optParameters = [

Modified: CalendarServer/branches/users/cdaboo/ischedule-dkim/twistedcaldav/scheduling/ischedule/dkim.py
===================================================================
--- CalendarServer/branches/users/cdaboo/ischedule-dkim/twistedcaldav/scheduling/ischedule/dkim.py	2012-09-21 17:16:16 UTC (rev 9831)
+++ CalendarServer/branches/users/cdaboo/ischedule-dkim/twistedcaldav/scheduling/ischedule/dkim.py	2012-09-21 20:13:55 UTC (rev 9832)
@@ -27,12 +27,15 @@
 from twistedcaldav.simpleresource import SimpleResource, SimpleDataResource
 from twistedcaldav.scheduling.ischedule.utils import lookupDataViaTXT
 
+from Crypto.Hash import SHA, SHA256
+from Crypto.PublicKey import RSA
+from Crypto.Signature import PKCS1_v1_5
+
 import base64
 import binascii
 import collections
 import hashlib
 import os
-import rsa
 import textwrap
 import time
 import uuid
@@ -92,7 +95,7 @@
                 log.error(msg)
                 raise ConfigurationError(msg)
             try:
-                rsa.PrivateKey.load_pkcs1(key_data)
+                RSA.importKey(key_data)
             except:
                 msg = "DKIM: Invalid private key file: %s" % (config.Scheduling.iSchedule.DKIM.PrivateKeyFile,)
                 log.error(msg)
@@ -106,7 +109,7 @@
                 log.error(msg)
                 raise ConfigurationError(msg)
             try:
-                rsa.PublicKey.load_pkcs1(key_data)
+                RSA.importKey(key_data)
             except:
                 msg = "DKIM: Invalid public key file: %s" % (config.Scheduling.iSchedule.DKIM.PublicKeyFile,)
                 log.error(msg)
@@ -170,11 +173,22 @@
         """
         return {
             RSA1  : "SHA-1",
-            RSA256: "SHA-256"
+            RSA256: "SHA-256",
         }[algorithm]
 
 
     @staticmethod
+    def hash_func(algorithm):
+        """
+        Return RSA hash name for DKIM algorithm.
+        """
+        return {
+            RSA1  : SHA,
+            RSA256: SHA256,
+        }[algorithm]
+
+
+    @staticmethod
     def extractTags(data):
         """
         Split a DKIM tag list into a dict, removing unneeded whitespace.
@@ -299,6 +313,7 @@
 
         self.hash_method = DKIMUtils.hashlib_method(self.algorithm)
         self.hash_name = DKIMUtils.hash_name(self.algorithm)
+        self.hash_func = DKIMUtils.hash_func(self.algorithm)
 
         self.keyMethods = []
         if useDNSKey:
@@ -404,8 +419,10 @@
     def generateSignature(self, headers):
         # Sign the hash
         if self.key_file not in self.keys:
-            self.keys[self.key_file] = rsa.PrivateKey.load_pkcs1(open(self.key_file).read())
-        return base64.b64encode(rsa.sign(headers, self.keys[self.key_file], self.hash_name))
+            self.keys[self.key_file] = RSA.importKey(open(self.key_file).read())
+        h = self.hash_func.new(headers)
+        signer = PKCS1_v1_5.new(self.keys[self.key_file])
+        return base64.b64encode(signer.sign(h))
 
 
 
@@ -447,7 +464,9 @@
             PublicKeyLookup_DNSTXT,
         ) if key_lookup is None else key_lookup
 
+        self.time = int(time.time())
 
+
     @inlineCallbacks
     def verify(self):
         """
@@ -468,8 +487,11 @@
 
         # Do header verification
         try:
-            rsa.verify(headers, base64.b64decode(self.dkim_tags["b"]), pubkey)
-        except rsa.VerificationError:
+            h = self.hash_func.new(headers)
+            verifier = PKCS1_v1_5.new(pubkey)
+            if not verifier.verify(h, base64.b64decode(self.dkim_tags["b"])):
+                raise ValueError()
+        except ValueError:
             msg = "Could not verify signature"
             _debug_msg = """
 DKIM-Signature:%s
@@ -563,7 +585,7 @@
 
         # Check expiration
         if "x" in self.dkim_tags:
-            diff_time = int(time.time()) - int(self.dkim_tags["x"])
+            diff_time = self.time - int(self.dkim_tags["x"])
             if diff_time > 0:
                 msg = "Signature expired: %d seconds" % (diff_time,)
                 log.debug("DKIM: " + msg)
@@ -593,6 +615,7 @@
 
         # Some useful bits
         self.hash_method = DKIMUtils.hashlib_method(self.dkim_tags["a"])
+        self.hash_func = DKIMUtils.hash_func(self.dkim_tags["a"])
         self.key_methods = self.dkim_tags["q"].split(":")
 
 
@@ -734,7 +757,7 @@
 """ % ("\n".join(textwrap.wrap(pkey["p"], 64)),)
 
         try:
-            key = rsa.PublicKey.load_pkcs1(key_data)
+            key = RSA.importKey(key_data)
             key._original_data = key_data
             return key
         except:
@@ -882,7 +905,7 @@
 
         # Make sure we can parse a valid public key
         try:
-            rsa.PublicKey.load_pkcs1(key_data)
+            RSA.importKey(key_data)
         except:
             log.error("DKIM: Invalid public key file: %s" % (pubkeyfile,))
             raise
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20120921/6c4b1606/attachment-0001.html>


More information about the calendarserver-changes mailing list