1# This software is provided 'as-is', without any express or implied 2# warranty. In no event will the author be held liable for any damages 3# arising from the use of this software. 4# 5# Permission is granted to anyone to use this software for any purpose, 6# including commercial applications, and to alter it and redistribute it 7# freely, subject to the following restrictions: 8# 9# 1. The origin of this software must not be misrepresented; you must not 10# claim that you wrote the original software. If you use this software 11# in a product, an acknowledgment in the product documentation would be 12# appreciated but is not required. 13# 2. Altered source versions must be plainly marked as such, and must not be 14# misrepresented as being the original software. 15# 3. This notice may not be removed or altered from any source distribution. 16# 17# Copyright (c) 2011 William Grant <me@williamgrant.id.au> 18 19import email 20import os.path 21import unittest 22import time 23 24import dkim 25 26 27def read_test_data(filename): 28 """Get the content of the given test data file. 29 30 The files live in dkim/tests/data. 31 """ 32 path = os.path.join(os.path.dirname(__file__), 'data', filename) 33 with open(path, 'rb') as f: 34 return f.read() 35 36 37class TestFold(unittest.TestCase): 38 39 def test_short_line(self): 40 self.assertEqual( 41 b"foo", dkim.fold(b"foo")) 42 43 def test_long_line(self): 44 # The function is terribly broken, not passing even this simple 45 # test. 46 self.assertEqual( 47 b"foo" * 24 + b"\r\n foo", dkim.fold(b"foo" * 25)) 48 49 def test_linesep(self): 50 self.assertEqual( 51 b"foo" * 24 + b"\n foo", dkim.fold(b"foo" * 25, linesep=b"\n")) 52 53 54 55class TestSignAndVerify(unittest.TestCase): 56 """End-to-end signature and verification tests.""" 57 58 def setUp(self): 59 self.message = read_test_data("test.message") 60 self.message3 = read_test_data("rfc6376.msg") 61 self.message4 = read_test_data("rfc6376.signed.msg") 62 self.message5 = read_test_data("rfc6376.signed.rsa.msg") 63 self.key = read_test_data("test.private") 64 self.rfckey = read_test_data("rfc8032_7_1.key") 65 66 def dnsfunc(self, domain, timeout=5): 67 sample_dns = """\ 68k=rsa; s=email;\ 69p=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANmBe10IgY+u7h3enWTukkqtUD5PR52T\ 70b/mPfjC0QJTocVBq6Za/PlzfV+Py92VaCak19F4WrbVTK5Gg5tW220MCAwEAAQ==""" 71 72 _dns_responses = { 73 'example._domainkey.canonical.com.': sample_dns, 74 'test._domainkey.example.com.': read_test_data("test.txt"), 75 '20120113._domainkey.gmail.com.': """k=rsa; \ 76p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1Kd87/UeJjenpabgbFwh\ 77+eBCsSTrqmwIYYvywlbhbqoo2DymndFkbjOVIPIldNs/m40KF+yzMn1skyoxcTUGCQ\ 78s8g3FgD2Ap3ZB5DekAo5wMmk4wimDO+U8QzI3SD07y2+07wlNWwIt8svnxgdxGkVbb\ 79hzY8i+RQ9DpSVpPbF7ykQxtKXkv/ahW3KjViiAH+ghvvIhkx4xYSIc9oSwVmAl5Oct\ 80MEeWUwg8Istjqz8BZeTWbf41fbNhte7Y+YqZOwq1Sd0DbvYAD9NOZK9vlfuac0598H\ 81Y+vtSBczUiKERHv1yRbcaQtZFh5wtiRrN04BLUTD21MycBX5jYchHjPY/wIDAQAB""" 82 } 83 try: 84 domain = domain.decode('ascii') 85 except UnicodeDecodeError: 86 return None 87 self.assertTrue(domain in _dns_responses,domain) 88 return _dns_responses[domain] 89 90 def dnsfunc2(self, domain, timeout=5): 91 sample_dns = """\ 92k=rsa; \ 93p=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANmBe10IgY+u7h3enWTukkqtUD5PR52T\ 94b/mPfjC0QJTocVBq6Za/PlzfV+Py92VaCak19F4WrbVTK5Gg5tW220MCAwEAAQ==""" 95 96 _dns_responses = { 97 'example._domainkey.canonical.com.': sample_dns, 98 'test._domainkey.example.com.': read_test_data("test2.txt"), 99 '20120113._domainkey.gmail.com.': """\ 100p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1Kd87/UeJjenpabgbFwh\ 101+eBCsSTrqmwIYYvywlbhbqoo2DymndFkbjOVIPIldNs/m40KF+yzMn1skyoxcTUGCQ\ 102s8g3FgD2Ap3ZB5DekAo5wMmk4wimDO+U8QzI3SD07y2+07wlNWwIt8svnxgdxGkVbb\ 103hzY8i+RQ9DpSVpPbF7ykQxtKXkv/ahW3KjViiAH+ghvvIhkx4xYSIc9oSwVmAl5Oct\ 104MEeWUwg8Istjqz8BZeTWbf41fbNhte7Y+YqZOwq1Sd0DbvYAD9NOZK9vlfuac0598H\ 105Y+vtSBczUiKERHv1yRbcaQtZFh5wtiRrN04BLUTD21MycBX5jYchHjPY/wIDAQAB""" 106 } 107 try: 108 domain = domain.decode('ascii') 109 except UnicodeDecodeError: 110 return None 111 self.assertTrue(domain in _dns_responses,domain) 112 return _dns_responses[domain] 113 114 def dnsfunc3(self, domain, timeout=5): 115 sample_dns = """\ 116k=rsa; \ 117p=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANmBe10IgY+u7h3enWTukkqtUD5PR52T\ 118b/mPfjC0QJTocVBq6Za/PlzfV+Py92VaCak19F4WrbVTK5Gg5tW220MCAwEAAQ==""" 119 120 _dns_responses = { 121 'example._domainkey.canonical.com.': sample_dns, 122 'test._domainkey.example.com.': read_test_data("badversion.txt"), 123 '20120113._domainkey.gmail.com.': """\ 124p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1Kd87/UeJjenpabgbFwh\ 125+eBCsSTrqmwIYYvywlbhbqoo2DymndFkbjOVIPIldNs/m40KF+yzMn1skyoxcTUGCQ\ 126s8g3FgD2Ap3ZB5DekAo5wMmk4wimDO+U8QzI3SD07y2+07wlNWwIt8svnxgdxGkVbb\ 127hzY8i+RQ9DpSVpPbF7ykQxtKXkv/ahW3KjViiAH+ghvvIhkx4xYSIc9oSwVmAl5Oct\ 128MEeWUwg8Istjqz8BZeTWbf41fbNhte7Y+YqZOwq1Sd0DbvYAD9NOZK9vlfuac0598H\ 129Y+vtSBczUiKERHv1yRbcaQtZFh5wtiRrN04BLUTD21MycBX5jYchHjPY/wIDAQAB""" 130 } 131 try: 132 domain = domain.decode('ascii') 133 except UnicodeDecodeError: 134 return None 135 self.assertTrue(domain in _dns_responses,domain) 136 return _dns_responses[domain] 137 138 def dnsfunc4(self, domain, timeout=5): 139 sample_dns = """\ 140k=rsa; \ 141p=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANmBe10IgY+u7h3enWTukkqtUD5PR52T\ 142b/mPfjC0QJTocVBq6Za/PlzfV+Py92VaCak19F4WrbVTK5Gg5tW220MCAwEAAQ==""" 143 144 _dns_responses = { 145 'example._domainkey.canonical.com.': sample_dns, 146 'test._domainkey.example.com.': read_test_data("badk.txt"), 147 '20120113._domainkey.gmail.com.': """\ 148p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1Kd87/UeJjenpabgbFwh\ 149+eBCsSTrqmwIYYvywlbhbqoo2DymndFkbjOVIPIldNs/m40KF+yzMn1skyoxcTUGCQ\ 150s8g3FgD2Ap3ZB5DekAo5wMmk4wimDO+U8QzI3SD07y2+07wlNWwIt8svnxgdxGkVbb\ 151hzY8i+RQ9DpSVpPbF7ykQxtKXkv/ahW3KjViiAH+ghvvIhkx4xYSIc9oSwVmAl5Oct\ 152MEeWUwg8Istjqz8BZeTWbf41fbNhte7Y+YqZOwq1Sd0DbvYAD9NOZK9vlfuac0598H\ 153Y+vtSBczUiKERHv1yRbcaQtZFh5wtiRrN04BLUTD21MycBX5jYchHjPY/wIDAQAB""" 154 } 155 try: 156 domain = domain.decode('ascii') 157 except UnicodeDecodeError: 158 return None 159 self.assertTrue(domain in _dns_responses,domain) 160 return _dns_responses[domain] 161 162 def dnsfunc5(self, domain, timeout=5): 163 sample_dns = """\ 164k=rsa; \ 165p=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANmBe10IgY+u7h3enWTukkqtUD5PR52T\ 166b/mPfjC0QJTocVBq6Za/PlzfV+Py92VaCak19F4WrbVTK5Gg5tW220MCAwEAAQ==""" 167 168 _dns_responses = { 169 'example._domainkey.canonical.com.': sample_dns, 170 'test._domainkey.football.example.com.': read_test_data("test.txt"), 171 'brisbane._domainkey.football.example.com.': """v=DKIM1; k=ed25519; \ 172p=11qYAYKxCrfVS/7TyWQHOg7hcvPapiMlrwIaaPcHURo=""" 173 } 174 try: 175 domain = domain.decode('ascii') 176 except UnicodeDecodeError: 177 return None 178 self.assertTrue(domain in _dns_responses,domain) 179 return _dns_responses[domain] 180 181 def dnsfunc6(self, domain, timeout=5): 182 sample_dns = """\ 183k=rsa; \ 184p=MFwwDQYJKoZIhvNAQEBBQADSwAwSAJBANmBe10IgY+u7h3enWTukkqtUD5PR52T\ 185b/mPfjC0QJTocVBq6Za/PlzfV+Py92VaCak19F4WrbVTK5Gg5tW220MCAwEAAQ==""" 186 187 _dns_responses = { 188 'test._domainkey.football.example.com.': sample_dns, 189 'brisbane._domainkey.football.example.com.': """v=DKIM1; k=ed25519; \ 190p=11qYAYKxCrfVS/7TyWQHOg7hcvPapiMlrwIaaPcHURo=""" 191 } 192 try: 193 domain = domain.decode('ascii') 194 except UnicodeDecodeError: 195 return None 196 self.assertTrue(domain in _dns_responses,domain) 197 return _dns_responses[domain] 198 199 def test_verifies(self): 200 # A message verifies after being signed. 201 for header_algo in (b"simple", b"relaxed"): 202 for body_algo in (b"simple", b"relaxed"): 203 sig = dkim.sign( 204 self.message, b"test", b"example.com", self.key, 205 canonicalize=(header_algo, body_algo)) 206 res = dkim.verify(sig + self.message, dnsfunc=self.dnsfunc) 207 self.assertTrue(res) 208 209 def test_verifies_nosig(self): 210 # A message without signature does not verify. 211 res = dkim.verify(self.message, dnsfunc=self.dnsfunc) 212 self.assertFalse(res) 213 214 def test_double_verifies(self): 215 # A message also containing a ed25519 signature verifies after being signed with rsa. 216 for header_algo in (b"simple", b"relaxed"): 217 for body_algo in (b"simple", b"relaxed"): 218 sig = dkim.sign( 219 self.message3, b"test", b"football.example.com", self.key, 220 canonicalize=(header_algo, body_algo), signature_algorithm=b'rsa-sha256') 221 res = dkim.verify(sig + self.message3, dnsfunc=self.dnsfunc5) 222 self.assertTrue(res) 223 224 def test_double_previous_verifies(self): 225 # A message previously signed using both rsa and ed25519 verifies after being signed. 226 for header_algo in (b"simple", b"relaxed"): 227 for body_algo in (b"simple", b"relaxed"): 228 sig = dkim.sign( 229 self.message3, b"test", b"football.example.com", self.key, 230 canonicalize=(header_algo, body_algo), signature_algorithm=b'rsa-sha256') 231 d = dkim.DKIM(self.message4) 232 res = d.verify(dnsfunc=self.dnsfunc5) 233 self.assertTrue(res) 234 235 def test_catch_bad_key(self): 236 # Raise correct error for defective public key. 237 d = dkim.DKIM(self.message5) 238 res = d.verify(dnsfunc=self.dnsfunc6) 239 self.assertFalse(res) 240 241 def test_verifies_lflinesep(self): 242 # A message verifies after being signed. 243 for header_algo in (b"simple", b"relaxed"): 244 for body_algo in (b"simple", b"relaxed"): 245 sig = dkim.sign( 246 self.message, b"test", b"example.com", self.key, 247 canonicalize=(header_algo, body_algo), linesep=b"\n") 248 res = dkim.verify(sig + self.message, dnsfunc=self.dnsfunc) 249 self.assertFalse(b'\r\n' in sig) 250 self.assertTrue(res) 251 252 def test_implicit_k(self): 253 # A message verifies after being signed when k= tag is not provided. 254 for header_algo in (b"simple", b"relaxed"): 255 for body_algo in (b"simple", b"relaxed"): 256 sig = dkim.sign( 257 self.message, b"test", b"example.com", self.key, 258 canonicalize=(header_algo, body_algo)) 259 res = dkim.verify(sig + self.message, dnsfunc=self.dnsfunc2) 260 self.assertTrue(res) 261 262 def test_bad_version(self): 263 # A error is detected if a bad version is used. 264 for header_algo in (b"simple", b"relaxed"): 265 for body_algo in (b"simple", b"relaxed"): 266 sig = dkim.sign( 267 self.message, b"test", b"example.com", self.key, 268 canonicalize=(header_algo, body_algo)) 269 res = dkim.verify(sig + self.message, dnsfunc=self.dnsfunc3) 270 self.assertFalse(res) 271 272 def test_unknown_k(self): 273 # A error is detected if an unknown algorithm is in the k= tag. 274 for header_algo in (b"simple", b"relaxed"): 275 for body_algo in (b"simple", b"relaxed"): 276 sig = dkim.sign( 277 self.message, b"test", b"example.com", self.key, 278 canonicalize=(header_algo, body_algo)) 279 res = dkim.verify(sig + self.message, dnsfunc=self.dnsfunc4) 280 self.assertFalse(res) 281 282 def test_simple_signature(self): 283 # A message verifies after being signed with SHOULD headers 284 for header_algo in (b"simple", b"relaxed"): 285 for body_algo in (b"simple", b"relaxed"): 286 sig = dkim.sign( 287 self.message, b"test", b"example.com", self.key, 288 canonicalize=(header_algo, body_algo), 289 include_headers=(b'from',) + dkim.DKIM.SHOULD) 290 res = dkim.verify(sig + self.message, dnsfunc=self.dnsfunc) 291 self.assertTrue(res) 292 293 def test_string_include(self): 294 # A message can be signed when the include_headers is string 295 for header_algo in (b"simple", b"relaxed"): 296 for body_algo in (b"simple", b"relaxed"): 297 sig = dkim.sign( 298 self.message, b"test", b"example.com", self.key, 299 canonicalize=(header_algo, body_algo), 300 include_headers=('from',) ) 301 res = dkim.verify(sig + self.message, dnsfunc=self.dnsfunc) 302 self.assertTrue(res) 303 304 def test_add_body_length(self): 305 sig = dkim.sign( 306 self.message, b"test", b"example.com", self.key, length=True) 307 msg = email.message_from_string(self.message.decode('utf-8')) 308 self.assertIn('; l=%s' % len(msg.get_payload() + '\n'), sig.decode('utf-8')) 309 res = dkim.verify(sig + self.message, dnsfunc=self.dnsfunc) 310 self.assertTrue(res) 311 312 def test_altered_body_fails(self): 313 # An altered body fails verification. 314 for header_algo in (b"simple", b"relaxed"): 315 for body_algo in (b"simple", b"relaxed"): 316 sig = dkim.sign( 317 self.message, b"test", b"example.com", self.key) 318 res = dkim.verify( 319 sig + self.message + b"foo", dnsfunc=self.dnsfunc) 320 self.assertFalse(res) 321 322 def test_l_verify(self): 323 # Sign with l=, add text, should verify 324 for header_algo in (b"simple", b"relaxed"): 325 for body_algo in (b"simple", b"relaxed"): 326 sig = dkim.sign( 327 self.message, b"test", b"example.com", self.key, 328 canonicalize=(header_algo, body_algo), length=True) 329 self.message += b'added more text\n' 330 res = dkim.verify(sig + self.message, dnsfunc=self.dnsfunc) 331 self.assertTrue(res) 332 333 def test_present(self): 334 # Test DKIM.present(). 335 d = dkim.DKIM(self.message,signature_algorithm=b'rsa-sha256') 336 present = d.present() 337 self.assertFalse(present) 338 sig = d.sign(b"test", b"example.com", self.key) 339 signed = sig + self.message 340 d2 = dkim.DKIM(signed) 341 present = d2.present() 342 self.assertTrue(present) 343 344 def test_badly_encoded_domain_fails(self): 345 # Domains should be ASCII. Bad ASCII causes verification to fail. 346 sig = dkim.sign(self.message, b"test", b"example.com\xe9", self.key) 347 res = dkim.verify(sig + self.message, dnsfunc=self.dnsfunc) 348 self.assertFalse(res) 349 350 def test_dkim_signature_canonicalization(self): 351 # <https://bugs.launchpad.net/ubuntu/+source/pydkim/+bug/587783> 352 # Relaxed-mode header signing is wrong 353 # <https://bugs.launchpad.net/dkimpy/+bug/939128> 354 # Simple-mode signature header verification is wrong 355 # (should ignore FWS anywhere in signature tag: b=) 356 sample_msg = b"""\ 357From: mbp@canonical.com 358To: scottk@example.com 359Subject: this is my 360 test message 361""".replace(b'\n', b'\r\n') 362 363 sample_privkey = b"""\ 364-----BEGIN RSA PRIVATE KEY----- 365MIIBOwIBAAJBANmBe10IgY+u7h3enWTukkqtUD5PR52Tb/mPfjC0QJTocVBq6Za/ 366PlzfV+Py92VaCak19F4WrbVTK5Gg5tW220MCAwEAAQJAYFUKsD+uMlcFu1D3YNaR 367EGYGXjJ6w32jYGJ/P072M3yWOq2S1dvDthI3nRT8MFjZ1wHDAYHrSpfDNJ3v2fvZ 368cQIhAPgRPmVYn+TGd59asiqG1SZqh+p+CRYHW7B8BsicG5t3AiEA4HYNOohlgWan 3698tKgqLJgUdPFbaHZO1nDyBgvV8hvWZUCIQDDdCq6hYKuKeYUy8w3j7cgJq3ih922 3702qNWwdJCfCWQbwIgTY0cBvQnNe0067WQIpj2pG7pkHZR6qqZ9SE+AjNTHX0CIQCI 371Mgq55Y9MCq5wqzy141rnxrJxTwK9ABo3IAFMWEov3g== 372-----END RSA PRIVATE KEY----- 373""" 374 375 sample_pubkey = """\ 376-----BEGIN PUBLIC KEY----- 377MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANmBe10IgY+u7h3enWTukkqtUD5PR52T 378b/mPfjC0QJTocVBq6Za/PlzfV+Py92VaCak19F4WrbVTK5Gg5tW220MCAwEAAQ== 379-----END PUBLIC KEY----- 380""" 381 382 for header_mode in [dkim.Relaxed, dkim.Simple]: 383 384 dkim_header = dkim.sign(sample_msg, b'example', b'canonical.com', 385 sample_privkey, canonicalize=(header_mode, dkim.Relaxed)) 386 # Folding dkim_header affects b= tag only, since dkim.sign folds 387 # sig_value with empty b= before hashing, and then appends the 388 # signature. So folding dkim_header again adds FWS to 389 # the b= tag only. This should be ignored even with 390 # simple canonicalization. 391 # http://tools.ietf.org/html/rfc4871#section-3.5 392 signed = dkim.fold(dkim_header) + sample_msg 393 result = dkim.verify(signed,dnsfunc=self.dnsfunc, 394 minkey=512) 395 self.assertTrue(result) 396 dkim_header = dkim.fold(dkim_header) 397 # use a tab for last fold to test tab in FWS bug 398 pos = dkim_header.rindex(b'\r\n ') 399 dkim_header = dkim_header[:pos]+b'\r\n\t'+dkim_header[pos+3:] 400 result = dkim.verify(dkim_header + sample_msg, 401 dnsfunc=self.dnsfunc, minkey=512) 402 self.assertTrue(result) 403 404 def test_degenerate_folding(self): 405 # <http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=711751> 406 # degenerate folding is ugly but legal 407 message = read_test_data("test2.message") 408 dv = dkim.DKIM(message) 409 res = dv.verify(dnsfunc=self.dnsfunc) 410 self.assertTrue(res) 411 412 def test_extra_headers(self): 413 # <https://bugs.launchpad.net/dkimpy/+bug/737311> 414 # extra headers above From caused failure 415 #message = read_test_data("test_extra.message") 416 message = read_test_data("message.mbox") 417 for header_algo in (b"simple", b"relaxed"): 418 for body_algo in (b"simple", b"relaxed"): 419 d = dkim.DKIM(message) 420 # bug requires a repeated header to manifest 421 d.should_not_sign.remove(b'received') 422 sig = d.sign(b"test", b"example.com", self.key, 423 include_headers=d.all_sign_headers(), 424 canonicalize=(header_algo, body_algo)) 425 dv = dkim.DKIM(sig + message) 426 res = dv.verify(dnsfunc=self.dnsfunc) 427 self.assertEqual(d.include_headers,dv.include_headers) 428 s = dkim.select_headers(d.headers,d.include_headers) 429 sv = dkim.select_headers(dv.headers,dv.include_headers) 430 self.assertEqual(s,sv) 431 self.assertTrue(res) 432 433 def test_multiple_from_fails(self): 434 # <https://bugs.launchpad.net/dkimpy/+bug/644046> 435 # additional From header fields should cause verify failure 436 hfrom = b'From: "Resident Evil" <sales@spammer.com>\r\n' 437 h,b = self.message.split(b'\n\n',1) 438 for header_algo in (b"simple", b"relaxed"): 439 for body_algo in (b"simple", b"relaxed"): 440 sig = dkim.sign( 441 self.message, b"test", b"example.com", self.key) 442 # adding an unknown header still verifies 443 h1 = h+b'\r\n'+b'X-Foo: bar' 444 message = b'\n\n'.join((h1,b)) 445 res = dkim.verify(sig+message, dnsfunc=self.dnsfunc) 446 self.assertTrue(res) 447 # adding extra from at end should not verify 448 h1 = h+b'\r\n'+hfrom.strip() 449 message = b'\n\n'.join((h1,b)) 450 res = dkim.verify(sig+message, dnsfunc=self.dnsfunc) 451 self.assertFalse(res) 452 # add extra from in front should not verify either 453 h1 = hfrom+h 454 message = b'\n\n'.join((h1,b)) 455 res = dkim.verify(sig+message, dnsfunc=self.dnsfunc) 456 self.assertFalse(res) 457 458 def test_no_from_fails(self): 459 # Body From is mandatory to be in the message and mandatory to sign 460 sigerror = False 461 sig = '' 462 message = read_test_data('test_nofrom.message') 463 selector = 'test' 464 domain = 'example.com' 465 identity = None 466 try: 467 sig = dkim.sign(message, selector, domain, read_test_data('test.private'), identity = identity) 468 except dkim.ParameterError as x: 469 sigerror = True 470 self.assertTrue(sigerror) 471 472 def test_validate_signature_fields(self): 473 sig = {b'v': b'1', 474 b'a': b'rsa-sha256', 475 b'b': b'K/UUOt8lCtgjp3kSTogqBm9lY1Yax/NwZ+bKm39/WKzo5KYe3L/6RoIA/0oiDX4kO\n \t Qut49HCV6ZUe6dY9V5qWBwLanRs1sCnObaOGMpFfs8tU4TWpDSVXaNZAqn15XVW0WH\n \t EzOzUfVuatpa1kF4voIgSbmZHR1vN3WpRtcTBe/I=', 476 b'bh': b'n0HUwGCP28PkesXBPH82Kboy8LhNFWU9zUISIpAez7M=', 477 b'c': b'simple/simple', 478 b'd': b'kitterman.com', 479 b'i': b'scott@Kitterman.com', 480 b'h': b'From:To:Subject:Date:Cc:MIME-Version:Content-Type:\n \t Content-Transfer-Encoding:Message-Id', 481 b's': b'2007-00', 482 b't': b'1299525798'} 483 dkim.validate_signature_fields(sig) 484 # try new version 485 sigVer = sig.copy() 486 sigVer[b'v'] = 2 487 self.assertRaises(dkim.ValidationError, dkim.validate_signature_fields, sigVer) 488 # try with x 489 sigX = sig.copy() 490 sigX[b'x'] = b'1399525798' 491 dkim.validate_signature_fields(sig) 492 # try with late t 493 sigX[b't'] = b'1400000000' 494 self.assertRaises(dkim.ValidationError, dkim.validate_signature_fields, sigX) 495 # try without t 496 now = int(time.time()) 497 sigX[b'x'] = str(now+400000).encode('ascii') 498 dkim.validate_signature_fields(sigX) 499 # try when expired a day ago 500 sigX[b'x'] = str(now - 24*3600).encode('ascii') 501 self.assertRaises(dkim.ValidationError, dkim.validate_signature_fields, sigX) 502 503 504def test_suite(): 505 from unittest import TestLoader 506 return TestLoader().loadTestsFromName(__name__) 507