Merge ~spitap/dkimpy:utf8-support into dkimpy:master
- Git
- lp:~spitap/dkimpy
- utf8-support
- Merge into master
Proposed by
Adrien
Status: | Needs review |
---|---|
Proposed branch: | ~spitap/dkimpy:utf8-support |
Merge into: | dkimpy:master |
Diff against target: |
533 lines (+157/-68) 13 files modified
.gitignore (+1/-0) dkim/asyncsupport.py (+9/-5) dkim/dkimverify.py (+34/-4) dkim/dnsplug.py (+2/-3) dkim/tests/__init__.py (+9/-0) dkim/tests/data/test_punnycode.message (+9/-0) dkim/tests/data/test_utf8.message (+9/-0) dkim/tests/test_arc.py (+3/-4) dkim/tests/test_dkim.py (+57/-22) dkim/tests/test_dkim_ed25519.py (+3/-3) dkim/tests/test_dkim_generate.py (+5/-6) dkim/tests/test_dkim_rsavariants.py (+3/-3) dkim/tests/test_dkim_tlsrpt.py (+13/-18) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
dkimpy developers | Pending | ||
Review via email: mp+427826@code.launchpad.net |
Commit message
Added UTF-8 support && ability to check message from CLI
Description of the change
To post a comment you must log in.
Unmerged commits
- 9813d5b... by Adrien
-
UT8-Support
- b446e70... by Scott Kitterman
-
WIP to support DNS data via text file and support for non-UTF-8 email addresses
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/.gitignore b/.gitignore | |||
2 | index debba34..589aff8 100644 | |||
3 | --- a/.gitignore | |||
4 | +++ b/.gitignore | |||
5 | @@ -4,3 +4,4 @@ dkim/*.pyc | |||
6 | 4 | dkim/tests/*.pyc | 4 | dkim/tests/*.pyc |
7 | 5 | dkimpy.egg-info | 5 | dkimpy.egg-info |
8 | 6 | dist | 6 | dist |
9 | 7 | build/ | ||
10 | diff --git a/dkim/asyncsupport.py b/dkim/asyncsupport.py | |||
11 | index 226cc42..e326827 100644 | |||
12 | --- a/dkim/asyncsupport.py | |||
13 | +++ b/dkim/asyncsupport.py | |||
14 | @@ -50,13 +50,17 @@ async def get_txt_async(name, timeout=5): | |||
15 | 50 | # Note: This will use the existing loop or create one if needed | 50 | # Note: This will use the existing loop or create one if needed |
16 | 51 | loop = asyncio.get_event_loop() | 51 | loop = asyncio.get_event_loop() |
17 | 52 | resolver = aiodns.DNSResolver(loop=loop, timeout=timeout) | 52 | resolver = aiodns.DNSResolver(loop=loop, timeout=timeout) |
21 | 53 | 53 | ||
22 | 54 | async def query(name, qtype): | 54 | try: |
23 | 55 | return await resolver.query(name, qtype) | 55 | unicode_name = name.decode('utf-8') |
24 | 56 | except UnicodeDecodeError: | ||
25 | 57 | return None | ||
26 | 58 | async def query(unicode_name, qtype): | ||
27 | 59 | return await resolver.query(unicode_name, qtype) | ||
28 | 56 | 60 | ||
29 | 57 | #q = query(name, 'TXT') | 61 | #q = query(name, 'TXT') |
30 | 58 | try: | 62 | try: |
32 | 59 | result = await query(name, 'TXT') | 63 | result = await query(unicode_name, 'TXT') |
33 | 60 | except aiodns.error.DNSError: | 64 | except aiodns.error.DNSError: |
34 | 61 | result = None | 65 | result = None |
35 | 62 | 66 | ||
36 | @@ -86,7 +90,7 @@ class DKIM(dkim.DKIM): | |||
37 | 86 | name = sig[b's'] + b"._domainkey." + sig[b'd'] + b"." | 90 | name = sig[b's'] + b"._domainkey." + sig[b'd'] + b"." |
38 | 87 | try: | 91 | try: |
39 | 88 | self.pk, self.keysize, self.ktag, self.seqtlsrpt = await load_pk_from_dns_async(name, | 92 | self.pk, self.keysize, self.ktag, self.seqtlsrpt = await load_pk_from_dns_async(name, |
41 | 89 | dnsfunc, timeout=self.timeout) | 93 | dnsfunc, timeout=self.timeout) |
42 | 90 | except dkim.KeyFormatError as e: | 94 | except dkim.KeyFormatError as e: |
43 | 91 | self.logger.error("%s" % e) | 95 | self.logger.error("%s" % e) |
44 | 92 | return False | 96 | return False |
45 | diff --git a/dkim/dkimverify.py b/dkim/dkimverify.py | |||
46 | index 2ca90d5..e1d6243 100644 | |||
47 | --- a/dkim/dkimverify.py | |||
48 | +++ b/dkim/dkimverify.py | |||
49 | @@ -28,24 +28,54 @@ import argparse | |||
50 | 28 | 28 | ||
51 | 29 | import dkim | 29 | import dkim |
52 | 30 | 30 | ||
53 | 31 | def filedns(datafile, tgtdomain, selector): | ||
54 | 32 | |||
55 | 33 | def dnsf(self, domain, timeout=5): | ||
56 | 34 | target = "{0}._domainkey.{1}.".format(selector, tgtdomain) | ||
57 | 35 | _dns_responses = { | ||
58 | 36 | target : read_test_data(datafile), | ||
59 | 37 | } | ||
60 | 38 | try: | ||
61 | 39 | domain = domain.decode('ascii') | ||
62 | 40 | except UnicodeDecodeError: | ||
63 | 41 | return None | ||
64 | 42 | self.assertTrue(domain in _dns_responses,domain) | ||
65 | 43 | return _dns_responses[domain] | ||
66 | 44 | |||
67 | 45 | return dnsf | ||
68 | 46 | |||
69 | 31 | def main(): | 47 | def main(): |
70 | 32 | parser = argparse.ArgumentParser( | 48 | parser = argparse.ArgumentParser( |
71 | 33 | description='Verify DKIM signature for email messages.', | 49 | description='Verify DKIM signature for email messages.', |
72 | 34 | epilog="message to be verified follows commands on stdin") | 50 | epilog="message to be verified follows commands on stdin") |
73 | 51 | parser.add_argument('-v', '--verbose', action='store_true', default=False, | ||
74 | 52 | help='turn verbose mode on') | ||
75 | 35 | parser.add_argument('--index', metavar='N', type=int, default=0, | 53 | parser.add_argument('--index', metavar='N', type=int, default=0, |
76 | 36 | help='Index of DKIM signature header to verify: default=0') | 54 | help='Index of DKIM signature header to verify: default=0') |
77 | 55 | parser.add_argument('-f', '--dnsfile', action="store", default=None, | ||
78 | 56 | help='File containing DKIM public key records' ) | ||
79 | 57 | parser.add_argument('-d', '--domain', action="store", default=None, | ||
80 | 58 | help='Domain for DNS record in dnsfile. Mandatory with -f.' ) | ||
81 | 59 | parser.add_argument('-s', '--selector', action="store", default=None, | ||
82 | 60 | help='Selector for DNS record in dnsfile. Mandatory with -f.' ) | ||
83 | 37 | args=parser.parse_args() | 61 | args=parser.parse_args() |
84 | 38 | if sys.version_info[0] >= 3: | 62 | if sys.version_info[0] >= 3: |
85 | 39 | # Make sys.stdin a binary stream. | 63 | # Make sys.stdin a binary stream. |
86 | 40 | sys.stdin = sys.stdin.detach() | 64 | sys.stdin = sys.stdin.detach() |
87 | 41 | 65 | ||
88 | 42 | message = sys.stdin.read() | 66 | message = sys.stdin.read() |
91 | 43 | verbose = '-v' in sys.argv | 67 | if args.verbose: |
90 | 44 | if verbose: | ||
92 | 45 | import logging | 68 | import logging |
94 | 46 | d = dkim.DKIM(message, logger=logging) | 69 | log=logging |
95 | 70 | else: | ||
96 | 71 | log=None | ||
97 | 72 | if args.dnsfile: | ||
98 | 73 | if not args.domain or not args.selector: | ||
99 | 74 | raise SyntaxError('Both --domain and --selector are required with --dnsfile') | ||
100 | 75 | dnsfc=filedns(args.dnsfile, args.domain, args.selector) | ||
101 | 76 | d = dkim.DKIM(message, logger=log, dnsfunc=dnsfc) | ||
102 | 47 | else: | 77 | else: |
104 | 48 | d = dkim.DKIM(message) | 78 | d = dkim.DKIM(message, logger=log) |
105 | 49 | res = d.verify(args.index) | 79 | res = d.verify(args.index) |
106 | 50 | if not res: | 80 | if not res: |
107 | 51 | print("signature verification failed") | 81 | print("signature verification failed") |
108 | diff --git a/dkim/dnsplug.py b/dkim/dnsplug.py | |||
109 | index 5ccf97c..4c75d34 100644 | |||
110 | --- a/dkim/dnsplug.py | |||
111 | +++ b/dkim/dnsplug.py | |||
112 | @@ -24,7 +24,6 @@ __all__ = [ | |||
113 | 24 | 'get_txt' | 24 | 'get_txt' |
114 | 25 | ] | 25 | ] |
115 | 26 | 26 | ||
116 | 27 | |||
117 | 28 | def get_txt_dnspython(name, timeout=5): | 27 | def get_txt_dnspython(name, timeout=5): |
118 | 29 | """Return a TXT record associated with a DNS name.""" | 28 | """Return a TXT record associated with a DNS name.""" |
119 | 30 | try: | 29 | try: |
120 | @@ -78,11 +77,11 @@ except ImportError: | |||
121 | 78 | def get_txt(name, timeout=5): | 77 | def get_txt(name, timeout=5): |
122 | 79 | """Return a TXT record associated with a DNS name. | 78 | """Return a TXT record associated with a DNS name. |
123 | 80 | 79 | ||
125 | 81 | @param name: The bytestring domain name to look up. | 80 | @param name: The string domain name to look up. |
126 | 82 | """ | 81 | """ |
127 | 83 | # pydns needs Unicode, but DKIM's d= is ASCII (already punycoded). | 82 | # pydns needs Unicode, but DKIM's d= is ASCII (already punycoded). |
128 | 84 | try: | 83 | try: |
130 | 85 | unicode_name = name.decode('UTF-8') | 84 | unicode_name = name.decode('utf-8') |
131 | 86 | except UnicodeDecodeError: | 85 | except UnicodeDecodeError: |
132 | 87 | return None | 86 | return None |
133 | 88 | txt = _get_txt(unicode_name, timeout) | 87 | txt = _get_txt(unicode_name, timeout) |
134 | diff --git a/dkim/tests/__init__.py b/dkim/tests/__init__.py | |||
135 | index 4fb2926..50fe90f 100644 | |||
136 | --- a/dkim/tests/__init__.py | |||
137 | +++ b/dkim/tests/__init__.py | |||
138 | @@ -24,6 +24,15 @@ | |||
139 | 24 | import unittest | 24 | import unittest |
140 | 25 | 25 | ||
141 | 26 | 26 | ||
142 | 27 | def utf8_decode(data): | ||
143 | 28 | """To decode utf-8 bytestring""" | ||
144 | 29 | try: | ||
145 | 30 | data = data.decode('utf-8') | ||
146 | 31 | except UnicodeDecodeError: | ||
147 | 32 | return None | ||
148 | 33 | return data | ||
149 | 34 | |||
150 | 35 | |||
151 | 27 | def test_suite(): | 36 | def test_suite(): |
152 | 28 | from dkim.tests import ( | 37 | from dkim.tests import ( |
153 | 29 | test_canonicalization, | 38 | test_canonicalization, |
154 | diff --git a/dkim/tests/data/test_punnycode.message b/dkim/tests/data/test_punnycode.message | |||
155 | 30 | new file mode 100644 | 39 | new file mode 100644 |
156 | index 0000000..bece5cc | |||
157 | --- /dev/null | |||
158 | +++ b/dkim/tests/data/test_punnycode.message | |||
159 | @@ -0,0 +1,9 @@ | |||
160 | 1 | Authentication-Results: lists.example.org; arc=none; spf=pass smtp.mfrom=jqd@d1.example; dkim=pass (1024-bit key) header.i=@d1.example; dmarc=pass | ||
161 | 2 | Received: from localhost | ||
162 | 3 | Message-ID: <example@example.com> | ||
163 | 4 | Date: Mon, 01 Jan 2011 01:02:03 +0400 | ||
164 | 5 | From: UTF8 user <test@xn----ylba7abgd9bnh0e.xn--qxa6a> | ||
165 | 6 | To: somebody@example.com | ||
166 | 7 | Subject: Testing | ||
167 | 8 | |||
168 | 9 | This is a test message. | ||
169 | diff --git a/dkim/tests/data/test_utf8.message b/dkim/tests/data/test_utf8.message | |||
170 | 0 | new file mode 100644 | 10 | new file mode 100644 |
171 | index 0000000..80b6543 | |||
172 | --- /dev/null | |||
173 | +++ b/dkim/tests/data/test_utf8.message | |||
174 | @@ -0,0 +1,9 @@ | |||
175 | 1 | Authentication-Results: lists.example.org; arc=none; spf=pass smtp.mfrom=jqd@d1.example; dkim=pass (1024-bit key) header.i=@d1.example; dmarc=pass | ||
176 | 2 | Received: from localhost | ||
177 | 3 | Message-ID: <example@example.com> | ||
178 | 4 | Date: Mon, 01 Jan 2011 01:02:03 +0400 | ||
179 | 5 | From: UTF8 user <test@κλαρα-σωλις.ευ> | ||
180 | 6 | To: somebody@example.com | ||
181 | 7 | Subject: Testing | ||
182 | 8 | |||
183 | 9 | This is a test message. | ||
184 | diff --git a/dkim/tests/test_arc.py b/dkim/tests/test_arc.py | |||
185 | index a65c7c3..c07b9ce 100644 | |||
186 | --- a/dkim/tests/test_arc.py | |||
187 | +++ b/dkim/tests/test_arc.py | |||
188 | @@ -23,7 +23,7 @@ | |||
189 | 23 | import os.path | 23 | import os.path |
190 | 24 | import unittest | 24 | import unittest |
191 | 25 | import time | 25 | import time |
193 | 26 | 26 | from . import utf8_decode | |
194 | 27 | import dkim | 27 | import dkim |
195 | 28 | 28 | ||
196 | 29 | 29 | ||
197 | @@ -61,9 +61,8 @@ hzY8i+RQ9DpSVpPbF7ykQxtKXkv/ahW3KjViiAH+ghvvIhkx4xYSIc9oSwVmAl5Oct\ | |||
198 | 61 | MEeWUwg8Istjqz8BZeTWbf41fbNhte7Y+YqZOwq1Sd0DbvYAD9NOZK9vlfuac0598H\ | 61 | MEeWUwg8Istjqz8BZeTWbf41fbNhte7Y+YqZOwq1Sd0DbvYAD9NOZK9vlfuac0598H\ |
199 | 62 | Y+vtSBczUiKERHv1yRbcaQtZFh5wtiRrN04BLUTD21MycBX5jYchHjPY/wIDAQAB""" | 62 | Y+vtSBczUiKERHv1yRbcaQtZFh5wtiRrN04BLUTD21MycBX5jYchHjPY/wIDAQAB""" |
200 | 63 | } | 63 | } |
204 | 64 | try: | 64 | domain = utf8_decode(domain) |
205 | 65 | domain = domain.decode('ascii') | 65 | if not domain: |
203 | 66 | except UnicodeDecodeError: | ||
206 | 67 | return None | 66 | return None |
207 | 68 | self.assertTrue(domain in _dns_responses,domain) | 67 | self.assertTrue(domain in _dns_responses,domain) |
208 | 69 | return _dns_responses[domain] | 68 | return _dns_responses[domain] |
209 | diff --git a/dkim/tests/test_dkim.py b/dkim/tests/test_dkim.py | |||
210 | index dc589d6..3ce5379 100644 | |||
211 | --- a/dkim/tests/test_dkim.py | |||
212 | +++ b/dkim/tests/test_dkim.py | |||
213 | @@ -22,7 +22,7 @@ import unittest | |||
214 | 22 | import time | 22 | import time |
215 | 23 | 23 | ||
216 | 24 | import dkim | 24 | import dkim |
218 | 25 | 25 | from . import utf8_decode | |
219 | 26 | 26 | ||
220 | 27 | def read_test_data(filename): | 27 | def read_test_data(filename): |
221 | 28 | """Get the content of the given test data file. | 28 | """Get the content of the given test data file. |
222 | @@ -61,6 +61,8 @@ class TestSignAndVerify(unittest.TestCase): | |||
223 | 61 | self.message4 = read_test_data("rfc6376.signed.msg") | 61 | self.message4 = read_test_data("rfc6376.signed.msg") |
224 | 62 | self.message5 = read_test_data("rfc6376.signed.rsa.msg") | 62 | self.message5 = read_test_data("rfc6376.signed.rsa.msg") |
225 | 63 | self.message6 = read_test_data("test.message.baddomain") | 63 | self.message6 = read_test_data("test.message.baddomain") |
226 | 64 | self.messageutf8 = read_test_data("test_utf8.message") | ||
227 | 65 | self.messagepunycode = read_test_data("test_punnycode.message") | ||
228 | 64 | self.key = read_test_data("test.private") | 66 | self.key = read_test_data("test.private") |
229 | 65 | self.rfckey = read_test_data("rfc8032_7_1.key") | 67 | self.rfckey = read_test_data("rfc8032_7_1.key") |
230 | 66 | 68 | ||
231 | @@ -81,9 +83,8 @@ hzY8i+RQ9DpSVpPbF7ykQxtKXkv/ahW3KjViiAH+ghvvIhkx4xYSIc9oSwVmAl5Oct\ | |||
232 | 81 | MEeWUwg8Istjqz8BZeTWbf41fbNhte7Y+YqZOwq1Sd0DbvYAD9NOZK9vlfuac0598H\ | 83 | MEeWUwg8Istjqz8BZeTWbf41fbNhte7Y+YqZOwq1Sd0DbvYAD9NOZK9vlfuac0598H\ |
233 | 82 | Y+vtSBczUiKERHv1yRbcaQtZFh5wtiRrN04BLUTD21MycBX5jYchHjPY/wIDAQAB""" | 84 | Y+vtSBczUiKERHv1yRbcaQtZFh5wtiRrN04BLUTD21MycBX5jYchHjPY/wIDAQAB""" |
234 | 83 | } | 85 | } |
238 | 84 | try: | 86 | domain = utf8_decode(domain) |
239 | 85 | domain = domain.decode('ascii') | 87 | if not domain: |
237 | 86 | except UnicodeDecodeError: | ||
240 | 87 | return None | 88 | return None |
241 | 88 | self.assertTrue(domain in _dns_responses,domain) | 89 | self.assertTrue(domain in _dns_responses,domain) |
242 | 89 | return _dns_responses[domain] | 90 | return _dns_responses[domain] |
243 | @@ -105,9 +106,8 @@ hzY8i+RQ9DpSVpPbF7ykQxtKXkv/ahW3KjViiAH+ghvvIhkx4xYSIc9oSwVmAl5Oct\ | |||
244 | 105 | MEeWUwg8Istjqz8BZeTWbf41fbNhte7Y+YqZOwq1Sd0DbvYAD9NOZK9vlfuac0598H\ | 106 | MEeWUwg8Istjqz8BZeTWbf41fbNhte7Y+YqZOwq1Sd0DbvYAD9NOZK9vlfuac0598H\ |
245 | 106 | Y+vtSBczUiKERHv1yRbcaQtZFh5wtiRrN04BLUTD21MycBX5jYchHjPY/wIDAQAB""" | 107 | Y+vtSBczUiKERHv1yRbcaQtZFh5wtiRrN04BLUTD21MycBX5jYchHjPY/wIDAQAB""" |
246 | 107 | } | 108 | } |
250 | 108 | try: | 109 | domain = utf8_decode(domain) |
251 | 109 | domain = domain.decode('ascii') | 110 | if not domain: |
249 | 110 | except UnicodeDecodeError: | ||
252 | 111 | return None | 111 | return None |
253 | 112 | self.assertTrue(domain in _dns_responses,domain) | 112 | self.assertTrue(domain in _dns_responses,domain) |
254 | 113 | return _dns_responses[domain] | 113 | return _dns_responses[domain] |
255 | @@ -129,9 +129,8 @@ hzY8i+RQ9DpSVpPbF7ykQxtKXkv/ahW3KjViiAH+ghvvIhkx4xYSIc9oSwVmAl5Oct\ | |||
256 | 129 | MEeWUwg8Istjqz8BZeTWbf41fbNhte7Y+YqZOwq1Sd0DbvYAD9NOZK9vlfuac0598H\ | 129 | MEeWUwg8Istjqz8BZeTWbf41fbNhte7Y+YqZOwq1Sd0DbvYAD9NOZK9vlfuac0598H\ |
257 | 130 | Y+vtSBczUiKERHv1yRbcaQtZFh5wtiRrN04BLUTD21MycBX5jYchHjPY/wIDAQAB""" | 130 | Y+vtSBczUiKERHv1yRbcaQtZFh5wtiRrN04BLUTD21MycBX5jYchHjPY/wIDAQAB""" |
258 | 131 | } | 131 | } |
262 | 132 | try: | 132 | domain = utf8_decode(domain) |
263 | 133 | domain = domain.decode('ascii') | 133 | if not domain: |
261 | 134 | except UnicodeDecodeError: | ||
264 | 135 | return None | 134 | return None |
265 | 136 | self.assertTrue(domain in _dns_responses,domain) | 135 | self.assertTrue(domain in _dns_responses,domain) |
266 | 137 | return _dns_responses[domain] | 136 | return _dns_responses[domain] |
267 | @@ -153,9 +152,8 @@ hzY8i+RQ9DpSVpPbF7ykQxtKXkv/ahW3KjViiAH+ghvvIhkx4xYSIc9oSwVmAl5Oct\ | |||
268 | 153 | MEeWUwg8Istjqz8BZeTWbf41fbNhte7Y+YqZOwq1Sd0DbvYAD9NOZK9vlfuac0598H\ | 152 | MEeWUwg8Istjqz8BZeTWbf41fbNhte7Y+YqZOwq1Sd0DbvYAD9NOZK9vlfuac0598H\ |
269 | 154 | Y+vtSBczUiKERHv1yRbcaQtZFh5wtiRrN04BLUTD21MycBX5jYchHjPY/wIDAQAB""" | 153 | Y+vtSBczUiKERHv1yRbcaQtZFh5wtiRrN04BLUTD21MycBX5jYchHjPY/wIDAQAB""" |
270 | 155 | } | 154 | } |
274 | 156 | try: | 155 | domain = utf8_decode(domain) |
275 | 157 | domain = domain.decode('ascii') | 156 | if not domain: |
273 | 158 | except UnicodeDecodeError: | ||
276 | 159 | return None | 157 | return None |
277 | 160 | self.assertTrue(domain in _dns_responses,domain) | 158 | self.assertTrue(domain in _dns_responses,domain) |
278 | 161 | return _dns_responses[domain] | 159 | return _dns_responses[domain] |
279 | @@ -172,9 +170,8 @@ b/mPfjC0QJTocVBq6Za/PlzfV+Py92VaCak19F4WrbVTK5Gg5tW220MCAwEAAQ==""" | |||
280 | 172 | 'brisbane._domainkey.football.example.com.': """v=DKIM1; k=ed25519; \ | 170 | 'brisbane._domainkey.football.example.com.': """v=DKIM1; k=ed25519; \ |
281 | 173 | p=11qYAYKxCrfVS/7TyWQHOg7hcvPapiMlrwIaaPcHURo=""" | 171 | p=11qYAYKxCrfVS/7TyWQHOg7hcvPapiMlrwIaaPcHURo=""" |
282 | 174 | } | 172 | } |
286 | 175 | try: | 173 | domain = utf8_decode(domain) |
287 | 176 | domain = domain.decode('ascii') | 174 | if not domain: |
285 | 177 | except UnicodeDecodeError: | ||
288 | 178 | return None | 175 | return None |
289 | 179 | self.assertTrue(domain in _dns_responses,domain) | 176 | self.assertTrue(domain in _dns_responses,domain) |
290 | 180 | return _dns_responses[domain] | 177 | return _dns_responses[domain] |
291 | @@ -190,9 +187,8 @@ b/mPfjC0QJTocVBq6Za/PlzfV+Py92VaCak19F4WrbVTK5Gg5tW220MCAwEAAQ==""" | |||
292 | 190 | 'brisbane._domainkey.football.example.com.': """v=DKIM1; k=ed25519; \ | 187 | 'brisbane._domainkey.football.example.com.': """v=DKIM1; k=ed25519; \ |
293 | 191 | p=11qYAYKxCrfVS/7TyWQHOg7hcvPapiMlrwIaaPcHURo=""" | 188 | p=11qYAYKxCrfVS/7TyWQHOg7hcvPapiMlrwIaaPcHURo=""" |
294 | 192 | } | 189 | } |
298 | 193 | try: | 190 | domain = utf8_decode(domain) |
299 | 194 | domain = domain.decode('ascii') | 191 | if not domain: |
297 | 195 | except UnicodeDecodeError: | ||
300 | 196 | return None | 192 | return None |
301 | 197 | self.assertTrue(domain in _dns_responses,domain) | 193 | self.assertTrue(domain in _dns_responses,domain) |
302 | 198 | return _dns_responses[domain] | 194 | return _dns_responses[domain] |
303 | @@ -206,13 +202,32 @@ b/mPfjC0QJTocVBq6Za/PlzfV+Py92VaCak19F4WrbVTK5Gg5tW220MCAwEAAQ==""" | |||
304 | 206 | _dns_responses = { | 202 | _dns_responses = { |
305 | 207 | 'test._domainkey.legitimate.com(.attacker.com.': read_test_data("test.txt"), | 203 | 'test._domainkey.legitimate.com(.attacker.com.': read_test_data("test.txt"), |
306 | 208 | } | 204 | } |
310 | 209 | try: | 205 | domain = utf8_decode(domain) |
311 | 210 | domain = domain.decode('ascii') | 206 | if not domain: |
309 | 211 | except UnicodeDecodeError: | ||
312 | 212 | return None | 207 | return None |
313 | 213 | self.assertTrue(domain in _dns_responses,domain) | 208 | self.assertTrue(domain in _dns_responses,domain) |
314 | 214 | return _dns_responses[domain] | 209 | return _dns_responses[domain] |
315 | 215 | 210 | ||
316 | 211 | def dnsfuncutf8(self, domain, timeout=5): | ||
317 | 212 | _dns_responses = { | ||
318 | 213 | '2022._domainkey.κλαρα-σωλις.ευ.': read_test_data("2048_testkey_PKCS8.key.pub.txt"), | ||
319 | 214 | 'test._domainkey.legitimate.com(.attacker.com.': read_test_data("test.txt"), | ||
320 | 215 | } | ||
321 | 216 | domain = utf8_decode(domain) | ||
322 | 217 | if not domain: | ||
323 | 218 | return None | ||
324 | 219 | self.assertTrue(domain in _dns_responses) | ||
325 | 220 | return _dns_responses[domain] | ||
326 | 221 | |||
327 | 222 | def dnsfuncpunycode(self, domain, timeout=5): | ||
328 | 223 | _dns_response = { | ||
329 | 224 | '2022._domainkey.xn----ylba7abgd9bnh0e.xn--qxa6a.' : read_test_data("2048_testkey_PKCS8.key.pub.txt") | ||
330 | 225 | } | ||
331 | 226 | domain = utf8_decode(domain) | ||
332 | 227 | if not domain: | ||
333 | 228 | return None | ||
334 | 229 | self.assertTrue(domain in _dns_response) | ||
335 | 230 | return _dns_response[domain] | ||
336 | 216 | 231 | ||
337 | 217 | def test_verifies(self): | 232 | def test_verifies(self): |
338 | 218 | # A message verifies after being signed. | 233 | # A message verifies after being signed. |
339 | @@ -499,6 +514,26 @@ b/mPfjC0QJTocVBq6Za/PlzfV+Py92VaCak19F4WrbVTK5Gg5tW220MCAwEAAQ== | |||
340 | 499 | sigerror = True | 514 | sigerror = True |
341 | 500 | self.assertTrue(sigerror) | 515 | self.assertTrue(sigerror) |
342 | 501 | 516 | ||
343 | 517 | def test_utf_8_verifies(self): | ||
344 | 518 | # Attempt to verify message with UTF-8 domain. | ||
345 | 519 | key = read_test_data("2048_testkey_PKCS8.key") | ||
346 | 520 | for header_algo in (b"simple", b"relaxed"): | ||
347 | 521 | for body_algo in (b"simple", b"relaxed"): | ||
348 | 522 | sig = dkim.sign(self.messageutf8, b'2022', "κλαρα-σωλις.ευ".encode('utf-8'), key, | ||
349 | 523 | canonicalize=(header_algo, body_algo)) | ||
350 | 524 | res = dkim.verify(sig + self.messageutf8, dnsfunc=self.dnsfuncutf8) | ||
351 | 525 | self.assertTrue(res) | ||
352 | 526 | |||
353 | 527 | def test_punnycode_verifies(self): | ||
354 | 528 | # Attempt to verify message with UTF-8 domain. | ||
355 | 529 | key = read_test_data("2048_testkey_PKCS8.key") | ||
356 | 530 | for header_algo in (b"simple", b"relaxed"): | ||
357 | 531 | for body_algo in (b"simple", b"relaxed"): | ||
358 | 532 | sig = dkim.sign(self.messagepunycode, b'2022', "xn----ylba7abgd9bnh0e.xn--qxa6a".encode('utf-8'), key, | ||
359 | 533 | canonicalize=(header_algo, body_algo)) | ||
360 | 534 | res = dkim.verify(sig + self.messagepunycode, dnsfunc=self.dnsfuncpunycode) | ||
361 | 535 | self.assertTrue(res) | ||
362 | 536 | |||
363 | 502 | def test_validate_signature_fields(self): | 537 | def test_validate_signature_fields(self): |
364 | 503 | sig = {b'v': b'1', | 538 | sig = {b'v': b'1', |
365 | 504 | b'a': b'rsa-sha256', | 539 | b'a': b'rsa-sha256', |
366 | diff --git a/dkim/tests/test_dkim_ed25519.py b/dkim/tests/test_dkim_ed25519.py | |||
367 | index 5cf89e4..adeea99 100644 | |||
368 | --- a/dkim/tests/test_dkim_ed25519.py | |||
369 | +++ b/dkim/tests/test_dkim_ed25519.py | |||
370 | @@ -22,6 +22,7 @@ import os.path | |||
371 | 22 | import unittest | 22 | import unittest |
372 | 23 | import time | 23 | import time |
373 | 24 | 24 | ||
374 | 25 | from . import utf8_decode | ||
375 | 25 | import dkim | 26 | import dkim |
376 | 26 | 27 | ||
377 | 27 | 28 | ||
378 | @@ -72,9 +73,8 @@ p=yi50DjK5O9pqbFpNHklsv9lqaS0ArSYu02qp1S0DW1Y=""", | |||
379 | 72 | 'brisbane._domainkey.football.example.com.': """v=DKIM1; k=ed25519; \ | 73 | 'brisbane._domainkey.football.example.com.': """v=DKIM1; k=ed25519; \ |
380 | 73 | p=11qYAYKxCrfVS/7TyWQHOg7hcvPapiMlrwIaaPcHURo=""" | 74 | p=11qYAYKxCrfVS/7TyWQHOg7hcvPapiMlrwIaaPcHURo=""" |
381 | 74 | } | 75 | } |
385 | 75 | try: | 76 | domain = utf8_decode(domain) |
386 | 76 | domain = domain.decode('ascii') | 77 | if not domain: |
384 | 77 | except UnicodeDecodeError: | ||
387 | 78 | return None | 78 | return None |
388 | 79 | self.assertTrue(domain in _dns_responses,domain) | 79 | self.assertTrue(domain in _dns_responses,domain) |
389 | 80 | return _dns_responses[domain] | 80 | return _dns_responses[domain] |
390 | diff --git a/dkim/tests/test_dkim_generate.py b/dkim/tests/test_dkim_generate.py | |||
391 | index 1649ab5..7063c5b 100644 | |||
392 | --- a/dkim/tests/test_dkim_generate.py | |||
393 | +++ b/dkim/tests/test_dkim_generate.py | |||
394 | @@ -23,6 +23,7 @@ import unittest | |||
395 | 23 | 23 | ||
396 | 24 | import dkim | 24 | import dkim |
397 | 25 | import dknewkey | 25 | import dknewkey |
398 | 26 | from . import utf8_decode | ||
399 | 26 | 27 | ||
400 | 27 | def read_data(path): | 28 | def read_data(path): |
401 | 28 | """Get the content of the given test data file.""" | 29 | """Get the content of the given test data file.""" |
402 | @@ -88,9 +89,8 @@ class TestSignAndVerify(unittest.TestCase): | |||
403 | 88 | _dns_responses = { | 89 | _dns_responses = { |
404 | 89 | 'test._domainkey.example.com.': read_data(self.rsa_dns_key_file), | 90 | 'test._domainkey.example.com.': read_data(self.rsa_dns_key_file), |
405 | 90 | } | 91 | } |
409 | 91 | try: | 92 | domain = utf8_decode(domain) |
410 | 92 | domain = domain.decode('ascii') | 93 | if not domain: |
408 | 93 | except UnicodeDecodeError: | ||
411 | 94 | return None | 94 | return None |
412 | 95 | self.assertTrue(domain in _dns_responses,domain) | 95 | self.assertTrue(domain in _dns_responses,domain) |
413 | 96 | return _dns_responses[domain] | 96 | return _dns_responses[domain] |
414 | @@ -99,9 +99,8 @@ class TestSignAndVerify(unittest.TestCase): | |||
415 | 99 | _dns_responses = { | 99 | _dns_responses = { |
416 | 100 | 'test1._domainkey.example.com.': read_data(self.ed25519_dns_key_file), | 100 | 'test1._domainkey.example.com.': read_data(self.ed25519_dns_key_file), |
417 | 101 | } | 101 | } |
421 | 102 | try: | 102 | domain = utf8_decode(domain) |
422 | 103 | domain = domain.decode('ascii') | 103 | if not domain: |
420 | 104 | except UnicodeDecodeError: | ||
423 | 105 | return None | 104 | return None |
424 | 106 | self.assertTrue(domain in _dns_responses,domain) | 105 | self.assertTrue(domain in _dns_responses,domain) |
425 | 107 | return _dns_responses[domain] | 106 | return _dns_responses[domain] |
426 | diff --git a/dkim/tests/test_dkim_rsavariants.py b/dkim/tests/test_dkim_rsavariants.py | |||
427 | index 992d466..a230b3a 100644 | |||
428 | --- a/dkim/tests/test_dkim_rsavariants.py | |||
429 | +++ b/dkim/tests/test_dkim_rsavariants.py | |||
430 | @@ -22,6 +22,7 @@ import unittest | |||
431 | 22 | import time | 22 | import time |
432 | 23 | 23 | ||
433 | 24 | import dkim | 24 | import dkim |
434 | 25 | from . import utf8_decode | ||
435 | 25 | 26 | ||
436 | 26 | 27 | ||
437 | 27 | def read_test_data(filename): | 28 | def read_test_data(filename): |
438 | @@ -51,9 +52,8 @@ class TestSignAndVerify(unittest.TestCase): | |||
439 | 51 | 'test4._domainkey.example.com.': read_test_data("2048_testkey_wo_markers.pub.rsa.txt"), | 52 | 'test4._domainkey.example.com.': read_test_data("2048_testkey_wo_markers.pub.rsa.txt"), |
440 | 52 | 'test5._domainkey.example.com.': read_test_data("2048_testkey_PKCS8.key.pub.txt") | 53 | 'test5._domainkey.example.com.': read_test_data("2048_testkey_PKCS8.key.pub.txt") |
441 | 53 | } | 54 | } |
445 | 54 | try: | 55 | domain = utf8_decode(domain) |
446 | 55 | domain = domain.decode('ascii') | 56 | if not domain: |
444 | 56 | except UnicodeDecodeError: | ||
447 | 57 | return None | 57 | return None |
448 | 58 | self.assertTrue(domain in _dns_responses,domain) | 58 | self.assertTrue(domain in _dns_responses,domain) |
449 | 59 | return _dns_responses[domain] | 59 | return _dns_responses[domain] |
450 | diff --git a/dkim/tests/test_dkim_tlsrpt.py b/dkim/tests/test_dkim_tlsrpt.py | |||
451 | index 83b31b6..2684885 100644 | |||
452 | --- a/dkim/tests/test_dkim_tlsrpt.py | |||
453 | +++ b/dkim/tests/test_dkim_tlsrpt.py | |||
454 | @@ -22,6 +22,7 @@ import unittest | |||
455 | 22 | import time | 22 | import time |
456 | 23 | 23 | ||
457 | 24 | import dkim | 24 | import dkim |
458 | 25 | from . import utf8_decode | ||
459 | 25 | 26 | ||
460 | 26 | 27 | ||
461 | 27 | def read_test_data(filename): | 28 | def read_test_data(filename): |
462 | @@ -79,9 +80,8 @@ hzY8i+RQ9DpSVpPbF7ykQxtKXkv/ahW3KjViiAH+ghvvIhkx4xYSIc9oSwVmAl5Oct\ | |||
463 | 79 | MEeWUwg8Istjqz8BZeTWbf41fbNhte7Y+YqZOwq1Sd0DbvYAD9NOZK9vlfuac0598H\ | 80 | MEeWUwg8Istjqz8BZeTWbf41fbNhte7Y+YqZOwq1Sd0DbvYAD9NOZK9vlfuac0598H\ |
464 | 80 | Y+vtSBczUiKERHv1yRbcaQtZFh5wtiRrN04BLUTD21MycBX5jYchHjPY/wIDAQAB""" | 81 | Y+vtSBczUiKERHv1yRbcaQtZFh5wtiRrN04BLUTD21MycBX5jYchHjPY/wIDAQAB""" |
465 | 81 | } | 82 | } |
469 | 82 | try: | 83 | domain = utf8_decode(domain) |
470 | 83 | domain = domain.decode('ascii') | 84 | if not domain: |
468 | 84 | except UnicodeDecodeError: | ||
471 | 85 | return None | 85 | return None |
472 | 86 | self.assertTrue(domain in _dns_responses,domain) | 86 | self.assertTrue(domain in _dns_responses,domain) |
473 | 87 | return _dns_responses[domain] | 87 | return _dns_responses[domain] |
474 | @@ -103,9 +103,8 @@ hzY8i+RQ9DpSVpPbF7ykQxtKXkv/ahW3KjViiAH+ghvvIhkx4xYSIc9oSwVmAl5Oct\ | |||
475 | 103 | MEeWUwg8Istjqz8BZeTWbf41fbNhte7Y+YqZOwq1Sd0DbvYAD9NOZK9vlfuac0598H\ | 103 | MEeWUwg8Istjqz8BZeTWbf41fbNhte7Y+YqZOwq1Sd0DbvYAD9NOZK9vlfuac0598H\ |
476 | 104 | Y+vtSBczUiKERHv1yRbcaQtZFh5wtiRrN04BLUTD21MycBX5jYchHjPY/wIDAQAB""" | 104 | Y+vtSBczUiKERHv1yRbcaQtZFh5wtiRrN04BLUTD21MycBX5jYchHjPY/wIDAQAB""" |
477 | 105 | } | 105 | } |
481 | 106 | try: | 106 | domain = utf8_decode(domain) |
482 | 107 | domain = domain.decode('ascii') | 107 | if not domain: |
480 | 108 | except UnicodeDecodeError: | ||
483 | 109 | return None | 108 | return None |
484 | 110 | self.assertTrue(domain in _dns_responses,domain) | 109 | self.assertTrue(domain in _dns_responses,domain) |
485 | 111 | return _dns_responses[domain] | 110 | return _dns_responses[domain] |
486 | @@ -127,9 +126,8 @@ hzY8i+RQ9DpSVpPbF7ykQxtKXkv/ahW3KjViiAH+ghvvIhkx4xYSIc9oSwVmAl5Oct\ | |||
487 | 127 | MEeWUwg8Istjqz8BZeTWbf41fbNhte7Y+YqZOwq1Sd0DbvYAD9NOZK9vlfuac0598H\ | 126 | MEeWUwg8Istjqz8BZeTWbf41fbNhte7Y+YqZOwq1Sd0DbvYAD9NOZK9vlfuac0598H\ |
488 | 128 | Y+vtSBczUiKERHv1yRbcaQtZFh5wtiRrN04BLUTD21MycBX5jYchHjPY/wIDAQAB""" | 127 | Y+vtSBczUiKERHv1yRbcaQtZFh5wtiRrN04BLUTD21MycBX5jYchHjPY/wIDAQAB""" |
489 | 129 | } | 128 | } |
493 | 130 | try: | 129 | domain = utf8_decode(domain) |
494 | 131 | domain = domain.decode('ascii') | 130 | if not domain: |
492 | 132 | except UnicodeDecodeError: | ||
495 | 133 | return None | 131 | return None |
496 | 134 | self.assertTrue(domain in _dns_responses,domain) | 132 | self.assertTrue(domain in _dns_responses,domain) |
497 | 135 | return _dns_responses[domain] | 133 | return _dns_responses[domain] |
498 | @@ -151,9 +149,8 @@ hzY8i+RQ9DpSVpPbF7ykQxtKXkv/ahW3KjViiAH+ghvvIhkx4xYSIc9oSwVmAl5Oct\ | |||
499 | 151 | MEeWUwg8Istjqz8BZeTWbf41fbNhte7Y+YqZOwq1Sd0DbvYAD9NOZK9vlfuac0598H\ | 149 | MEeWUwg8Istjqz8BZeTWbf41fbNhte7Y+YqZOwq1Sd0DbvYAD9NOZK9vlfuac0598H\ |
500 | 152 | Y+vtSBczUiKERHv1yRbcaQtZFh5wtiRrN04BLUTD21MycBX5jYchHjPY/wIDAQAB""" | 150 | Y+vtSBczUiKERHv1yRbcaQtZFh5wtiRrN04BLUTD21MycBX5jYchHjPY/wIDAQAB""" |
501 | 153 | } | 151 | } |
505 | 154 | try: | 152 | domain = utf8_decode(domain) |
506 | 155 | domain = domain.decode('ascii') | 153 | if not domain: |
504 | 156 | except UnicodeDecodeError: | ||
507 | 157 | return None | 154 | return None |
508 | 158 | self.assertTrue(domain in _dns_responses,domain) | 155 | self.assertTrue(domain in _dns_responses,domain) |
509 | 159 | return _dns_responses[domain] | 156 | return _dns_responses[domain] |
510 | @@ -170,9 +167,8 @@ b/mPfjC0QJTocVBq6Za/PlzfV+Py92VaCak19F4WrbVTK5Gg5tW220MCAwEAAQ==""" | |||
511 | 170 | 'brisbane._domainkey.football.example.com.': """v=DKIM1; k=ed25519; \ | 167 | 'brisbane._domainkey.football.example.com.': """v=DKIM1; k=ed25519; \ |
512 | 171 | p=11qYAYKxCrfVS/7TyWQHOg7hcvPapiMlrwIaaPcHURo=""" | 168 | p=11qYAYKxCrfVS/7TyWQHOg7hcvPapiMlrwIaaPcHURo=""" |
513 | 172 | } | 169 | } |
517 | 173 | try: | 170 | domain = utf8_decode(domain) |
518 | 174 | domain = domain.decode('ascii') | 171 | if not domain: |
516 | 175 | except UnicodeDecodeError: | ||
519 | 176 | return None | 172 | return None |
520 | 177 | self.assertTrue(domain in _dns_responses,domain) | 173 | self.assertTrue(domain in _dns_responses,domain) |
521 | 178 | return _dns_responses[domain] | 174 | return _dns_responses[domain] |
522 | @@ -194,9 +190,8 @@ hzY8i+RQ9DpSVpPbF7ykQxtKXkv/ahW3KjViiAH+ghvvIhkx4xYSIc9oSwVmAl5Oct\ | |||
523 | 194 | MEeWUwg8Istjqz8BZeTWbf41fbNhte7Y+YqZOwq1Sd0DbvYAD9NOZK9vlfuac0598H\ | 190 | MEeWUwg8Istjqz8BZeTWbf41fbNhte7Y+YqZOwq1Sd0DbvYAD9NOZK9vlfuac0598H\ |
524 | 195 | Y+vtSBczUiKERHv1yRbcaQtZFh5wtiRrN04BLUTD21MycBX5jYchHjPY/wIDAQAB""" | 191 | Y+vtSBczUiKERHv1yRbcaQtZFh5wtiRrN04BLUTD21MycBX5jYchHjPY/wIDAQAB""" |
525 | 196 | } | 192 | } |
529 | 197 | try: | 193 | domain = utf8_decode(domain) |
530 | 198 | domain = domain.decode('ascii') | 194 | if not domain: |
528 | 199 | except UnicodeDecodeError: | ||
531 | 200 | return None | 195 | return None |
532 | 201 | self.assertTrue(domain in _dns_responses,domain) | 196 | self.assertTrue(domain in _dns_responses,domain) |
533 | 202 | return _dns_responses[domain] | 197 | return _dns_responses[domain] |