1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 import email
21 import os.path
22 import unittest
23 import time
24
25 import dkim
26
27
29 """Get the content of the given test data file.
30
31 The files live in dkim/tests/data.
32 """
33 path = os.path.join(os.path.dirname(__file__), 'data', filename)
34 with open(path, 'rb') as f:
35 return f.read()
36
37
39
41 self.assertEqual(
42 b"foo", dkim.fold(b"foo"))
43
45
46
47 self.assertEqual(
48 b"foo" * 24 + b"\r\n foo", dkim.fold(b"foo" * 25))
49
50
52 """End-to-end signature and verification tests."""
53
61
62 - def dnsfunc(self, domain, timeout=5):
63 sample_dns = """\
64 k=ed25519; \
65 p=yi50DjK5O9pqbFpNHklsv9lqaS0ArSYu02qp1S0DW1Y="""
66
67 _dns_responses = {
68 'example._domainkey.canonical.com.': sample_dns,
69 'test._domainkey.example.net.': """v=DKIM1; k=ed25519; \
70 p=yi50DjK5O9pqbFpNHklsv9lqaS0ArSYu02qp1S0DW1Y=""",
71 'sed._domainkey.test.ex.': read_test_data("eximtest.dns"),
72 'brisbane._domainkey.football.example.com.': """v=DKIM1; k=ed25519; \
73 p=11qYAYKxCrfVS/7TyWQHOg7hcvPapiMlrwIaaPcHURo="""
74 }
75 try:
76 domain = domain.decode('ascii')
77 except UnicodeDecodeError:
78 return None
79 self.assertTrue(domain in _dns_responses,domain)
80 return _dns_responses[domain]
81
83
84 for header_algo in (b"simple", b"relaxed"):
85 for body_algo in (b"simple", b"relaxed"):
86 sig = dkim.sign(
87 self.message, b"test", b"example.net", self.key,
88 canonicalize=(header_algo, body_algo), signature_algorithm=b'ed25519-sha256')
89 res = dkim.verify(sig + self.message, dnsfunc=self.dnsfunc)
90 self.assertTrue(res)
91
93
94 for header_algo in (b"simple", b"relaxed"):
95 for body_algo in (b"simple", b"relaxed"):
96 sig = dkim.sign(
97 self.message3, b"brisbane", b"football.example.com", self.rfckey,
98 canonicalize=(header_algo, body_algo), signature_algorithm=b'ed25519-sha256')
99 res = dkim.verify(sig + self.message3, dnsfunc=self.dnsfunc)
100 self.assertTrue(res)
101
103
104 for header_algo in (b"simple", b"relaxed"):
105 for body_algo in (b"simple", b"relaxed"):
106 sig = dkim.sign(
107 self.message3, b"brisbane", b"football.example.com", self.rfckey,
108 canonicalize=(header_algo, body_algo), signature_algorithm=b'ed25519-sha256')
109 d = dkim.DKIM(self.message4)
110 res = d.verify(dnsfunc=self.dnsfunc)
111 self.assertTrue(res)
112
114
115 for header_algo in (b"simple", b"relaxed"):
116 for body_algo in (b"simple", b"relaxed"):
117 sig = dkim.sign(
118 self.message, b"test", b"example.net", self.key,
119 canonicalize=(header_algo, body_algo),
120 include_headers=(b'from',) + dkim.DKIM.SHOULD,
121 signature_algorithm=b'ed25519-sha256')
122 res = dkim.verify(sig + self.message, dnsfunc=self.dnsfunc)
123 self.assertTrue(res)
124
129
131 sig = dkim.sign(
132 self.message, b"test", b"example.net", self.key, length=True,
133 signature_algorithm=b'ed25519-sha256')
134 msg = email.message_from_string(self.message.decode('utf-8'))
135 self.assertIn('; l=%s' % len(msg.get_payload() + '\n'), sig.decode('utf-8'))
136 res = dkim.verify(sig + self.message, dnsfunc=self.dnsfunc)
137 self.assertTrue(res)
138
140
141 for header_algo in (b"simple", b"relaxed"):
142 for body_algo in (b"simple", b"relaxed"):
143 sig = dkim.sign(
144 self.message, b"test", b"example.net", self.key,
145 signature_algorithm=b'ed25519-sha256')
146 res = dkim.verify(
147 sig + self.message + b"foo", dnsfunc=self.dnsfunc)
148 self.assertFalse(res)
149
151
152 sig = dkim.sign(self.message, b"test", b"example.net\xe9", self.key,
153 signature_algorithm=b'ed25519-sha256')
154 res = dkim.verify(sig + self.message, dnsfunc=self.dnsfunc)
155 self.assertFalse(res)
156
158
159
160
161
162
163 sample_msg = b"""\
164 From: mbp@canonical.com
165 To: scottk@example.net
166 Subject: this is my
167 test message
168 """.replace(b'\n', b'\r\n')
169
170 sample_privkey = b"""\
171 fL+5V9EquCZAovKik3pA6Lk9zwCzoEtjIuIqK9ZXHHA=\
172 """
173
174 sample_pubkey = """\
175 yi50DjK5O9pqbFpNHklsv9lqaS0ArSYu02qp1S0DW1Y=\
176 """
177
178 for header_mode in [dkim.Relaxed, dkim.Simple]:
179
180 dkim_header = dkim.sign(sample_msg, b'example', b'canonical.com',
181 sample_privkey, canonicalize=(header_mode, dkim.Relaxed),
182 signature_algorithm=b'ed25519-sha256')
183
184
185
186
187
188
189 signed = dkim.fold(dkim_header) + sample_msg
190 result = dkim.verify(signed,dnsfunc=self.dnsfunc)
191 self.assertTrue(result)
192 dkim_header = dkim.fold(dkim_header)
193
194 pos = dkim_header.rindex(b'\r\n ')
195 dkim_header = dkim_header[:pos]+b'\r\n\t'+dkim_header[pos+3:]
196 result = dkim.verify(dkim_header + sample_msg,
197 dnsfunc=self.dnsfunc)
198 self.assertTrue(result)
199
201
202
203
204 message = read_test_data("message.mbox")
205 for header_algo in (b"simple", b"relaxed"):
206 for body_algo in (b"simple", b"relaxed"):
207 d = dkim.DKIM(message)
208
209 d.should_not_sign.remove(b'received')
210 sig = d.sign(b"test", b"example.net", self.key,
211 signature_algorithm=b'ed25519-sha256',
212 include_headers=d.all_sign_headers(),
213 canonicalize=(header_algo, body_algo))
214 dv = dkim.DKIM(sig + message)
215 res = dv.verify(dnsfunc=self.dnsfunc)
216 self.assertEqual(d.include_headers,dv.include_headers)
217 s = dkim.select_headers(d.headers,d.include_headers)
218 sv = dkim.select_headers(dv.headers,dv.include_headers)
219 self.assertEqual(s,sv)
220 self.assertTrue(res)
221
223
224
225 hfrom = b'From: "Resident Evil" <sales@spammer.com>\r\n'
226 h,b = self.message.split(b'\n\n',1)
227 for header_algo in (b"simple", b"relaxed"):
228 for body_algo in (b"simple", b"relaxed"):
229 sig = dkim.sign(
230 self.message, b"test", b"example.net", self.key,
231 signature_algorithm=b'ed25519-sha256')
232
233 h1 = h+b'\r\n'+b'X-Foo: bar'
234 message = b'\n\n'.join((h1,b))
235 res = dkim.verify(sig+message, dnsfunc=self.dnsfunc)
236 self.assertTrue(res)
237
238 h1 = h+b'\r\n'+hfrom.strip()
239 message = b'\n\n'.join((h1,b))
240 res = dkim.verify(sig+message, dnsfunc=self.dnsfunc)
241 self.assertFalse(res)
242
243 h1 = hfrom+h
244 message = b'\n\n'.join((h1,b))
245 res = dkim.verify(sig+message, dnsfunc=self.dnsfunc)
246 self.assertFalse(res)
247
249
250 sigerror = False
251 sig = ''
252 message = read_test_data('test_nofrom.message')
253 selector = 'test'
254 domain = 'example.net'
255 identity = None
256 try:
257 sig = dkim.sign(message, selector, domain,
258 read_test_data('ed25519test.key'), identity = identity,
259 signature_algorithm=b'ed25519-sha256')
260 except dkim.ParameterError as x:
261 sigerror = True
262 self.assertTrue(sigerror)
263
265 sig = {b'v': b'1',
266 b'a': b'ed25519-sha256',
267 b'b': b'K/UUOt8lCtgjp3kSTogqBm9lY1Yax/NwZ+bKm39/WKzo5KYe3L/6RoIA/0oiDX4kO\n \t Qut49HCV6ZUe6dY9V5qWBwLanRs1sCnObaOGMpFfs8tU4TWpDSVXaNZAqn15XVW0WH\n \t EzOzUfVuatpa1kF4voIgSbmZHR1vN3WpRtcTBe/I=',
268 b'bh': b'n0HUwGCP28PkesXBPH82Kboy8LhNFWU9zUISIpAez7M=',
269 b'c': b'simple/simple',
270 b'd': b'kitterman.com',
271 b'i': b'scott@Kitterman.com',
272 b'h': b'From:To:Subject:Date:Cc:MIME-Version:Content-Type:\n \t Content-Transfer-Encoding:Message-Id',
273 b's': b'2007-00',
274 b't': b'1299525798'}
275 dkim.validate_signature_fields(sig)
276
277 sigVer = sig.copy()
278 sigVer[b'v'] = 2
279 self.assertRaises(dkim.ValidationError, dkim.validate_signature_fields, sigVer)
280
281 sigX = sig.copy()
282 sigX[b'x'] = b'1399525798'
283 dkim.validate_signature_fields(sig)
284
285 sigX[b't'] = b'1400000000'
286 self.assertRaises(dkim.ValidationError, dkim.validate_signature_fields, sigX)
287
288 now = int(time.time())
289 sigX[b'x'] = str(now+400000).encode('ascii')
290 dkim.validate_signature_fields(sigX)
291
292 sigX[b'x'] = str(now - 24*3600).encode('ascii')
293 self.assertRaises(dkim.ValidationError, dkim.validate_signature_fields, sigX)
294
295
297 from unittest import TestLoader
298 return TestLoader().loadTestsFromName(__name__)
299