[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