aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJ08nY2017-06-25 00:51:29 +0200
committerJ08nY2017-06-25 00:51:29 +0200
commita47b61581f6ddd040c25f8e4bb3dd6bd7b51aa30 (patch)
tree0fdd8eb0b47021f52e2d0b0258c8e4fae1cf8e31
parent52ab7fcef755d0adea8a23b5aa77e30119356ac1 (diff)
downloadmailman-pgp-a47b61581f6ddd040c25f8e4bb3dd6bd7b51aa30.tar.gz
mailman-pgp-a47b61581f6ddd040c25f8e4bb3dd6bd7b51aa30.tar.zst
mailman-pgp-a47b61581f6ddd040c25f8e4bb3dd6bd7b51aa30.zip
-rw-r--r--src/mailman_pgp/pgp/inline.py178
-rw-r--r--src/mailman_pgp/pgp/mime.py27
-rw-r--r--src/mailman_pgp/pgp/tests/base.py49
-rw-r--r--src/mailman_pgp/pgp/tests/data/dsa_elgamal_1024.priv.asc26
-rw-r--r--src/mailman_pgp/pgp/tests/data/dsa_elgamal_1024.pub.asc25
-rw-r--r--src/mailman_pgp/pgp/tests/data/ecc_p256.priv.asc16
-rw-r--r--src/mailman_pgp/pgp/tests/data/ecc_p256.pub.asc15
-rw-r--r--src/mailman_pgp/pgp/tests/test_inline.py46
-rw-r--r--src/mailman_pgp/pgp/utils.py17
-rw-r--r--src/mailman_pgp/pgp/wrapper.py19
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()