From 84b94dfcc84c9f9e1a69734a3005637daee77cb3 Mon Sep 17 00:00:00 2001 From: J08nY Date: Fri, 28 Jul 2017 15:25:57 +0200 Subject: Move PGP test base to `testing` subpackage. Fix exceptions in wrappers. --- src/mailman_pgp/commands/tests/test_key.py | 2 +- src/mailman_pgp/mta/tests/test_bulk.py | 2 +- src/mailman_pgp/mta/tests/test_deliver.py | 2 +- src/mailman_pgp/mta/tests/test_personalized.py | 2 +- src/mailman_pgp/pgp/inline.py | 25 ++- src/mailman_pgp/pgp/mime.py | 15 +- src/mailman_pgp/pgp/tests/base.py | 170 --------------------- .../tests/data/messages/inline_privkey_invalid.eml | 43 ++++++ .../tests/data/messages/inline_pubkey_invalid.eml | 30 ++++ .../tests/data/messages/mime_privkey_invalid.eml | 59 +++++++ .../tests/data/messages/mime_pubkey_invalid.eml | 46 ++++++ src/mailman_pgp/pgp/tests/test_inline.py | 12 +- src/mailman_pgp/pgp/tests/test_mime.py | 12 +- src/mailman_pgp/pgp/tests/test_mime_multisig.py | 2 +- src/mailman_pgp/pgp/tests/test_pgp.py | 2 +- src/mailman_pgp/pgp/tests/test_wrapper.py | 2 +- src/mailman_pgp/rest/tests/test_lists.py | 2 +- src/mailman_pgp/rules/tests/test_signature.py | 2 +- src/mailman_pgp/runners/tests/test_incoming.py | 2 +- src/mailman_pgp/testing/pgp.py | 170 +++++++++++++++++++++ src/mailman_pgp/workflows/tests/test_base.py | 2 +- src/mailman_pgp/workflows/tests/test_key_change.py | 2 +- 22 files changed, 404 insertions(+), 202 deletions(-) delete mode 100644 src/mailman_pgp/pgp/tests/base.py create mode 100644 src/mailman_pgp/pgp/tests/data/messages/inline_privkey_invalid.eml create mode 100644 src/mailman_pgp/pgp/tests/data/messages/inline_pubkey_invalid.eml create mode 100644 src/mailman_pgp/pgp/tests/data/messages/mime_privkey_invalid.eml create mode 100644 src/mailman_pgp/pgp/tests/data/messages/mime_pubkey_invalid.eml create mode 100644 src/mailman_pgp/testing/pgp.py diff --git a/src/mailman_pgp/commands/tests/test_key.py b/src/mailman_pgp/commands/tests/test_key.py index 8920e44..af482b4 100644 --- a/src/mailman_pgp/commands/tests/test_key.py +++ b/src/mailman_pgp/commands/tests/test_key.py @@ -33,9 +33,9 @@ from mailman_pgp.database import transaction from mailman_pgp.model.address import PGPAddress from mailman_pgp.model.list import PGPMailingList from mailman_pgp.pgp.mime import MIMEWrapper -from mailman_pgp.pgp.tests.base import load_key from mailman_pgp.pgp.wrapper import PGPWrapper from mailman_pgp.testing.layers import PGPConfigLayer +from mailman_pgp.testing.pgp import load_key from mailman_pgp.workflows.key_change import CHANGE_CONFIRM_REQUEST from mailman_pgp.workflows.pubkey import CONFIRM_REQUEST from mailman_pgp.workflows.subscription import OpenSubscriptionPolicy diff --git a/src/mailman_pgp/mta/tests/test_bulk.py b/src/mailman_pgp/mta/tests/test_bulk.py index ccb4988..02c5bf2 100644 --- a/src/mailman_pgp/mta/tests/test_bulk.py +++ b/src/mailman_pgp/mta/tests/test_bulk.py @@ -27,9 +27,9 @@ from mailman_pgp.database import transaction from mailman_pgp.model.address import PGPAddress from mailman_pgp.model.list import PGPMailingList from mailman_pgp.mta.bulk import (PGPBulkDelivery) -from mailman_pgp.pgp.tests.base import load_key from mailman_pgp.pgp.wrapper import PGPWrapper from mailman_pgp.testing.layers import PGPConfigLayer +from mailman_pgp.testing.pgp import load_key from mailman_pgp.utils.pgp import verifies diff --git a/src/mailman_pgp/mta/tests/test_deliver.py b/src/mailman_pgp/mta/tests/test_deliver.py index 756fd78..3b0594f 100644 --- a/src/mailman_pgp/mta/tests/test_deliver.py +++ b/src/mailman_pgp/mta/tests/test_deliver.py @@ -28,8 +28,8 @@ from mailman_pgp.database import transaction from mailman_pgp.model.address import PGPAddress from mailman_pgp.model.list import PGPMailingList from mailman_pgp.mta.deliver import deliver -from mailman_pgp.pgp.tests.base import load_key from mailman_pgp.testing.layers import PGPSMTPLayer +from mailman_pgp.testing.pgp import load_key class TestDeliver(TestCase): diff --git a/src/mailman_pgp/mta/tests/test_personalized.py b/src/mailman_pgp/mta/tests/test_personalized.py index 0357757..7f4caa8 100644 --- a/src/mailman_pgp/mta/tests/test_personalized.py +++ b/src/mailman_pgp/mta/tests/test_personalized.py @@ -27,9 +27,9 @@ from mailman_pgp.database import transaction from mailman_pgp.model.address import PGPAddress from mailman_pgp.model.list import PGPMailingList from mailman_pgp.mta.personalized import PGPPersonalizedDelivery -from mailman_pgp.pgp.tests.base import load_key from mailman_pgp.pgp.wrapper import PGPWrapper from mailman_pgp.testing.layers import PGPConfigLayer +from mailman_pgp.testing.pgp import load_key from mailman_pgp.utils.pgp import verifies diff --git a/src/mailman_pgp/pgp/inline.py b/src/mailman_pgp/pgp/inline.py index 2fe22cf..c93d801 100644 --- a/src/mailman_pgp/pgp/inline.py +++ b/src/mailman_pgp/pgp/inline.py @@ -19,11 +19,12 @@ import copy from email.iterators import walk -from pgpy import PGPKey, PGPMessage +from pgpy import PGPMessage from pgpy.constants import SymmetricKeyAlgorithm -from pgpy.types import Armorable from public import public +from mailman_pgp.utils.pgp import key_from_blob + @public class InlineWrapper: @@ -143,9 +144,8 @@ class InlineWrapper: def _has_keys(self, part): try: - dearm = Armorable.ascii_unarmor(part.get_payload()) - if dearm['magic'] in ('PUBLIC KEY BLOCK', 'PRIVATE KEY BLOCK'): - return True + key_from_blob(part.get_payload()) + return True except: pass return False @@ -168,11 +168,6 @@ class InlineWrapper: """ return any(self._walk(self._has_keys)) - def _keys(self, part): - # TODO: potentially return all things returned from from_blob? - key, _ = PGPKey.from_blob(part.get_payload()) - return key - def keys(self): """ Get the collection of keys in this message. @@ -183,7 +178,7 @@ class InlineWrapper: for part in walk(self.msg): if not part.is_multipart() and self._has_keys(part): try: - key = self._keys(part) + key = key_from_blob(part.get_payload()) except: continue yield key @@ -229,6 +224,7 @@ class InlineWrapper: def _decrypt(self, part, key): message = PGPMessage.from_blob(part.get_payload()) + # TODO: exception safe this. decrypted = key.decrypt(message) if decrypted.is_signed: part.set_payload(str(decrypted)) @@ -309,8 +305,11 @@ class InlineWrapper: out = copy.deepcopy(self.msg) for part in walk(out): if not part.is_multipart(): - payload = str(part.get_payload()) - pmsg = PGPMessage.new(payload) + if self._is_signed(part): + pmsg = PGPMessage.from_blob(part.get_payload()) + else: + payload = str(part.get_payload()) + pmsg = PGPMessage.new(payload) smsg = self._sign(pmsg, key, hash=hash) emsg = self._encrypt(smsg, *keys, cipher=cipher, **kwargs) part.set_payload(str(emsg)) diff --git a/src/mailman_pgp/pgp/mime.py b/src/mailman_pgp/pgp/mime.py index 33ec93e..a1303c9 100644 --- a/src/mailman_pgp/pgp/mime.py +++ b/src/mailman_pgp/pgp/mime.py @@ -24,11 +24,12 @@ from email.mime.application import MIMEApplication from email.utils import collapse_rfc2231_value from mailman.email.message import Message, MultipartDigestMessage -from pgpy import PGPDetachedSignature, PGPKey, PGPMessage +from pgpy import PGPDetachedSignature, PGPMessage from pgpy.constants import HashAlgorithm, SymmetricKeyAlgorithm from public import public from mailman_pgp.utils.email import copy_headers +from mailman_pgp.utils.pgp import key_from_blob @public @@ -179,7 +180,10 @@ class MIMEWrapper: for part in walk(self.msg): if (not part.is_multipart() # noqa and part.get_content_type() == MIMEWrapper._keys_type): - key, _ = PGPKey.from_blob(part.get_payload()) + try: + key = key_from_blob(part.get_payload()) + except: + continue yield key def attach_key(self, key): @@ -215,7 +219,11 @@ class MIMEWrapper: """ clear_text = next(iter(self.get_signed())) signature = next(iter(self.get_signature())) - yield key.verify(clear_text, signature) + try: + verification = key.verify(clear_text, signature) + except: + return + yield verification def _micalg(self, hash_algo): algs = { @@ -281,6 +289,7 @@ class MIMEWrapper: :rtype: mailman.email.message.Message """ pmsg = next(iter(self.get_encrypted())) + # TODO: exception safe this. decrypted = key.decrypt(pmsg) dmsg = decrypted.message diff --git a/src/mailman_pgp/pgp/tests/base.py b/src/mailman_pgp/pgp/tests/base.py deleted file mode 100644 index e16c95e..0000000 --- a/src/mailman_pgp/pgp/tests/base.py +++ /dev/null @@ -1,170 +0,0 @@ -# Copyright (C) 2017 Jan Jancar -# -# This file is a part of the Mailman PGP plugin. -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free -# Software Foundation, either version 3 of the License, or (at your option) -# any later version. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -# more details. -# -# You should have received a copy of the GNU General Public License along with -# this program. If not, see . - -"""""" - -import os -from email import message_from_bytes -from unittest import TestCase - -from mailman.email.message import Message -from pgpy import PGPKey -from pkg_resources import resource_string - -from mailman_pgp.testing.layers import PGPLayer - - -def load_message(path): - data = resource_string('mailman_pgp.pgp.tests', - os.path.join('data', 'messages', path)) - return message_from_bytes(data, Message) - - -def load_key(path): - key, _ = PGPKey.from_blob( - resource_string('mailman_pgp.pgp.tests', - os.path.join('data', 'keys', path))) - return key - - -def payload_equal(one_msg, other_msg): - one_payload = one_msg.get_payload() - other_payload = other_msg.get_payload() - if isinstance(one_payload, list) and isinstance(other_payload, list): - if len(one_payload) != len(other_payload): - return False - for one_inner, other_inner in zip(one_payload, other_payload): - if not payload_equal(one_inner, other_inner): - return False - return True - else: - return one_payload == other_payload - - -class WrapperTestCase(TestCase): - layer = PGPLayer - wrapper = None - maxDiff = None - - def wrap(self, message): - return self.wrapper(message) - - def is_signed(self, message, signed): - wrapped = self.wrap(message) - self.assertEqual(wrapped.is_signed(), signed) - - def has_signature(self, message, has): - wrapped = self.wrap(message) - self.assertEqual(wrapped.has_signature(), has) - - def sign(self, message, key): - wrapped = self.wrap(message) - signed = wrapped.sign(key) - signed_wrapped = self.wrap(signed) - self.assertTrue(signed_wrapped.is_signed()) - - def sign_verify(self, message, priv, pub): - wrapped = self.wrap(message) - signed = wrapped.sign(priv) - signed_wrapped = self.wrap(signed) - for signature in signed_wrapped.verify(pub): - self.assertTrue(bool(signature)) - - def verify(self, message, key, valid): - wrapped = self.wrap(message) - for signature in wrapped.verify(key): - self.assertEqual(bool(signature), valid) - - def is_encrypted(self, message, encrypted): - wrapped = self.wrap(message) - self.assertEqual(wrapped.is_encrypted(), encrypted) - - def has_encryption(self, message, has): - wrapped = self.wrap(message) - self.assertEqual(wrapped.has_encryption(), has) - - def encrypt(self, message, *keys, **kwargs): - wrapped = self.wrap(message) - encrypted = wrapped.encrypt(*keys, **kwargs) - encrypted_wrapped = self.wrap(encrypted) - self.assertTrue(encrypted_wrapped.is_encrypted()) - - def encrypt_decrypt(self, message, pub, priv): - wrapped = self.wrap(message) - encrypted = wrapped.encrypt(pub) - - encrypted_wrapped = self.wrap(encrypted) - decrypted = encrypted_wrapped.decrypt(priv) - decrypted_wrapped = self.wrap(decrypted) - - self.assertFalse(decrypted_wrapped.is_encrypted()) - self.assertTrue(payload_equal(decrypted, message)) - - def decrypt(self, message, key, clear): - wrapped = self.wrap(message) - decrypted = wrapped.decrypt(key) - decrypted_wrapped = self.wrap(decrypted) - - self.assertFalse(decrypted_wrapped.is_encrypted()) - self.assertEqual(decrypted.get_payload(), clear) - - def has_keys(self, message, has_keys): - wrapped = self.wrap(message) - self.assertEqual(wrapped.has_keys(), has_keys) - - def keys(self, message, keys): - wrapped = self.wrap(message) - loaded = list(wrapped.keys()) - self.assertEqual(len(loaded), len(keys)) - - loaded_fingerprints = list(map(lambda key: key.fingerprint, loaded)) - fingerprints = list(map(lambda key: key.fingerprint, keys)) - self.assertListEqual(loaded_fingerprints, fingerprints) - - def sign_encrypt_decrypt_verify(self, message, sign_key, encrypt_key): - wrapped = self.wrap(message) - encrypted = wrapped.sign_encrypt(sign_key, encrypt_key.pubkey) - encrypted_wrapped = self.wrap(encrypted) - self.assertTrue(encrypted_wrapped.is_encrypted()) - - decrypted = encrypted_wrapped.decrypt(encrypt_key) - decrypted_wrapped = self.wrap(decrypted) - self.assertTrue(decrypted_wrapped.is_signed()) - self.assertFalse(decrypted_wrapped.is_encrypted()) - - verification = decrypted_wrapped.verify(sign_key.pubkey) - for sig in verification: - self.assertTrue(bool(sig)) - self.assertListEqual(list(decrypted_wrapped.get_signed()), - list(wrapped.get_payload())) - - def sign_then_encrypt_decrypt_verify(self, message, sign_key, encrypt_key): - wrapped = self.wrap(message) - encrypted = wrapped.sign_then_encrypt(sign_key, encrypt_key.pubkey) - encrypted_wrapped = self.wrap(encrypted) - self.assertTrue(encrypted_wrapped.is_encrypted()) - - decrypted = encrypted_wrapped.decrypt(encrypt_key) - decrypted_wrapped = self.wrap(decrypted) - self.assertTrue(decrypted_wrapped.is_signed()) - self.assertFalse(decrypted_wrapped.is_encrypted()) - - verification = decrypted_wrapped.verify(sign_key.pubkey) - for sig in verification: - self.assertTrue(bool(sig)) - self.assertListEqual(list(decrypted_wrapped.get_signed()), - list(wrapped.get_payload())) diff --git a/src/mailman_pgp/pgp/tests/data/messages/inline_privkey_invalid.eml b/src/mailman_pgp/pgp/tests/data/messages/inline_privkey_invalid.eml new file mode 100644 index 0000000..d7d9063 --- /dev/null +++ b/src/mailman_pgp/pgp/tests/data/messages/inline_privkey_invalid.eml @@ -0,0 +1,43 @@ +To: nobody@example.org +From: RSA 1024b example +Subject: Some subject. +Message-ID: <76a591ed-bfc4-d08b-73d3-fc2489148fd7@example.org> +Date: Wed, 21 Jun 2017 13:50:59 +0200 +User-Agent: Mutt/1.7.2 (2016-11-26) +MIME-Version: 1.0 +Content-Type: text/plain; charset=utf-8 +Content-Transfer-Encoding: 8bit + +-----BEGIN PGP PRIVATE KEY BLOCK----- + +lQHYBFlKWqQBBADY8dCIub6x4tcFS3SmWW1r5ctwldFixkLz8LpBdgpc+zt7iWZI +XvVWH9GYgTzgE+qEAVJcvQDG7/cX6zTopkkGEUmnwBgEFpc7Xi0D+/GYC23XleQk +IF/R2Mf0AW3FBx/UxK0kIkYfTelkuZBrag83iLh3yKXkcLm1T010eGbTWQARAQAB +AAP/ayABKrqZrQpHxfAMEmZ/TzPCIP6BzeX7wqxAtLlehapzJV3kGWF2gQgmPcm3 +byi2eWeTrYKQwytUVsXIswcFcCY1FAAAAAAASqc6VJzGhvjKE9CTNJmgSjHlILMW +Ma/9TzhxH2DT9tNMgOEN3+FoKRqjlLrhO/OiXqYZJdkXU+8CAOQin6DGnn+qvjlN +5zw1GIXXXXX9b1S/WEqKaG1t4D/V0m0pAJBqeUbSC6MonQLA9pmIK/LbetUKRwM/ +xLNRMs8CAPNxR4NY73YIfSnlGRPdZHmOq4wMtqXYtoN1qZruHcXsrojcKHV72n8y +Hb3R+LEKVjky95RKeKGowousIhGYQVcCAKsWtTWQA/VMW2V+ydzFHZHN0xwC9eTc +uhs7G1qAqJR2POCq/0Cr3W4nXnbYSAr9wHal/YUuIKr54vNbPlXp6xqf/rQpUlNB +IDEwMjRiIGV4YW1wbGUgPFJTQS0xMDI0YkBleGFtcGxlLm9yZz6IzgQTAQgAOBYh +BNSpSIaxyiCWpkWOXEfxDAh4hLdYBQJZSlqkAhsDBQsJCAcCBhUICQoLAgQWAgMB +Ah4BAheAAAoJEEfxDAh4hLdYbQIEALBwJUxkBGJYlP5kOZYAgQHw6+rwIgdXAoPk +ldwcGPhvlBEocRYx+5KUYrx7W352dv+AgxSZ590JL7JGvWJDvMxoZGCJiRvJ7+Ve +luKRH167wT8H4hxftBJqzorTU4eyPzG8bFneuag5dw+81DdngXjWRCorEHEdaQjc +sfvVEmnhnQHYBFlKWqQBBADXLjeg+K0ZAuchhNkvMnfb8TlKoc1t1lH94unoqoj3 +L5Srf3XSo1fmuEBNr1nc5BAAAAAAAAAAAAAAAAAAen9uaGvWO0U4t/OUiIWqPjBm +XbjZWDdGKksb0cQNy13cvEBBBBBBBBBBBBBBBBBBlcbopN2wnecKOxLKK9PaLBZ6 +9wARAQABAAP+NyRiYA9p+GCCCCCCCCCCCCCCCCCCAs8x2sB8FcIh8EpSrHIw5OYz +/+2GmQHPIwKVclOfrmW45jRkICvUgZuurMmMrnDu63UT/9Pm+4EiUkPFnKfAgYFm +XwWf2SklsoHCFT2WQ31WCp/hZxWucYqfPM/5ZsE+EAToK+C8BnMicI0CAOAUhruY +JVh/7u6ES7xI6Hd7yHmU6rLr7oP2Z8cILirt5+1FgOP/qPdVEK9UGShbBcjfA+8W +u4LISyuvCm8vuyMCAPXVJmTdmJjzBvP7BZe+HbDzWfhPCN+BvepGi3DQdHonLU2t +IVOcMQsu0raaLevZ19nCKeMrOoAaIhCdjJUBGB0B/iSErQY+sNUAbjsW0zWovY2u +e+0TBMDkO3OZCFKxCr/7mEuIfsAENXvDwAmx/YDklMwN8zgUAc5BqObGtRE7BYSg +n4i2BBgBCAAgFiEE1KlIhrHKIJamRY5cR/EMCHiEt1gFAllKWqQCGwwACgkQR/EM +CHiEt1gOwQP7BuyH2oKuWPAXcVrJdl3P3fIqvm4JzKRjXCVA9j5QOdihH1W5rZFC +x3J+bUvKa1QJ1NuHoU9gYvR2NPs+CpMlAAAAAAksgtddlC9lsVi1ijvjJCugveBh +a/CQJt7Cl6uDkifPsD5kxyUTecmnVfYZB6aFb/nrZxJ5Cb1dKPGixDpw= +=e2aw +-----END PGP PRIVATE KEY BLOCK----- diff --git a/src/mailman_pgp/pgp/tests/data/messages/inline_pubkey_invalid.eml b/src/mailman_pgp/pgp/tests/data/messages/inline_pubkey_invalid.eml new file mode 100644 index 0000000..602a2f6 --- /dev/null +++ b/src/mailman_pgp/pgp/tests/data/messages/inline_pubkey_invalid.eml @@ -0,0 +1,30 @@ +To: nobody@example.org +From: RSA 1024b example +Subject: Some subject. +Message-ID: <76a591ed-bfc4-d08b-73d3-fc2489148fd7@example.org> +Date: Wed, 21 Jun 2017 13:50:59 +0200 +User-Agent: Mutt/1.7.2 (2016-11-26) +MIME-Version: 1.0 +Content-Type: text/plain; charset=utf-8 +Content-Transfer-Encoding: 8bit + +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mI0EWUpapAEEANjx0Ii5vrHi1wVLdKZZbWvly3CV0WLGQvPwukF2Clz7O3uJZkhe +9VYf0ZiBAAAAAAAAUly9AMbv9xfrNOimSQYRSafAGAQWlzteLQP78ZgLbdeV5CQg +X9HYx/QBbcUHH9TErSQiRh9N6WS5kGtqDzeIuHfIpeRwubVPTXR4ZtNZABEBAAG0 +KVJTQSAxMDI0YiBleGFtcGxlIDxSU0EtMTAyNGJAZXhhbXBsZS5vcmc+iM4EEwEI +ADgWIQTUqUiGscoglqZFjlxH8QwIeIS3WAUCWUpapAIbAwULCQgHAgYVCAkKCwIE +FgIDAQIeAQIXgAAKCRBH8QwIeIS3WG0CBACwcCVMZARiWJT+ZDmWAIEB8Ovq8CIH +VwKD5JXcHBj4b5QRKHEWAAAAAAAAAAAAAAAAgIMUmefdCS+yRr1iQ7zMaGRgiYkb +ye/lXpbikR9eu8E/B+IcBBBBBBBBBBBBBBBBvGxZ3rmoOXcPvNQ3Z4F41kQqKxBx +HWkI3LH71RJp4biNBFlKCCCCCCCCCCCCCCCCAuchhNkvMnfb8TlKoc1t1lH94uno +qoj3L5Srf3XSo1fmuEBNr1nc5BLyH0tPDJZyuVCypfY4en9uaGvWO0U4t/OUiIWq +PjBmXbjZWDdGKksb0cQNy13cvE57EmirbuVcXCE0OdU8lcbopN2wnecKOxLKK9Pa +LBZ69wARAQABiLYEGAEIACAWIQTUqUiGscoglqZFjlxH8QwIeIS3WAUCWUpapAIb +DAAKCRBH8QwIeIS3WA7BA/sG7Ifagq5Y8BdxWsl2Xc/d8iq+bgnMpGNcJUD2PlA5 +2KEfVbmtkULHcn5tS8prVAnU24ehT2BAAAAAAA4KkyVMnbCDuSyC112UL2WxWLWK +O+MkK6C94GFr8JAm3sKXq4OSJ8+wPmTHJRN5yadV9hkHoVv+etnEnkJvV0o8aLEO +anA== +=4oxM +-----END PGP PUBLIC KEY BLOCK----- diff --git a/src/mailman_pgp/pgp/tests/data/messages/mime_privkey_invalid.eml b/src/mailman_pgp/pgp/tests/data/messages/mime_privkey_invalid.eml new file mode 100644 index 0000000..25077de --- /dev/null +++ b/src/mailman_pgp/pgp/tests/data/messages/mime_privkey_invalid.eml @@ -0,0 +1,59 @@ +To: nobody@example.org +From: RSA 1024b example +Subject: Some subject. +Message-ID: <76a591ed-bfc4-d08b-73d3-fc2489148fd7@example.org> +Date: Wed, 21 Jun 2017 13:50:59 +0200 +User-Agent: Mutt/1.7.2 (2016-11-26) +MIME-Version: 1.0 +Content-Type: multipart/mixed; + boundary="------------A851F166D50529639139DD0B" + +This is a multi-part message in MIME format. +--------------A851F166D50529639139DD0B +Content-Type: text/plain; charset=utf-8 +Content-Transfer-Encoding: 7bit + +Some other text. + +--------------A851F166D50529639139DD0B +Content-Type: application/pgp-keys; + name="0x7884B758.asc" +Content-Transfer-Encoding: 7bit +Content-Disposition: attachment; + filename="0x7884B758.asc" + +-----BEGIN PGP PRIVATE KEY BLOCK----- + +lQHYBFlKWqQBBADY8dCIub6x4tcFS3SmWW1r5ctwldFixkLz8LpBdgpc+zt7iWZI +XvVWH9GYgTzgE+qEAVJcvQDG7/cX6zTopkkGEUmnwBgEFpc7Xi0D+/GYC23XleQk +IF/R2Mf0AW3FBx/UxK0kIkYfTelkuZBrag83iLh3yKXkcLm1T010eGbTWQARAQAB +AAP/ayABKrqZrQpHxfAMEmZ/TzPCIP6BzeX7wqxAtLlehapzJV3kGWF2gQgmPcm3 +byi2eWeTrYKQwytUVsXIswcFcCY1FAAAAAAASqc6VJzGhvjKE9CTNJmgSjHlILMW +Ma/9TzhxH2DT9tNMgOEN3+FoKRqjlLrhO/OiXqYZJdkXU+8CAOQin6DGnn+qvjlN +5zw1GIXXXXX9b1S/WEqKaG1t4D/V0m0pAJBqeUbSC6MonQLA9pmIK/LbetUKRwM/ +xLNRMs8CAPNxR4NY73YIfSnlGRPdZHmOq4wMtqXYtoN1qZruHcXsrojcKHV72n8y +Hb3R+LEKVjky95RKeKGowousIhGYQVcCAKsWtTWQA/VMW2V+ydzFHZHN0xwC9eTc +uhs7G1qAqJR2POCq/0Cr3W4nXnbYSAr9wHal/YUuIKr54vNbPlXp6xqf/rQpUlNB +IDEwMjRiIGV4YW1wbGUgPFJTQS0xMDI0YkBleGFtcGxlLm9yZz6IzgQTAQgAOBYh +BNSpSIaxyiCWpkWOXEfxDAh4hLdYBQJZSlqkAhsDBQsJCAcCBhUICQoLAgQWAgMB +Ah4BAheAAAoJEEfxDAh4hLdYbQIEALBwJUxkBGJYlP5kOZYAgQHw6+rwIgdXAoPk +ldwcGPhvlBEocRYx+5KUYrx7W352dv+AgxSZ590JL7JGvWJDvMxoZGCJiRvJ7+Ve +luKRH167wT8H4hxftBJqzorTU4eyPzG8bFneuag5dw+81DdngXjWRCorEHEdaQjc +sfvVEmnhnQHYBFlKWqQBBADXLjeg+K0ZAuchhNkvMnfb8TlKoc1t1lH94unoqoj3 +L5Srf3XSo1fmuEBNr1nc5BAAAAAAAAAAAAAAAAAAen9uaGvWO0U4t/OUiIWqPjBm +XbjZWDdGKksb0cQNy13cvEBBBBBBBBBBBBBBBBBBlcbopN2wnecKOxLKK9PaLBZ6 +9wARAQABAAP+NyRiYA9p+GCCCCCCCCCCCCCCCCCCAs8x2sB8FcIh8EpSrHIw5OYz +/+2GmQHPIwKVclOfrmW45jRkICvUgZuurMmMrnDu63UT/9Pm+4EiUkPFnKfAgYFm +XwWf2SklsoHCFT2WQ31WCp/hZxWucYqfPM/5ZsE+EAToK+C8BnMicI0CAOAUhruY +JVh/7u6ES7xI6Hd7yHmU6rLr7oP2Z8cILirt5+1FgOP/qPdVEK9UGShbBcjfA+8W +u4LISyuvCm8vuyMCAPXVJmTdmJjzBvP7BZe+HbDzWfhPCN+BvepGi3DQdHonLU2t +IVOcMQsu0raaLevZ19nCKeMrOoAaIhCdjJUBGB0B/iSErQY+sNUAbjsW0zWovY2u +e+0TBMDkO3OZCFKxCr/7mEuIfsAENXvDwAmx/YDklMwN8zgUAc5BqObGtRE7BYSg +n4i2BBgBCAAgFiEE1KlIhrHKIJamRY5cR/EMCHiEt1gFAllKWqQCGwwACgkQR/EM +CHiEt1gOwQP7BuyH2oKuWPAXcVrJdl3P3fIqvm4JzKRjXCVA9j5QOdihH1W5rZFC +x3J+bUvKa1QJ1NuHoU9gYvR2NPs+CpMlAAAAAAksgtddlC9lsVi1ijvjJCugveBh +a/CQJt7Cl6uDkifPsD5kxyUTecmnVfYZB6aFb/nrZxJ5Cb1dKPGixDpw= +=e2aw +-----END PGP PRIVATE KEY BLOCK----- + +--------------A851F166D50529639139DD0B-- diff --git a/src/mailman_pgp/pgp/tests/data/messages/mime_pubkey_invalid.eml b/src/mailman_pgp/pgp/tests/data/messages/mime_pubkey_invalid.eml new file mode 100644 index 0000000..eecc329 --- /dev/null +++ b/src/mailman_pgp/pgp/tests/data/messages/mime_pubkey_invalid.eml @@ -0,0 +1,46 @@ +To: nobody@example.org +From: RSA 1024b example +Subject: Some subject. +Message-ID: <76a591ed-bfc4-d08b-73d3-fc2489148fd7@example.org> +Date: Wed, 21 Jun 2017 13:50:59 +0200 +User-Agent: Mutt/1.7.2 (2016-11-26) +MIME-Version: 1.0 +Content-Type: multipart/mixed; + boundary="------------A851F166D50529639139DD0B" + +This is a multi-part message in MIME format. +--------------A851F166D50529639139DD0B +Content-Type: text/plain; charset=utf-8 +Content-Transfer-Encoding: 7bit + +Some other text. + +--------------A851F166D50529639139DD0B +Content-Type: application/pgp-keys; + name="0x7884B758.asc" +Content-Transfer-Encoding: 7bit +Content-Disposition: attachment; + filename="0x7884B758.asc" + +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mI0EWUpapAEEANjx0Ii5vrHi1wVLdKZZbWvly3CV0WLGQvPwukF2Clz7O3uJZkhe +9VYf0ZiBAAAAAAAAUly9AMbv9xfrNOimSQYRSafAGAQWlzteLQP78ZgLbdeV5CQg +X9HYx/QBbcUHH9TErSQiRh9N6WS5kGtqDzeIuHfIpeRwubVPTXR4ZtNZABEBAAG0 +KVJTQSAxMDI0YiBleGFtcGxlIDxSU0EtMTAyNGJAZXhhbXBsZS5vcmc+iM4EEwEI +ADgWIQTUqUiGscoglqZFjlxH8QwIeIS3WAUCWUpapAIbAwULCQgHAgYVCAkKCwIE +FgIDAQIeAQIXgAAKCRBH8QwIeIS3WG0CBACwcCVMZARiWJT+ZDmWAIEB8Ovq8CIH +VwKD5JXcHBj4b5QRKHEWAAAAAAAAAAAAAAAAgIMUmefdCS+yRr1iQ7zMaGRgiYkb +ye/lXpbikR9eu8E/B+IcBBBBBBBBBBBBBBBBvGxZ3rmoOXcPvNQ3Z4F41kQqKxBx +HWkI3LH71RJp4biNBFlKCCCCCCCCCCCCCCCCAuchhNkvMnfb8TlKoc1t1lH94uno +qoj3L5Srf3XSo1fmuEBNr1nc5BLyH0tPDJZyuVCypfY4en9uaGvWO0U4t/OUiIWq +PjBmXbjZWDdGKksb0cQNy13cvE57EmirbuVcXCE0OdU8lcbopN2wnecKOxLKK9Pa +LBZ69wARAQABiLYEGAEIACAWIQTUqUiGscoglqZFjlxH8QwIeIS3WAUCWUpapAIb +DAAKCRBH8QwIeIS3WA7BA/sG7Ifagq5Y8BdxWsl2Xc/d8iq+bgnMpGNcJUD2PlA5 +2KEfVbmtkULHcn5tS8prVAnU24ehT2BAAAAAAA4KkyVMnbCDuSyC112UL2WxWLWK +O+MkK6C94GFr8JAm3sKXq4OSJ8+wPmTHJRN5yadV9hkHoVv+etnEnkJvV0o8aLEO +anA== +=4oxM +-----END PGP PUBLIC KEY BLOCK----- + +--------------A851F166D50529639139DD0B-- diff --git a/src/mailman_pgp/pgp/tests/test_inline.py b/src/mailman_pgp/pgp/tests/test_inline.py index e2fccb7..4009a4b 100644 --- a/src/mailman_pgp/pgp/tests/test_inline.py +++ b/src/mailman_pgp/pgp/tests/test_inline.py @@ -20,7 +20,7 @@ from parameterized import parameterized from mailman_pgp.pgp.inline import InlineWrapper -from mailman_pgp.pgp.tests.base import load_key, load_message, WrapperTestCase +from mailman_pgp.testing.pgp import load_key, load_message, WrapperTestCase class InlineWrapperTestCase(WrapperTestCase): @@ -181,6 +181,10 @@ class TestKeys(InlineWrapperTestCase): (load_message('clear_multipart.eml'), False), (load_message('inline_cleartext_signed.eml'), + False), + (load_message('inline_privkey_invalid.eml'), + False), + (load_message('inline_pubkey_invalid.eml'), False) ]) def test_has_keys(self, message, has_keys): @@ -190,7 +194,11 @@ class TestKeys(InlineWrapperTestCase): (load_message('inline_privkey.eml'), [load_key('rsa_1024.priv.asc')]), (load_message('inline_pubkey.eml'), - [load_key('rsa_1024.pub.asc')]) + [load_key('rsa_1024.pub.asc')]), + (load_message('inline_privkey_invalid.eml'), + []), + (load_message('inline_pubkey_invalid.eml'), + []) ]) def test_keys(self, message, keys): self.keys(message, keys) diff --git a/src/mailman_pgp/pgp/tests/test_mime.py b/src/mailman_pgp/pgp/tests/test_mime.py index 5d983b8..883e6ca 100644 --- a/src/mailman_pgp/pgp/tests/test_mime.py +++ b/src/mailman_pgp/pgp/tests/test_mime.py @@ -20,7 +20,7 @@ from parameterized import parameterized from mailman_pgp.pgp.mime import MIMEWrapper -from mailman_pgp.pgp.tests.base import load_key, load_message, WrapperTestCase +from mailman_pgp.testing.pgp import load_key, load_message, WrapperTestCase class MIMEWrapperTestCase(WrapperTestCase): @@ -144,6 +144,10 @@ class TestKeys(MIMEWrapperTestCase): True), (load_message('mime_pubkey.eml'), True), + (load_message('mime_privkey_invalid.eml'), + True), + (load_message('mime_pubkey_invalid.eml'), + True), (load_message('clear.eml'), False), (load_message('mime_signed.eml'), @@ -156,7 +160,11 @@ class TestKeys(MIMEWrapperTestCase): (load_message('mime_privkey.eml'), [load_key('rsa_1024.priv.asc')]), (load_message('mime_pubkey.eml'), - [load_key('rsa_1024.pub.asc')]) + [load_key('rsa_1024.pub.asc')]), + (load_message('mime_privkey_invalid.eml'), + []), + (load_message('mime_pubkey_invalid.eml'), + []) ]) def test_keys(self, message, keys): self.keys(message, keys) diff --git a/src/mailman_pgp/pgp/tests/test_mime_multisig.py b/src/mailman_pgp/pgp/tests/test_mime_multisig.py index 2c02a97..fe08e5b 100644 --- a/src/mailman_pgp/pgp/tests/test_mime_multisig.py +++ b/src/mailman_pgp/pgp/tests/test_mime_multisig.py @@ -19,7 +19,7 @@ from parameterized import parameterized from mailman_pgp.pgp.mime_multisig import MIMEMultiSigWrapper -from mailman_pgp.pgp.tests.base import load_key, load_message, WrapperTestCase +from mailman_pgp.testing.pgp import load_key, load_message, WrapperTestCase class MultiSigWrapperTestCase(WrapperTestCase): diff --git a/src/mailman_pgp/pgp/tests/test_pgp.py b/src/mailman_pgp/pgp/tests/test_pgp.py index 6ff674d..ab2a69a 100644 --- a/src/mailman_pgp/pgp/tests/test_pgp.py +++ b/src/mailman_pgp/pgp/tests/test_pgp.py @@ -25,8 +25,8 @@ from mailman_pgp.config import config from mailman_pgp.database import mm_transaction, transaction from mailman_pgp.model.address import PGPAddress from mailman_pgp.model.list import PGPMailingList -from mailman_pgp.pgp.tests.base import load_key from mailman_pgp.testing.layers import PGPConfigLayer +from mailman_pgp.testing.pgp import load_key class TestPGP(TestCase): diff --git a/src/mailman_pgp/pgp/tests/test_wrapper.py b/src/mailman_pgp/pgp/tests/test_wrapper.py index ce206e7..eb096d3 100644 --- a/src/mailman_pgp/pgp/tests/test_wrapper.py +++ b/src/mailman_pgp/pgp/tests/test_wrapper.py @@ -18,8 +18,8 @@ """Tests for the combined wrapper.""" from parameterized import parameterized -from mailman_pgp.pgp.tests.base import load_key, load_message, WrapperTestCase from mailman_pgp.pgp.wrapper import PGPWrapper +from mailman_pgp.testing.pgp import load_key, load_message, WrapperTestCase class PGPWrapperTestCase(WrapperTestCase): diff --git a/src/mailman_pgp/rest/tests/test_lists.py b/src/mailman_pgp/rest/tests/test_lists.py index 6509c04..381d4ac 100644 --- a/src/mailman_pgp/rest/tests/test_lists.py +++ b/src/mailman_pgp/rest/tests/test_lists.py @@ -26,8 +26,8 @@ from pgpy import PGPKey from mailman_pgp.database import mm_transaction, transaction from mailman_pgp.model.list import PGPMailingList -from mailman_pgp.pgp.tests.base import load_key from mailman_pgp.testing.layers import PGPRESTLayer +from mailman_pgp.testing.pgp import load_key class TestLists(TestCase): diff --git a/src/mailman_pgp/rules/tests/test_signature.py b/src/mailman_pgp/rules/tests/test_signature.py index 24cb5e9..c6bcb16 100644 --- a/src/mailman_pgp/rules/tests/test_signature.py +++ b/src/mailman_pgp/rules/tests/test_signature.py @@ -34,10 +34,10 @@ from mailman_pgp.database import mm_transaction, transaction from mailman_pgp.model.address import PGPAddress from mailman_pgp.model.list import PGPMailingList from mailman_pgp.model.sighash import PGPSigHash -from mailman_pgp.pgp.tests.base import load_key, load_message from mailman_pgp.pgp.wrapper import PGPWrapper from mailman_pgp.rules.signature import Signature from mailman_pgp.testing.layers import PGPConfigLayer +from mailman_pgp.testing.pgp import load_key, load_message from mailman_pgp.utils.pgp import hashes diff --git a/src/mailman_pgp/runners/tests/test_incoming.py b/src/mailman_pgp/runners/tests/test_incoming.py index be82c61..b03f149 100644 --- a/src/mailman_pgp/runners/tests/test_incoming.py +++ b/src/mailman_pgp/runners/tests/test_incoming.py @@ -29,10 +29,10 @@ from mailman_pgp.config import mm_config from mailman_pgp.database import mm_transaction, transaction from mailman_pgp.model.address import PGPAddress from mailman_pgp.model.list import PGPMailingList -from mailman_pgp.pgp.tests.base import load_key, load_message from mailman_pgp.pgp.wrapper import PGPWrapper from mailman_pgp.runners.incoming import PGPIncomingRunner from mailman_pgp.testing.layers import PGPConfigLayer +from mailman_pgp.testing.pgp import load_key, load_message class TestPGPIncomingRunner(TestCase): diff --git a/src/mailman_pgp/testing/pgp.py b/src/mailman_pgp/testing/pgp.py new file mode 100644 index 0000000..e16c95e --- /dev/null +++ b/src/mailman_pgp/testing/pgp.py @@ -0,0 +1,170 @@ +# Copyright (C) 2017 Jan Jancar +# +# This file is a part of the Mailman PGP plugin. +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along with +# this program. If not, see . + +"""""" + +import os +from email import message_from_bytes +from unittest import TestCase + +from mailman.email.message import Message +from pgpy import PGPKey +from pkg_resources import resource_string + +from mailman_pgp.testing.layers import PGPLayer + + +def load_message(path): + data = resource_string('mailman_pgp.pgp.tests', + os.path.join('data', 'messages', path)) + return message_from_bytes(data, Message) + + +def load_key(path): + key, _ = PGPKey.from_blob( + resource_string('mailman_pgp.pgp.tests', + os.path.join('data', 'keys', path))) + return key + + +def payload_equal(one_msg, other_msg): + one_payload = one_msg.get_payload() + other_payload = other_msg.get_payload() + if isinstance(one_payload, list) and isinstance(other_payload, list): + if len(one_payload) != len(other_payload): + return False + for one_inner, other_inner in zip(one_payload, other_payload): + if not payload_equal(one_inner, other_inner): + return False + return True + else: + return one_payload == other_payload + + +class WrapperTestCase(TestCase): + layer = PGPLayer + wrapper = None + maxDiff = None + + def wrap(self, message): + return self.wrapper(message) + + def is_signed(self, message, signed): + wrapped = self.wrap(message) + self.assertEqual(wrapped.is_signed(), signed) + + def has_signature(self, message, has): + wrapped = self.wrap(message) + self.assertEqual(wrapped.has_signature(), has) + + def sign(self, message, key): + wrapped = self.wrap(message) + signed = wrapped.sign(key) + signed_wrapped = self.wrap(signed) + self.assertTrue(signed_wrapped.is_signed()) + + def sign_verify(self, message, priv, pub): + wrapped = self.wrap(message) + signed = wrapped.sign(priv) + signed_wrapped = self.wrap(signed) + for signature in signed_wrapped.verify(pub): + self.assertTrue(bool(signature)) + + def verify(self, message, key, valid): + wrapped = self.wrap(message) + for signature in wrapped.verify(key): + self.assertEqual(bool(signature), valid) + + def is_encrypted(self, message, encrypted): + wrapped = self.wrap(message) + self.assertEqual(wrapped.is_encrypted(), encrypted) + + def has_encryption(self, message, has): + wrapped = self.wrap(message) + self.assertEqual(wrapped.has_encryption(), has) + + def encrypt(self, message, *keys, **kwargs): + wrapped = self.wrap(message) + encrypted = wrapped.encrypt(*keys, **kwargs) + encrypted_wrapped = self.wrap(encrypted) + self.assertTrue(encrypted_wrapped.is_encrypted()) + + def encrypt_decrypt(self, message, pub, priv): + wrapped = self.wrap(message) + encrypted = wrapped.encrypt(pub) + + encrypted_wrapped = self.wrap(encrypted) + decrypted = encrypted_wrapped.decrypt(priv) + decrypted_wrapped = self.wrap(decrypted) + + self.assertFalse(decrypted_wrapped.is_encrypted()) + self.assertTrue(payload_equal(decrypted, message)) + + def decrypt(self, message, key, clear): + wrapped = self.wrap(message) + decrypted = wrapped.decrypt(key) + decrypted_wrapped = self.wrap(decrypted) + + self.assertFalse(decrypted_wrapped.is_encrypted()) + self.assertEqual(decrypted.get_payload(), clear) + + def has_keys(self, message, has_keys): + wrapped = self.wrap(message) + self.assertEqual(wrapped.has_keys(), has_keys) + + def keys(self, message, keys): + wrapped = self.wrap(message) + loaded = list(wrapped.keys()) + self.assertEqual(len(loaded), len(keys)) + + loaded_fingerprints = list(map(lambda key: key.fingerprint, loaded)) + fingerprints = list(map(lambda key: key.fingerprint, keys)) + self.assertListEqual(loaded_fingerprints, fingerprints) + + def sign_encrypt_decrypt_verify(self, message, sign_key, encrypt_key): + wrapped = self.wrap(message) + encrypted = wrapped.sign_encrypt(sign_key, encrypt_key.pubkey) + encrypted_wrapped = self.wrap(encrypted) + self.assertTrue(encrypted_wrapped.is_encrypted()) + + decrypted = encrypted_wrapped.decrypt(encrypt_key) + decrypted_wrapped = self.wrap(decrypted) + self.assertTrue(decrypted_wrapped.is_signed()) + self.assertFalse(decrypted_wrapped.is_encrypted()) + + verification = decrypted_wrapped.verify(sign_key.pubkey) + for sig in verification: + self.assertTrue(bool(sig)) + self.assertListEqual(list(decrypted_wrapped.get_signed()), + list(wrapped.get_payload())) + + def sign_then_encrypt_decrypt_verify(self, message, sign_key, encrypt_key): + wrapped = self.wrap(message) + encrypted = wrapped.sign_then_encrypt(sign_key, encrypt_key.pubkey) + encrypted_wrapped = self.wrap(encrypted) + self.assertTrue(encrypted_wrapped.is_encrypted()) + + decrypted = encrypted_wrapped.decrypt(encrypt_key) + decrypted_wrapped = self.wrap(decrypted) + self.assertTrue(decrypted_wrapped.is_signed()) + self.assertFalse(decrypted_wrapped.is_encrypted()) + + verification = decrypted_wrapped.verify(sign_key.pubkey) + for sig in verification: + self.assertTrue(bool(sig)) + self.assertListEqual(list(decrypted_wrapped.get_signed()), + list(wrapped.get_payload())) diff --git a/src/mailman_pgp/workflows/tests/test_base.py b/src/mailman_pgp/workflows/tests/test_base.py index 109ece2..31b3d05 100644 --- a/src/mailman_pgp/workflows/tests/test_base.py +++ b/src/mailman_pgp/workflows/tests/test_base.py @@ -33,9 +33,9 @@ from zope.interface import implementer from mailman_pgp.database import mm_transaction, transaction from mailman_pgp.model.address import PGPAddress from mailman_pgp.model.list import PGPMailingList -from mailman_pgp.pgp.tests.base import load_key from mailman_pgp.pgp.wrapper import PGPWrapper from mailman_pgp.testing.layers import PGPConfigLayer +from mailman_pgp.testing.pgp import load_key from mailman_pgp.workflows.base import (PGPMixin) from mailman_pgp.workflows.pubkey import (ConfirmPubkeyMixin, KEY_REQUEST, SetPubkeyMixin) diff --git a/src/mailman_pgp/workflows/tests/test_key_change.py b/src/mailman_pgp/workflows/tests/test_key_change.py index 8efeb58..e469d51 100644 --- a/src/mailman_pgp/workflows/tests/test_key_change.py +++ b/src/mailman_pgp/workflows/tests/test_key_change.py @@ -28,9 +28,9 @@ from zope.component import getUtility from mailman_pgp.database import mm_transaction, transaction from mailman_pgp.model.address import PGPAddress from mailman_pgp.model.list import PGPMailingList -from mailman_pgp.pgp.tests.base import load_key from mailman_pgp.pgp.wrapper import PGPWrapper from mailman_pgp.testing.layers import PGPConfigLayer +from mailman_pgp.testing.pgp import load_key from mailman_pgp.workflows.key_change import KeyChangeWorkflow -- cgit v1.2.3-70-g09d2