diff options
| -rw-r--r-- | src/mailman_pgp/pgp/inline.py | 178 | ||||
| -rw-r--r-- | src/mailman_pgp/pgp/mime.py | 27 | ||||
| -rw-r--r-- | src/mailman_pgp/pgp/tests/base.py | 49 | ||||
| -rw-r--r-- | src/mailman_pgp/pgp/tests/data/dsa_elgamal_1024.priv.asc | 26 | ||||
| -rw-r--r-- | src/mailman_pgp/pgp/tests/data/dsa_elgamal_1024.pub.asc | 25 | ||||
| -rw-r--r-- | src/mailman_pgp/pgp/tests/data/ecc_p256.priv.asc | 16 | ||||
| -rw-r--r-- | src/mailman_pgp/pgp/tests/data/ecc_p256.pub.asc | 15 | ||||
| -rw-r--r-- | src/mailman_pgp/pgp/tests/test_inline.py | 46 | ||||
| -rw-r--r-- | src/mailman_pgp/pgp/utils.py | 17 | ||||
| -rw-r--r-- | src/mailman_pgp/pgp/wrapper.py | 19 |
10 files changed, 357 insertions, 61 deletions
diff --git a/src/mailman_pgp/pgp/inline.py b/src/mailman_pgp/pgp/inline.py index 94091cc..a327ef4 100644 --- a/src/mailman_pgp/pgp/inline.py +++ b/src/mailman_pgp/pgp/inline.py @@ -16,8 +16,11 @@ # this program. If not, see <http://www.gnu.org/licenses/>. """Strict inline PGP message wrapper.""" +import copy +from email.iterators import walk from pgpy import PGPKey, PGPMessage +from pgpy.constants import SymmetricKeyAlgorithm from pgpy.types import Armorable from public import public @@ -35,54 +38,80 @@ class InlineWrapper: """ self.msg = msg - def _is_inline(self): - return not self.msg.is_multipart() + def _walk(self, walk_fn, *args, **kwargs): + for part in walk(self.msg): + if not part.is_multipart(): + yield walk_fn(part, *args, **kwargs) - def _as_string(self): - return str(self.msg.get_payload()) - - def _has_signature(self): + def _is_signed(self, part): try: - msg = PGPMessage.from_blob(self._as_string()) + msg = PGPMessage.from_blob(part.get_payload()) return msg.is_signed except: pass return False - def _has_message(self): + def is_signed(self): + """ + Whether the message is inline signed. + + :return: If the message is inline signed. + :rtype: bool + """ + return all(self._walk(self._is_signed)) + + def has_signature(self): + """ + Whether some parts of the message are inline signed. + + :return: If some parts of the message are inline signed. + :rtype: bool + """ + return any(self._walk(self._is_signed)) + + def _is_encrypted(self, part): try: - msg = PGPMessage.from_blob(self._as_string()) + msg = PGPMessage.from_blob(part.get_payload()) return msg.is_encrypted except: pass return False - def _has_armor(self, block_type): - try: - dearm = Armorable.ascii_unarmor(self._as_string()) - if dearm['magic'] == block_type: - return True - except: - pass - return False + def is_encrypted(self): + """ + Whether the message is inline encrypted. - def is_signed(self): + :return: If the message is inline encrypted. + :rtype: bool """ - Whether the message is inline signed (cleartext). + return all(self._walk(self._is_encrypted)) - :return: If the message is inline signed. + def has_encryption(self): + """ + Whether some parts of the message are inline encrypted. + + :return: If some parts of the message are inline encrypted. :rtype: bool """ - return self._is_inline() and self._has_signature() + return any(self._walk(self._is_encrypted)) - def is_encrypted(self): + 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 + except: + pass + return False + + def is_keys(self): """ - Whether the message is inline encrypted. + Whether the message is all keys (all parts). - :return: If the message is inline encrypted. + :return: If the message is keys. :rtype: bool """ - return self._is_inline() and self._has_message() + return all(self._walk(self._has_keys)) def has_keys(self): """ @@ -91,8 +120,15 @@ class InlineWrapper: :return: If the message contains keys. :rtype: bool """ - return self._is_inline() and (self._has_armor('PUBLIC KEY BLOCK') or - self._has_armor('PRIVATE KEY BLOCK')) + return any(self._walk(self._has_keys)) + + def _keys(self, part): + try: + # TODO: potentially return all things returned from from_blob? + key, _ = PGPKey.from_blob(part.get_payload()) + return key + except: + pass def keys(self): """ @@ -100,9 +136,15 @@ class InlineWrapper: :return: A collection of keys. """ - # TODO: potentially return all things returned from from_blob? - key, _ = PGPKey.from_blob(self._as_string()) - yield key + yield from self._walk(self._keys) + + def _verify(self, part, key): + try: + message = PGPMessage.from_blob(part.get_payload()) + return key.verify(message) + except: + pass + return False def verify(self, key): """ @@ -110,14 +152,38 @@ class InlineWrapper: :param key: The key to verify with. :type key: pgpy.PGPKey - :return: The verified signature. + :return: The verified signatures. :rtype: pgpy.types.SignatureVerification """ - message = PGPMessage.from_blob(self._as_string()) - return key.verify(message) + for part in walk(self.msg): + if not part.is_multipart() and self._is_signed(part): + yield self._verify(part, key) + + def _sign(self, part, key): + payload = str(part.get_payload()) + pmsg = PGPMessage.new(payload, cleartext=True) + pmsg |= key.sign(pmsg) + part.set_payload(str(pmsg)) + + def sign(self, key): + """ + Sign a message with key. + + :param key: The key to sign with. + :type key: pgpy.PGPKey + :return: The signed message. + :rtype: mailman.email.message.Message + """ + out = copy.deepcopy(self.msg) + for part in walk(out): + if not part.is_multipart(): + self._sign(part, key) + return out - def sign(self): - pass + def _decrypt(self, part, key): + message = PGPMessage.from_blob(part.get_payload()) + decrypted = key.decrypt(message) + part.set_payload(decrypted.message) def decrypt(self, key): """ @@ -126,10 +192,42 @@ class InlineWrapper: :param key: The key to decrypt with. :type key: pgpy.PGPKey :return: The decrypted message. - :rtype: PGPMessage + :rtype: mailman.email.message.Message + """ + out = copy.deepcopy(self.msg) + for part in walk(out): + if not part.is_multipart() and self._is_encrypted(part): + self._decrypt(part, key) + return out + + def _encrypt(self, part, *keys, **kwargs): + payload = str(part.get_payload()) + pmsg = PGPMessage.new(payload) + for key in keys: + pmsg = key.encrypt(pmsg, **kwargs) + part.set_payload(str(pmsg)) + + def encrypt(self, *keys, cipher=SymmetricKeyAlgorithm.AES256): + """ + Encrypt the message with key/s, using cipher. + + :param keys: The key/s to encrypt with. + :type keys: pgpy.PGPKey + :param cipher: The symmetric cipher to use. + :type cipher: SymmetricKeyAlgorithm + :return: mailman.email.message.Message """ - message = PGPMessage.from_blob(self._as_string()) - return key.decrypt(message) + if len(keys) == 0: + raise ValueError('At least one key necessary.') - def encrypt(self): - pass + out = copy.deepcopy(self.msg) + for part in walk(out): + if not part.is_multipart(): + if len(keys) == 1: + self._encrypt(part, *keys) + else: + session_key = cipher.gen_key() + for key in keys: + self._encrypt(part, key, session_key=session_key) + del session_key + return out diff --git a/src/mailman_pgp/pgp/mime.py b/src/mailman_pgp/pgp/mime.py index 3421167..3a20ce3 100644 --- a/src/mailman_pgp/pgp/mime.py +++ b/src/mailman_pgp/pgp/mime.py @@ -20,6 +20,7 @@ from email.iterators import walk from email.utils import collapse_rfc2231_value from pgpy import PGPKey, PGPMessage, PGPSignature +from pgpy.constants import SymmetricKeyAlgorithm from public import public @@ -84,6 +85,12 @@ class MIMEWrapper: content_subtype == 'encrypted' and protocol_param == MIMEWrapper._encrypted_subtype) + def is_keys(self): + for part in walk(self.msg): + if part.get_content_type() != MIMEWrapper._keys_subtype: + return False + return True + def has_keys(self): """ Whether the message contains keys as per RFC3156 section 7. @@ -119,9 +126,15 @@ class MIMEWrapper: clear_text = self.msg.get_payload(0).as_string() sig_text = self.msg.get_payload(1).get_payload() signature = PGPSignature.from_blob(sig_text) - return key.verify(clear_text, signature) + yield key.verify(clear_text, signature) - def sign(self): + def sign(self, key): + """ + + :param key: + :type key: pgpy.PGPKey + :return: + """ pass def decrypt(self, key): @@ -137,5 +150,13 @@ class MIMEWrapper: msg = PGPMessage.from_blob(msg_text) return key.decrypt(msg) - def encrypt(self): + def encrypt(self, *keys, cipher=SymmetricKeyAlgorithm.AES256): + """ + + :param keys: + :type keys: pgpy.PGPKey + :param cipher: + :type cipher: SymmetricKeyAlgorithm + :return: + """ pass diff --git a/src/mailman_pgp/pgp/tests/base.py b/src/mailman_pgp/pgp/tests/base.py index ccdf7dd..8a5473d 100644 --- a/src/mailman_pgp/pgp/tests/base.py +++ b/src/mailman_pgp/pgp/tests/base.py @@ -41,27 +41,52 @@ class WrapperTestCase(TestCase): wrapped = self.wrapper(message) self.assertEqual(wrapped.is_signed(), signed) + def sign(self, message, key): + wrapped = self.wrapper(message) + signed = wrapped.sign(key) + signed_wrapped = self.wrapper(signed) + self.assertTrue(signed_wrapped.is_signed()) + + def sign_verify(self, message, priv, pub): + wrapped = self.wrapper(message) + signed = wrapped.sign(priv) + signed_wrapped = self.wrapper(signed) + for signature in signed_wrapped.verify(pub): + self.assertTrue(bool(signature)) + def verify(self, message, key, valid): wrapped = self.wrapper(message) - verified = False - try: - verified = bool(wrapped.verify(key)) - except: - pass - self.assertEqual(verified, valid) + for signature in wrapped.verify(key): + self.assertEqual(bool(signature), valid) def is_encrypted(self, message, encrypted): wrapped = self.wrapper(message) self.assertEqual(wrapped.is_encrypted(), encrypted) + def encrypt(self, message, *keys, **kwargs): + wrapped = self.wrapper(message) + encrypted = wrapped.encrypt(*keys, **kwargs) + encrypted_wrapped = self.wrapper(encrypted) + self.assertTrue(encrypted_wrapped.is_encrypted()) + + def encrypt_decrypt(self, message, pub, priv): + wrapped = self.wrapper(message) + encrypted = wrapped.encrypt(pub) + + encrypted_wrapped = self.wrapper(encrypted) + decrypted = encrypted_wrapped.decrypt(priv) + decrypted_wrapped = self.wrapper(decrypted) + + self.assertFalse(decrypted_wrapped.is_encrypted()) + self.assertEqual(decrypted.get_payload(), message.get_payload()) + def decrypt(self, message, key, clear): wrapped = self.wrapper(message) - decrypted = None - try: - decrypted = wrapped.decrypt(key).message - except: - pass - self.assertEqual(decrypted, bytearray(clear, 'latin-1')) + decrypted = wrapped.decrypt(key) + decrypted_wrapped = self.wrapper(decrypted) + + self.assertFalse(decrypted_wrapped.is_encrypted()) + self.assertEqual(decrypted.get_payload(), clear) def has_keys(self, message, has_keys): wrapped = self.wrapper(message) diff --git a/src/mailman_pgp/pgp/tests/data/dsa_elgamal_1024.priv.asc b/src/mailman_pgp/pgp/tests/data/dsa_elgamal_1024.priv.asc new file mode 100644 index 0000000..34ed61d --- /dev/null +++ b/src/mailman_pgp/pgp/tests/data/dsa_elgamal_1024.priv.asc @@ -0,0 +1,26 @@ +-----BEGIN PGP PRIVATE KEY BLOCK----- + +lQG7BFlO2csRBACVXwZnBCY9CasnsMhHTsLmE1iHd03SDS+2Kz8AWV5AsttjaO9T +Y18PlZwfiy+Brw3x09/5tK9sgeFBwSrb137VQ3Tm37sQBLpDfGYL5ghi2/wiApMF +MHml/UXzU2P9jZMmTHdferxMiNVk/9si0qUUgfXR/KCV/LwJo9L20SIQTwCgk56w +Fpxgm4Yxy5OshgN71WXaS2kD/3LMp7JSUjVeiP0et8WfaR4O4hzAI0FDFTN+dU/l +RMxqX7OEAgBf/Jl35jK9JOv4zVkSGJb9zCHr/xeN/mEQYvnn0ZH1nGJ1/yZfJ4zz +Cmdu7x8IMLVbC5DWQigE3PdSXV0Mc5ynlwuuwyzC6rLzfCab1qERJMwwPLZ0+RJJ ++znBA/9s+1cXE28nNhsR94MufefNP7gmRUysAtWoJ7tHrPJw8w5tmyP0VjDetCp1 +bgjnulkXPnQVKliMxo6M6bc0i47aHT4bTxHoeiHPg4wXAfEoECgLT+LhEqUF0Kzl +p0JQQskUFxgutOnIsUVQ3ZtFFv+EowcEtP1pel5eK7I7xnL/KgAAn2uJiDSpdUMa +4fR2XL7Z5yy0ZkIHCn60N0RTQSAxMDI0ICYgRWxHYW1hbCAxMDI0IDxEU0EtRWxH +YW1hbC0xMDI0YkBleGFtcGxlLm9yZz6IeAQTEQIAOBYhBFHl8YQSvA1Dyd69k8PM +CgY4QcenBQJZTtnLAhsDBQsJCAcCBhUICQoLAgQWAgMBAh4BAheAAAoJEMPMCgY4 +QcenNxMAnR2umA2PPnWsj1+qFXC48CytVSphAJ9cyTZzRlvXUekPTrDhL+hAsxvP +j50BMgRZTtnLEAQAnnjkCf4X2hsJuXXkEk7XMHh489XiVH6QLVdWGdoIOngWPQV9 +RHhyLYeJ6s4+ir4Jl7syG1wWrPnGftAZ6NpWDDEwTzRRsdvolkgaWA/ITQhfA50r +Lu/tUjYDb8K7iw61UaxwDdEBmZbBf06mup6l5vUQ4cJG4YAH3TctLhiqO0MAAwUD +/3NtgV6aXlHDHcHuyz4iTNwxgZITwYcXFaVdglbBP49IDU0184vuXdd81tpy8sjU +6kotXX0o3I0p1G+1Su2RsYQ5S7PQhOGkaS6UtzMGMI/hvVtvrTChkP0uX2Q9FUF2 +kIgCnNewOamjsrol9JBzt/rr9qDF0B5YMZriI9gS2JvgAAD5AZnxfwraRmuXUhYX +ZA+cdRYj7j3b4vjDWYWCaJfGryYQCIhgBBgRAgAgFiEEUeXxhBK8DUPJ3r2Tw8wK +BjhBx6cFAllO2csCGwwACgkQw8wKBjhBx6cAAQCbBfP32czelOrJKJ+62wrGIkxg +UIkAniXeyeJZEU3CPDns/d2MZn4iVEwY +=Dzmm +-----END PGP PRIVATE KEY BLOCK----- diff --git a/src/mailman_pgp/pgp/tests/data/dsa_elgamal_1024.pub.asc b/src/mailman_pgp/pgp/tests/data/dsa_elgamal_1024.pub.asc new file mode 100644 index 0000000..51625d9 --- /dev/null +++ b/src/mailman_pgp/pgp/tests/data/dsa_elgamal_1024.pub.asc @@ -0,0 +1,25 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQGiBFlO2csRBACVXwZnBCY9CasnsMhHTsLmE1iHd03SDS+2Kz8AWV5AsttjaO9T +Y18PlZwfiy+Brw3x09/5tK9sgeFBwSrb137VQ3Tm37sQBLpDfGYL5ghi2/wiApMF +MHml/UXzU2P9jZMmTHdferxMiNVk/9si0qUUgfXR/KCV/LwJo9L20SIQTwCgk56w +Fpxgm4Yxy5OshgN71WXaS2kD/3LMp7JSUjVeiP0et8WfaR4O4hzAI0FDFTN+dU/l +RMxqX7OEAgBf/Jl35jK9JOv4zVkSGJb9zCHr/xeN/mEQYvnn0ZH1nGJ1/yZfJ4zz +Cmdu7x8IMLVbC5DWQigE3PdSXV0Mc5ynlwuuwyzC6rLzfCab1qERJMwwPLZ0+RJJ ++znBA/9s+1cXE28nNhsR94MufefNP7gmRUysAtWoJ7tHrPJw8w5tmyP0VjDetCp1 +bgjnulkXPnQVKliMxo6M6bc0i47aHT4bTxHoeiHPg4wXAfEoECgLT+LhEqUF0Kzl +p0JQQskUFxgutOnIsUVQ3ZtFFv+EowcEtP1pel5eK7I7xnL/KrQ3RFNBIDEwMjQg +JiBFbEdhbWFsIDEwMjQgPERTQS1FbEdhbWFsLTEwMjRiQGV4YW1wbGUub3JnPoh4 +BBMRAgA4FiEEUeXxhBK8DUPJ3r2Tw8wKBjhBx6cFAllO2csCGwMFCwkIBwIGFQgJ +CgsCBBYCAwECHgECF4AACgkQw8wKBjhBx6c3EwCdHa6YDY8+dayPX6oVcLjwLK1V +KmEAn1zJNnNGW9dR6Q9OsOEv6ECzG8+PuQENBFlO2csQBACeeOQJ/hfaGwm5deQS +TtcweHjz1eJUfpAtV1YZ2gg6eBY9BX1EeHIth4nqzj6KvgmXuzIbXBas+cZ+0Bno +2lYMMTBPNFGx2+iWSBpYD8hNCF8DnSsu7+1SNgNvwruLDrVRrHAN0QGZlsF/Tqa6 +nqXm9RDhwkbhgAfdNy0uGKo7QwADBQP/c22BXppeUcMdwe7LPiJM3DGBkhPBhxcV +pV2CVsE/j0gNTTXzi+5d13zW2nLyyNTqSi1dfSjcjSnUb7VK7ZGxhDlLs9CE4aRp +LpS3MwYwj+G9W2+tMKGQ/S5fZD0VQXaQiAKc17A5qaOyuiX0kHO3+uv2oMXQHlgx +muIj2BLYm+CIYAQYEQIAIBYhBFHl8YQSvA1Dyd69k8PMCgY4QcenBQJZTtnLAhsM +AAoJEMPMCgY4QcenAAEAmwXz99nM3pTqySifutsKxiJMYFCJAJ4l3sniWRFNwjw5 +7P3djGZ+IlRMGA== +=Waxz +-----END PGP PUBLIC KEY BLOCK----- diff --git a/src/mailman_pgp/pgp/tests/data/ecc_p256.priv.asc b/src/mailman_pgp/pgp/tests/data/ecc_p256.priv.asc new file mode 100644 index 0000000..b591afc --- /dev/null +++ b/src/mailman_pgp/pgp/tests/data/ecc_p256.priv.asc @@ -0,0 +1,16 @@ +-----BEGIN PGP PRIVATE KEY BLOCK----- + +lHcEWU7bKhMIKoZIzj0DAQcCAwThGqSLtEwiMpxeYq/Rn2KBsIMFtzyQAbUimhh7 +zZLIHCCEkkCmgspsm6/PyKD7KKVXQdBQ8plxp0xIFRRdoPkvAAEA3zdhVq93r9E2 +u7rFDI0GVezCrCMTvkIMvbPsYfLr5fkR7LQsRUNDIFAtMjU2ICYgRUNDIFAtMjU2 +IDxFQ0MtUDI1NkBleGFtcGxlLm9yZz6IkAQTEwgAOBYhBLIosOb7llWGWt/SPTeT +cgW+FajQBQJZTtsqAhsDBQsJCAcCBhUICQoLAgQWAgMBAh4BAheAAAoJEDeTcgW+ +FajQNqABAKnhpvhIYcX3QToj7HQa7z9VCxTS3QXqOAlxSbEbC9u2AP9hshbxfCGn +O4WzpbKcMW+MUvmpRgV0pY4Kehzvsu8hnJx7BFlO2yoSCCqGSM49AwEHAgMEo11V +G86zoOvLvYVGeINItew+c/F7UoUZkeOJIZvm83R1ER+JcS3LZwUZ4w7DRGk9Bva8 +NY3z621LgE4i6XJNlAMBCAcAAQCcPgYOAAp3urADU2wB2lhW6dUPZYhpW3qybyZl +Bi46JwteiHgEGBMIACAWIQSyKLDm+5ZVhlrf0j03k3IFvhWo0AUCWU7bKgIbDAAK +CRA3k3IFvhWo0AQcAQDLqKJVuLrlsqg6JokkV70/D2HtzZlul8HZMEAPIAb+nwD/ +Q35vBQhHf1ALWu8RdTXCQnWyjUJO03NHMpfF69XRH0o= +=rOnV +-----END PGP PRIVATE KEY BLOCK----- diff --git a/src/mailman_pgp/pgp/tests/data/ecc_p256.pub.asc b/src/mailman_pgp/pgp/tests/data/ecc_p256.pub.asc new file mode 100644 index 0000000..92cafc7 --- /dev/null +++ b/src/mailman_pgp/pgp/tests/data/ecc_p256.pub.asc @@ -0,0 +1,15 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mFIEWU7bKhMIKoZIzj0DAQcCAwThGqSLtEwiMpxeYq/Rn2KBsIMFtzyQAbUimhh7 +zZLIHCCEkkCmgspsm6/PyKD7KKVXQdBQ8plxp0xIFRRdoPkvtCxFQ0MgUC0yNTYg +JiBFQ0MgUC0yNTYgPEVDQy1QMjU2QGV4YW1wbGUub3JnPoiQBBMTCAA4FiEEsiiw +5vuWVYZa39I9N5NyBb4VqNAFAllO2yoCGwMFCwkIBwIGFQgJCgsCBBYCAwECHgEC +F4AACgkQN5NyBb4VqNA2oAEAqeGm+EhhxfdBOiPsdBrvP1ULFNLdBeo4CXFJsRsL +27YA/2GyFvF8Iac7hbOlspwxb4xS+alGBXSljgp6HO+y7yGcuFYEWU7bKhIIKoZI +zj0DAQcCAwSjXVUbzrOg68u9hUZ4g0i17D5z8XtShRmR44khm+bzdHURH4lxLctn +BRnjDsNEaT0G9rw1jfPrbUuATiLpck2UAwEIB4h4BBgTCAAgFiEEsiiw5vuWVYZa +39I9N5NyBb4VqNAFAllO2yoCGwwACgkQN5NyBb4VqNAEHAEAy6iiVbi65bKoOiaJ +JFe9Pw9h7c2ZbpfB2TBADyAG/p8A/0N+bwUIR39QC1rvEXU1wkJ1so1CTtNzRzKX +xevV0R9K +=64hS +-----END PGP PUBLIC KEY BLOCK----- diff --git a/src/mailman_pgp/pgp/tests/test_inline.py b/src/mailman_pgp/pgp/tests/test_inline.py index d347c6a..900d5b2 100644 --- a/src/mailman_pgp/pgp/tests/test_inline.py +++ b/src/mailman_pgp/pgp/tests/test_inline.py @@ -45,8 +45,25 @@ class TestSigning(InlineWrapperTestCase): def test_is_signed(self, message, signed): super().is_signed(message, signed) - def test_sign(self): - pass + @parameterized.expand([ + (load_message('data/clear.eml'), + load_key('data/rsa_1024.priv.asc')), + (load_message('data/clear.eml'), + load_key('data/ecc_p256.priv.asc')) + ]) + def test_sign(self, message, key): + super().sign(message, key) + + @parameterized.expand([ + (load_message('data/clear.eml'), + load_key('data/rsa_1024.priv.asc'), + load_key('data/rsa_1024.pub.asc')), + (load_message('data/clear.eml'), + load_key('data/ecc_p256.priv.asc'), + load_key('data/ecc_p256.pub.asc')) + ]) + def test_sign_verify(self, message, priv, pub): + super().sign_verify(message, priv, pub) @parameterized.expand([ (load_message('data/inline_cleartext_signed.eml'), @@ -84,8 +101,29 @@ class TestEncryption(InlineWrapperTestCase): def test_is_encrypted(self, message, encrypted): super().is_encrypted(message, encrypted) - def test_encrypt(self): - pass + @parameterized.expand([ + (load_message('data/clear.eml'), + load_key('data/rsa_1024.pub.asc')), + (load_message('data/clear.eml'), + (load_key('data/rsa_1024.pub.asc'), + load_key('data/ecc_p256.pub.asc'))) + ]) + def test_encrypt(self, message, keys, **kwargs): + if isinstance(keys, tuple): + super().encrypt(message, *keys, **kwargs) + else: + super().encrypt(message, keys, **kwargs) + + @parameterized.expand([ + (load_message('data/clear.eml'), + load_key('data/rsa_1024.pub.asc'), + load_key('data/rsa_1024.priv.asc')), + (load_message('data/clear.eml'), + load_key('data/ecc_p256.pub.asc'), + load_key('data/ecc_p256.priv.asc')) + ]) + def test_encrypt_decrypt(self, message, pub, priv): + super().encrypt_decrypt(message, pub, priv) @parameterized.expand([ (load_message('data/inline_encrypted.eml'), diff --git a/src/mailman_pgp/pgp/utils.py b/src/mailman_pgp/pgp/utils.py new file mode 100644 index 0000000..5047cc6 --- /dev/null +++ b/src/mailman_pgp/pgp/utils.py @@ -0,0 +1,17 @@ +# 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 <http://www.gnu.org/licenses/>. + diff --git a/src/mailman_pgp/pgp/wrapper.py b/src/mailman_pgp/pgp/wrapper.py index c6d8dff..7049dc4 100644 --- a/src/mailman_pgp/pgp/wrapper.py +++ b/src/mailman_pgp/pgp/wrapper.py @@ -46,6 +46,12 @@ class PGPWrapper(): def is_signed(self): return self.is_mime_signed() or self.is_inline_signed() + def mime_sign(self, key): + return self.mime.sign(key) + + def inline_sign(self, key): + return self.inline.sign(key) + def verify(self, key): """ Verify the signature of this message with key. @@ -56,9 +62,9 @@ class PGPWrapper(): :rtype: pgpy.types.SignatureVerification """ if self.is_mime_signed(): - return self.mime.verify(key) + yield from self.mime.verify(key) elif self.is_inline_signed(): - return self.inline.verify(key) + yield from self.inline.verify(key) def is_mime_encrypted(self): return self.mime.is_encrypted() @@ -69,6 +75,12 @@ class PGPWrapper(): def is_encrypted(self): return self.is_mime_encrypted() or self.is_inline_encrypted() + def mime_encrypt(self, *keys, **kwargs): + return self.mime.encrypt(*keys, **kwargs) + + def inline_encrypt(self, *keys, **kwargs): + return self.inline.encrypt(*keys, **kwargs) + def decrypt(self, key): """ Decrypt this message with key. @@ -83,6 +95,9 @@ class PGPWrapper(): elif self.is_inline_encrypted(): return self.inline.decrypt(key) + def is_keys(self): + return self.mime.is_keys() or self.inline.is_keys() + def has_keys(self): return self.mime.has_keys() or self.inline.has_keys() |
