Set up the TSIG hashes table only once.
authorBob Halley <halley@nominum.com>
Sun, 17 Oct 2010 17:11:07 +0000 (18:11 +0100)
committerBob Halley <halley@nominum.com>
Sun, 17 Oct 2010 17:11:07 +0000 (18:11 +0100)
Make algorithm constants domain names.

Detect attempts to use HMAC-SHA384 and HMAC-SHA512 on Python versions
less than 2.5.2, and raise a NotImplemented exception.  (We want to do
this because old versions of Python do not compute them correctly.)

ChangeLog
dns/tsig.py

index a07c67b209d86c143633e12908f315809ef46037..5a90235ae29cd927bd278c96e482027bb02dbce3 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,10 @@
 2010-10-17  Bob Halley  <halley@dnspython.org>
 
+       * Python prior to 2.5.2 doesn't compute the correct values for
+         HMAC-SHA384 and HMAC-SHA512.  We now detect attempts to use
+         them and raise NotImplemented if the Python version is too old.
+         Thanks to Kevin Chen for reporting the problem.
+
        * Various routines that took the string forms of rdata types and
          classes did not permit the strings to be Unicode strings.
          Thanks to Ryan Workman for reporting the issue.
index 7d4c3e0346d5445dfdf58116a26490e5f4ab3684..e09572ee50ad8a67873713206307cedca1b50d37 100644 (file)
@@ -17,6 +17,7 @@
 
 import hmac
 import struct
+import sys
 
 import dns.exception
 import dns.rdataclass
@@ -52,12 +53,12 @@ class PeerBadTruncation(PeerError):
 
 # TSIG Algorithms
 
-HMAC_MD5 = "HMAC-MD5.SIG-ALG.REG.INT"
-HMAC_SHA1 = "hmac-sha1"
-HMAC_SHA224 = "hmac-sha224"
-HMAC_SHA256 = "hmac-sha256"
-HMAC_SHA384 = "hmac-sha384"
-HMAC_SHA512 = "hmac-sha512"
+HMAC_MD5 = dns.name.from_text("HMAC-MD5.SIG-ALG.REG.INT")
+HMAC_SHA1 = dns.name.from_text("hmac-sha1")
+HMAC_SHA224 = dns.name.from_text("hmac-sha224")
+HMAC_SHA256 = dns.name.from_text("hmac-sha256")
+HMAC_SHA384 = dns.name.from_text("hmac-sha384")
+HMAC_SHA512 = dns.name.from_text("hmac-sha512")
 
 default_algorithm = HMAC_MD5
 
@@ -176,25 +177,20 @@ def validate(wire, keyname, secret, now, request_mac, tsig_start, tsig_rdata,
         raise BadSignature
     return ctx
 
-def get_algorithm(algorithm):
-    """Returns the wire format string and the hash module to use for the
-    specified TSIG algorithm
+_hashes = None
 
-    @rtype: (string, hash constructor)
-    @raises NotImplementedError: I{algorithm} is not supported
-    """
-
-    hashes = {}
+def _setup_hashes():
+    global _hashes
+    _hashes = {}
     try:
         import hashlib
-        hashes[dns.name.from_text(HMAC_SHA224)] = hashlib.sha224
-        hashes[dns.name.from_text(HMAC_SHA256)] = hashlib.sha256
-        hashes[dns.name.from_text(HMAC_SHA384)] = hashlib.sha384
-        hashes[dns.name.from_text(HMAC_SHA512)] = hashlib.sha512
-        hashes[dns.name.from_text(HMAC_SHA1)] = hashlib.sha1
-        hashes[dns.name.from_text(HMAC_MD5)] = hashlib.md5
-
-        import sys
+        _hashes[HMAC_SHA224] = hashlib.sha224
+        _hashes[HMAC_SHA256] = hashlib.sha256
+        _hashes[HMAC_SHA384] = hashlib.sha384
+        _hashes[HMAC_SHA512] = hashlib.sha512
+        _hashes[HMAC_SHA1] = hashlib.sha1
+        _hashes[HMAC_MD5] = hashlib.md5
+
         if sys.hexversion < 0x02050000:
             # hashlib doesn't conform to PEP 247: API for
             # Cryptographic Hash Functions, which hmac before python
@@ -207,19 +203,36 @@ def get_algorithm(algorithm):
                 def new(self, *args, **kwargs):
                     return self.basehash(*args, **kwargs)
 
-            for name in hashes:
-                hashes[name] = HashlibWrapper(hashes[name])
+            for name in _hashes:
+                _hashes[name] = HashlibWrapper(_hashes[name])
 
     except ImportError:
         import md5, sha
-        hashes[dns.name.from_text(HMAC_MD5)] =  md5
-        hashes[dns.name.from_text(HMAC_SHA1)] = sha
+        _hashes[dns.name.from_text(HMAC_MD5)] =  md5
+        _hashes[dns.name.from_text(HMAC_SHA1)] = sha
+
+def get_algorithm(algorithm):
+    """Returns the wire format string and the hash module to use for the
+    specified TSIG algorithm
+
+    @rtype: (string, hash constructor)
+    @raises NotImplementedError: I{algorithm} is not supported
+    """
+
+    global _hashes
+    if _hashes is None:
+        _setup_hashes()
 
     if isinstance(algorithm, (str, unicode)):
         algorithm = dns.name.from_text(algorithm)
 
-    if algorithm in hashes:
-        return (algorithm.to_digestable(), hashes[algorithm])
+    if sys.hexversion < 0x02050200 and \
+       (algorithm == HMAC_SHA384 or algorithm == HMAC_SHA512):
+        raise NotImplementedError("TSIG algorithm " + str(algorithm) +
+                                  " requires Python 2.5.2 or later")
 
-    raise NotImplementedError("TSIG algorithm " + str(algorithm) +
-                              " is not supported")
+    try:
+        return (algorithm.to_digestable(), _hashes[algorithm])
+    except KeyError:
+        raise NotImplementedError("TSIG algorithm " + str(algorithm) +
+                                  " is not supported")