[Python-modules-commits] [dkimpy] 01/04: Import dkimpy_0.5.5.orig.tar.gz

Scott Kitterman kitterman at moszumanska.debian.org
Mon Dec 7 22:36:36 UTC 2015


This is an automated email from the git hooks/post-receive script.

kitterman pushed a commit to branch master
in repository dkimpy.

commit aeeb28de2c1885e832da763db5b6291651b745b0
Author: Scott Kitterman <scott at kitterman.com>
Date:   Mon Dec 7 17:27:35 2015 -0500

    Import dkimpy_0.5.5.orig.tar.gz
---
 ChangeLog                     |  4 +++
 PKG-INFO                      |  2 +-
 README                        |  4 +--
 dkim/__init__.py              | 80 +++++++++++++++++++++++++++--------------
 dkim/tests/data/test2.message | 24 +++++++++++++
 dkim/tests/test_dkim.py       | 84 ++++++++++++++++++++++++++++++++++---------
 dkim/tests/test_util.py       |  4 +--
 dkim/util.py                  |  2 +-
 setup.py                      |  2 +-
 9 files changed, 155 insertions(+), 51 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 1551193..4dcce8a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2015-12-07 Version 0.5.5
+    - Fix and test case for case insensitive subdomain matching.
+    - Python3 compatibility fixes and test cases thanks to Diane Trout
+
 2013-06-10 Version 0.5.4
     - Fixed error in FWS regular expression that cause some valid signatures
       to fail verification (Thanks to Peter Palfrader (weasel) for the patch)
diff --git a/PKG-INFO b/PKG-INFO
index 96d9d0d..9e0caec 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: dkimpy
-Version: 0.5.4
+Version: 0.5.5
 Summary: DKIM (DomainKeys Identified Mail)
 Home-page: https://launchpad.net/dkimpy
 Author: Scott Kitterman
diff --git a/README b/README
index 85c38b6..f03a59f 100644
--- a/README
+++ b/README
@@ -1,7 +1,7 @@
 dkimpy - DKIM (DomainKeys Identified Mail)
 https://launchpad.net/dkimpy/
 
-Fork of:
+Friendly fork of:
 http://hewgill.com/pydkim/
 
 INTRODUCTION
@@ -11,7 +11,7 @@ signing and verification.
 
 VERSION
 
-This is dkimpy 0.5.4.
+This is dkimpy 0.5.5.
 
 REQUIREMENTS
 
diff --git a/dkim/__init__.py b/dkim/__init__.py
index 36d6fb0..eec9992 100644
--- a/dkim/__init__.py
+++ b/dkim/__init__.py
@@ -119,8 +119,8 @@ def select_headers(headers, include_headers):
     return sign_headers
 
 # FWS  =  ([*WSP CRLF] 1*WSP) /  obs-FWS ; Folding white space  [RFC5322]
-FWS = r'(?:(?:\s*\r?\n)?\s+)?'
-RE_BTAG = re.compile(r'([;\s]b'+FWS+r'=)(?:'+FWS+r'[a-zA-Z0-9+/=])*(?:\r?\n\Z)?')
+FWS = br'(?:(?:\s*\r?\n)?\s+)?'
+RE_BTAG = re.compile(br'([;\s]b'+FWS+br'=)(?:'+FWS+br'[a-zA-Z0-9+/=])*(?:\r?\n\Z)?')
 
 def hash_headers(hasher, canonicalize_headers, headers, include_headers,
                  sigheader, sig):
@@ -161,24 +161,36 @@ def validate_signature_fields(sig):
     # Nasty hack to support both str and bytes... check for both the
     # character and integer values.
     if b'i' in sig and (
-        not sig[b'i'].endswith(sig[b'd']) or
+        not sig[b'i'].lower().endswith(sig[b'd'].lower()) or
         sig[b'i'][-len(sig[b'd'])-1] not in ('@', '.', 64, 46)):
         raise ValidationError(
             "i= domain is not a subdomain of d= (i=%s d=%s)" %
             (sig[b'i'], sig[b'd']))
-    if b'l' in sig and re.match(br"\d{,76}$", sig['l']) is None:
+    if b'l' in sig and re.match(br"\d{,76}$", sig[b'l']) is None:
         raise ValidationError(
             "l= value is not a decimal integer (%s)" % sig[b'l'])
     if b'q' in sig and sig[b'q'] != b"dns/txt":
         raise ValidationError("q= value is not dns/txt (%s)" % sig[b'q'])
-    if b't' in sig and re.match(br"\d+$", sig[b't']) is None:
-        raise ValidationError(
-            "t= value is not a decimal integer (%s)" % sig[b't'])
+    now = int(time.time())
+    slop = 36000		# 10H leeway for mailers with inaccurate clocks
+    t_sign = 0
+    if b't' in sig:
+        if re.match(br"\d+$", sig[b't']) is None:
+            raise ValidationError(
+        	"t= value is not a decimal integer (%s)" % sig[b't'])
+        t_sign = int(sig[b't'])
+        if t_sign > now + slop:
+            raise ValidationError(
+        	"t= value is in the future (%s)" % sig[b't'])
     if b'x' in sig:
         if re.match(br"\d+$", sig[b'x']) is None:
             raise ValidationError(
                 "x= value is not a decimal integer (%s)" % sig[b'x'])
-        if int(sig[b'x']) < int(sig[b't']):
+        x_sign = int(sig[b'x'])
+        if x_sign < now - slop:
+            raise ValidationError(
+        	"x= value is past (%s)" % sig[b'x'])
+        if x_sign < t_sign:
             raise ValidationError(
                 "x= value is less than t= value (x=%s t=%s)" %
                 (sig[b'x'], sig[b't']))
@@ -212,16 +224,28 @@ def rfc822_parse(message):
         i += 1
     return (headers, b"\r\n".join(lines[i:]))
 
-
+def text(s):
+    """Normalize bytes/str to str for python 2/3 compatible doctests.
+    >>> text(b'foo')
+    'foo'
+    >>> text(u'foo')
+    'foo'
+    >>> text('foo')
+    'foo'
+    """
+    if type(s) is str: return s
+    s = s.decode('ascii')
+    if type(s) is str: return s
+    return s.encode('ascii')
 
 def fold(header):
     """Fold a header line into multiple crlf-separated lines at column 72.
 
-    >>> fold(b'foo')
+    >>> text(fold(b'foo'))
     'foo'
-    >>> fold(b'foo  '+b'foo'*24).splitlines()[0]
+    >>> text(fold(b'foo  '+b'foo'*24).splitlines()[0])
     'foo  '
-    >>> fold(b'foo'*25).splitlines()[-1]
+    >>> text(fold(b'foo'*25).splitlines()[-1])
     ' foo'
     >>> len(fold(b'foo'*25).splitlines()[0])
     72
@@ -256,31 +280,31 @@ class DKIM(object):
   #: be in the default FROZEN list, but that could also make signatures 
   #: more fragile than necessary.  
   #: @since: 0.5
-  RFC5322_SINGLETON = ('date','from','sender','reply-to','to','cc','bcc',
-        'message-id','in-reply-to','references')
+  RFC5322_SINGLETON = (b'date',b'from',b'sender',b'reply-to',b'to',b'cc',b'bcc',
+        b'message-id',b'in-reply-to',b'references')
 
   #: Header fields to protect from additions by default.
   #: 
   #: The short list below is the result more of instinct than logic.
   #: @since: 0.5
-  FROZEN = ('from','date','subject')
+  FROZEN = (b'from',b'date',b'subject')
 
   #: The rfc4871 recommended header fields to sign
   #: @since: 0.5
   SHOULD = (
-    'sender', 'reply-to', 'subject', 'date', 'message-id', 'to', 'cc',
-    'mime-version', 'content-type', 'content-transfer-encoding', 'content-id',
-    'content- description', 'resent-date', 'resent-from', 'resent-sender',
-    'resent-to', 'resent-cc', 'resent-message-id', 'in-reply-to', 'references',
-    'list-id', 'list-help', 'list-unsubscribe', 'list-subscribe', 'list-post',
-    'list-owner', 'list-archive'
+    b'sender', b'reply-to', b'subject', b'date', b'message-id', b'to', b'cc',
+    b'mime-version', b'content-type', b'content-transfer-encoding',
+    b'content-id', b'content- description', b'resent-date', b'resent-from',
+    b'resent-sender', b'resent-to', b'resent-cc', b'resent-message-id',
+    b'in-reply-to', 'references', b'list-id', b'list-help', b'list-unsubscribe',
+    b'list-subscribe', b'list-post', b'list-owner', b'list-archive'
   )
 
   #: The rfc4871 recommended header fields not to sign.
   #: @since: 0.5
   SHOULD_NOT = (
-    'return-path', 'received', 'comments', 'keywords', 'bcc', 'resent-bcc',
-    'dkim-signature'
+    b'return-path',b'received',b'comments',b'keywords',b'bcc',b'resent-bcc',
+    b'dkim-signature'
   )
 
   #: Create a DKIM instance to sign and verify rfc5322 messages.
@@ -319,7 +343,7 @@ class DKIM(object):
 
     >>> dkim = DKIM()
     >>> dkim.add_frozen(DKIM.RFC5322_SINGLETON)
-    >>> sorted(dkim.frozen_sign)
+    >>> [text(x) for x in sorted(dkim.frozen_sign)]
     ['cc', 'date', 'from', 'in-reply-to', 'message-id', 'references', 'reply-to', 'sender', 'subject', 'to']
     """
     self.frozen_sign.update(x.lower() for x in s
@@ -418,7 +442,7 @@ class DKIM(object):
         include_headers = self.default_sign_headers()
 
     # rfc4871 says FROM is required
-    if 'from' not in ( x.lower() for x in include_headers ):
+    if b'from' not in ( x.lower() for x in include_headers ):
         raise ParameterError("The From header field MUST be signed")
 
     # raise exception for any SHOULD_NOT headers, call can modify 
@@ -540,6 +564,8 @@ class DKIM(object):
     if not s:
         raise KeyFormatError("missing public key: %s"%name)
     try:
+        if type(s) is str:
+          s = s.encode('ascii')
         pub = parse_tag_value(s)
     except InvalidTagValueList as e:
         raise KeyFormatError(e)
@@ -556,8 +582,8 @@ class DKIM(object):
     # fields when verifying.  Since there should be only one From header,
     # this shouldn't break any legitimate messages.  This could be
     # generalized to check for extras of other singleton headers.
-    if 'from' in include_headers:
-      include_headers.append('from')      
+    if b'from' in include_headers:
+      include_headers.append(b'from')      
     h = hasher()
     self.signed_headers = hash_headers(
         h, canon_policy, headers, include_headers, sigheaders[idx], sig)
diff --git a/dkim/tests/data/test2.message b/dkim/tests/data/test2.message
new file mode 100644
index 0000000..d689fbf
--- /dev/null
+++ b/dkim/tests/data/test2.message
@@ -0,0 +1,24 @@
+Comment: degenerate folding is ugly but legal, reported in Debian bug#711751
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
+	d=gmail.com; s=20120113;
+	h=mime-version:sender:from:date:x-google-sender-auth:message-id
+	:subject:to:content-type;
+	bh=NRDGmXYX648Rm6cs06aAQIE77gu68nsSHYB4kAMm7QQ=;
+	b=VaN3KmNPlU1uSNproy8wF+6qwTUKEcyzanoPSo/u8P0p8rtHgQpOW5/nJ+/ExQ9jKN
+	FWTyZ9PLecg/0De0QYV18GQovYb3PVUCDHS7dYzfWp072lFPAhISUancFc30amzRPXcy
+	J2lnvgoPcFuqDh5tLPchz8LdeIL0hMr2Xt+xEibHftqYT0JRXX4LXkZdO/b/i825qMtL
+	W51wBB0V6L1ZU156A9cZWQWvwnQ/lV7PV7AwRqGbIESguRLfCbM+UIAGoCR8QtTO0lkY
+	bGqPQucn+1eZZUNsEJAWFI6eo2MmxY/FABEURGYAukaTg13UC9W+O6kGPH5iS5aRpAAT eKbQ==
+MIME-Version: 1.0
+Sender: kaner.mail at gmail.com
+Received: by 10.42.92.137 with HTTP; Sun, 9 Jun 2013 02:37:02 -0700 (PDT)
+From: Christian Fromme <cfr at strace.org>
+Date: Sun, 9 Jun 2013 11:37:02 +0200
+X-Google-Sender-Auth: ZkDaYkXZHozJZyNGgvWFlv78IRY
+Message-ID: <CABop5ezogOsxQXV1pUHCZDf-w3XpOFPsbSm8o8Lx5CRX-eq4fg at mail.gmail.com>
+Subject: DKIM test mail #1
+To: gettor at gettor.torproject.org
+Content-Type: text/plain; charset=ISO-8859-1
+
+Hello, DKIM
+
diff --git a/dkim/tests/test_dkim.py b/dkim/tests/test_dkim.py
index bc00077..d721d78 100644
--- a/dkim/tests/test_dkim.py
+++ b/dkim/tests/test_dkim.py
@@ -18,6 +18,7 @@
 
 import os.path
 import unittest
+import time
 
 import dkim
 
@@ -53,12 +54,28 @@ class TestSignAndVerify(unittest.TestCase):
         self.key = read_test_data("test.private")
 
     def dnsfunc(self, domain):
+        sample_dns = """\
+k=rsa; \
+p=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANmBe10IgY+u7h3enWTukkqtUD5PR52T\
+b/mPfjC0QJTocVBq6Za/PlzfV+Py92VaCak19F4WrbVTK5Gg5tW220MCAwEAAQ=="""
+
+        _dns_responses = {
+          'example._domainkey.canonical.com.': sample_dns,
+          'test._domainkey.example.com.': read_test_data("test.txt"),
+          '20120113._domainkey.gmail.com.': """k=rsa; \
+p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1Kd87/UeJjenpabgbFwh\
++eBCsSTrqmwIYYvywlbhbqoo2DymndFkbjOVIPIldNs/m40KF+yzMn1skyoxcTUGCQ\
+s8g3FgD2Ap3ZB5DekAo5wMmk4wimDO+U8QzI3SD07y2+07wlNWwIt8svnxgdxGkVbb\
+hzY8i+RQ9DpSVpPbF7ykQxtKXkv/ahW3KjViiAH+ghvvIhkx4xYSIc9oSwVmAl5Oct\
+MEeWUwg8Istjqz8BZeTWbf41fbNhte7Y+YqZOwq1Sd0DbvYAD9NOZK9vlfuac0598H\
+Y+vtSBczUiKERHv1yRbcaQtZFh5wtiRrN04BLUTD21MycBX5jYchHjPY/wIDAQAB"""
+        }
         try:
             domain = domain.decode('ascii')
         except UnicodeDecodeError:
             return None
-        self.assertEqual('test._domainkey.example.com.', domain)
-        return read_test_data("test.txt")
+        self.assertTrue(domain in _dns_responses,domain)
+        return _dns_responses[domain]
 
     def test_verifies(self):
         # A message verifies after being signed.
@@ -92,14 +109,14 @@ class TestSignAndVerify(unittest.TestCase):
       # <https://bugs.launchpad.net/dkimpy/+bug/939128>
       # Simple-mode signature header verification is wrong
       # (should ignore FWS anywhere in signature tag: b=)
-      sample_msg = """\
+      sample_msg = b"""\
 From: mbp at canonical.com
 To: scottk at example.com
 Subject: this is my
     test message
-""".replace('\n', '\r\n')
+""".replace(b'\n', b'\r\n')
 
-      sample_privkey = """\
+      sample_privkey = b"""\
 -----BEGIN RSA PRIVATE KEY-----
 MIIBOwIBAAJBANmBe10IgY+u7h3enWTukkqtUD5PR52Tb/mPfjC0QJTocVBq6Za/
 PlzfV+Py92VaCak19F4WrbVTK5Gg5tW220MCAwEAAQJAYFUKsD+uMlcFu1D3YNaR
@@ -118,15 +135,9 @@ b/mPfjC0QJTocVBq6Za/PlzfV+Py92VaCak19F4WrbVTK5Gg5tW220MCAwEAAQ==
 -----END PUBLIC KEY-----
 """
 
-      sample_dns = """\
-k=rsa; \
-p=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANmBe10IgY+u7h3enWTukkqtUD5PR52T\
-b/mPfjC0QJTocVBq6Za/PlzfV+Py92VaCak19F4WrbVTK5Gg5tW220MCAwEAAQ=="""
-
-      _dns_responses = {'example._domainkey.canonical.com.': sample_dns}
       for header_mode in [dkim.Relaxed, dkim.Simple]:
 
-        dkim_header = dkim.sign(sample_msg, 'example', 'canonical.com',
+        dkim_header = dkim.sign(sample_msg, b'example', b'canonical.com',
             sample_privkey, canonicalize=(header_mode, dkim.Relaxed))
         # Folding dkim_header affects b= tag only, since dkim.sign folds
         # sig_value with empty b= before hashing, and then appends the
@@ -135,7 +146,7 @@ b/mPfjC0QJTocVBq6Za/PlzfV+Py92VaCak19F4WrbVTK5Gg5tW220MCAwEAAQ=="""
         # simple canonicalization.  
         # http://tools.ietf.org/html/rfc4871#section-3.5
         signed = dkim.fold(dkim_header) + sample_msg
-        result = dkim.verify(signed,dnsfunc=lambda x: _dns_responses[x],
+        result = dkim.verify(signed,dnsfunc=self.dnsfunc,
                 minkey=512)
         self.assertTrue(result)
         dkim_header = dkim.fold(dkim_header)
@@ -143,9 +154,17 @@ b/mPfjC0QJTocVBq6Za/PlzfV+Py92VaCak19F4WrbVTK5Gg5tW220MCAwEAAQ=="""
         pos = dkim_header.rindex(b'\r\n ')
         dkim_header = dkim_header[:pos]+b'\r\n\t'+dkim_header[pos+3:]
         result = dkim.verify(dkim_header + sample_msg,
-                dnsfunc=lambda x: _dns_responses[x], minkey=512)
+                dnsfunc=self.dnsfunc, minkey=512)
         self.assertTrue(result)
 
+    def test_degenerate_folding(self):
+        # <http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=711751>
+        # degenerate folding is ugly but legal
+        message = read_test_data("test2.message")
+        dv = dkim.DKIM(message)
+        res = dv.verify(dnsfunc=self.dnsfunc)
+        self.assertTrue(res)
+    	
     def test_extra_headers(self):
         # <https://bugs.launchpad.net/dkimpy/+bug/737311>
         # extra headers above From caused failure
@@ -155,7 +174,7 @@ b/mPfjC0QJTocVBq6Za/PlzfV+Py92VaCak19F4WrbVTK5Gg5tW220MCAwEAAQ=="""
             for body_algo in (b"simple", b"relaxed"):
                 d = dkim.DKIM(message)
                 # bug requires a repeated header to manifest
-                d.should_not_sign.remove('received')
+                d.should_not_sign.remove(b'received')
                 sig = d.sign(b"test", b"example.com", self.key,
                     include_headers=d.all_sign_headers(),
                     canonicalize=(header_algo, body_algo))
@@ -202,10 +221,41 @@ b/mPfjC0QJTocVBq6Za/PlzfV+Py92VaCak19F4WrbVTK5Gg5tW220MCAwEAAQ=="""
         identity = None
         try:
             sig = dkim.sign(message, selector, domain, read_test_data('test.private'), identity = identity)
-        except dkim.ParameterError as sigerror:
-            pass
+        except dkim.ParameterError as x:
+            sigerror = True
         self.assertTrue(sigerror)
 
+    def test_validate_signature_fields(self):
+      sig = {b'v': b'1',
+      b'a': b'rsa-sha256',
+      b'b': b'K/UUOt8lCtgjp3kSTogqBm9lY1Yax/NwZ+bKm39/WKzo5KYe3L/6RoIA/0oiDX4kO\n \t Qut49HCV6ZUe6dY9V5qWBwLanRs1sCnObaOGMpFfs8tU4TWpDSVXaNZAqn15XVW0WH\n \t EzOzUfVuatpa1kF4voIgSbmZHR1vN3WpRtcTBe/I=',
+      b'bh': b'n0HUwGCP28PkesXBPH82Kboy8LhNFWU9zUISIpAez7M=',
+      b'c': b'simple/simple',
+      b'd': b'kitterman.com',
+      b'i': b'scott at Kitterman.com',
+      b'h': b'From:To:Subject:Date:Cc:MIME-Version:Content-Type:\n \t Content-Transfer-Encoding:Message-Id',
+      b's': b'2007-00',
+      b't': b'1299525798'}
+      dkim.validate_signature_fields(sig)
+      # try new version
+      sigVer = sig.copy()
+      sigVer[b'v'] = 2
+      self.assertRaises(dkim.ValidationError, dkim.validate_signature_fields, sigVer)
+      # try with x
+      sigX = sig.copy()
+      sigX[b'x'] = b'1399525798'
+      dkim.validate_signature_fields(sig)
+      # try with late t
+      sigX[b't'] = b'1400000000'
+      self.assertRaises(dkim.ValidationError, dkim.validate_signature_fields, sigX)
+      # try without t
+      now = int(time.time())
+      sigX[b'x'] = str(now+400000).encode('ascii')
+      dkim.validate_signature_fields(sigX)
+      # try when expired a day ago
+      sigX[b'x'] = str(now - 24*3600).encode('ascii')
+      self.assertRaises(dkim.ValidationError, dkim.validate_signature_fields, sigX)
+
 def test_suite():
     from unittest import TestLoader
     return TestLoader().loadTestsFromName(__name__)
diff --git a/dkim/tests/test_util.py b/dkim/tests/test_util.py
index 85e8106..2da48ea 100644
--- a/dkim/tests/test_util.py
+++ b/dkim/tests/test_util.py
@@ -62,7 +62,7 @@ class TestParseTagValue(unittest.TestCase):
             DuplicateTag, parse_tag_value, b'foo=bar;foo=baz')
 
     def test_trailing_whitespace(self):
-      hval = '''v=1; a=rsa-sha256; d=facebookmail.com; s=s1024-2011-q2; c=relaxed/simple;
+      hval = b'''v=1; a=rsa-sha256; d=facebookmail.com; s=s1024-2011-q2; c=relaxed/simple;
           q=dns/txt; i=@facebookmail.com; t=1308078492;
           h=From:Subject:Date:To:MIME-Version:Content-Type;
           bh=+qPyCOiDQkusTPstCoGjimgDgeZbUaJWIr1mdE6RFxk=;
@@ -71,7 +71,7 @@ class TestParseTagValue(unittest.TestCase):
           3KzW0yB9JHwiDCw1EioVkv+OMHhAYzoIypA0bQyi2bc=;
   '''
       sig = parse_tag_value(hval)
-      self.assertEquals(sig[b't'],'1308078492')
+      self.assertEquals(sig[b't'],b'1308078492')
       self.assertEquals(len(sig),11)
 
 
diff --git a/dkim/util.py b/dkim/util.py
index 0be290b..5332127 100644
--- a/dkim/util.py
+++ b/dkim/util.py
@@ -52,7 +52,7 @@ def parse_tag_value(tag_list):
     Interprets the syntax specified by RFC4871 section 3.2.
     Assumes that folding whitespace is already unfolded.
 
-    @param tag_list: A string containing a DKIM Tag=Value list.
+    @param tag_list: A bytes string containing a DKIM Tag=Value list.
     """
     tags = {}
     tag_specs = tag_list.strip().split(b';')
diff --git a/setup.py b/setup.py
index e4a5787..156a545 100644
--- a/setup.py
+++ b/setup.py
@@ -24,7 +24,7 @@
 from distutils.core import setup
 import os
 
-version = "0.5.4"
+version = "0.5.5"
 
 setup(
     name = "dkimpy",

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/dkimpy.git



More information about the Python-modules-commits mailing list