diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/mailman_pgp/pgp/mime.py | 4 | ||||
| -rw-r--r-- | src/mailman_pgp/pgp/mime_multisig.py | 77 |
2 files changed, 76 insertions, 5 deletions
diff --git a/src/mailman_pgp/pgp/mime.py b/src/mailman_pgp/pgp/mime.py index 3674f85..33ec93e 100644 --- a/src/mailman_pgp/pgp/mime.py +++ b/src/mailman_pgp/pgp/mime.py @@ -101,11 +101,11 @@ class MIMEWrapper: :rtype: typing.Generator[pgpy.PGPDetachedSignature] """ try: - msg = PGPDetachedSignature.from_blob( + sig = PGPDetachedSignature.from_blob( self.msg.get_payload(1).get_payload()) except: return - yield msg + yield sig def is_encrypted(self): """ diff --git a/src/mailman_pgp/pgp/mime_multisig.py b/src/mailman_pgp/pgp/mime_multisig.py index ff9039e..ced90fa 100644 --- a/src/mailman_pgp/pgp/mime_multisig.py +++ b/src/mailman_pgp/pgp/mime_multisig.py @@ -21,24 +21,62 @@ from email import message_from_string from email.encoders import encode_7or8bit from email.mime.application import MIMEApplication from email.mime.multipart import MIMEMultipart +from email.utils import collapse_rfc2231_value from mailman.email.message import MultipartDigestMessage, Message +from pgpy import PGPSignature, PGPDetachedSignature from mailman_pgp.pgp.mime import MIMEWrapper from mailman_pgp.utils.email import copy_headers class MIMEMultiSigWrapper(MIMEWrapper): - """""" + """https://tools.ietf.org/html/draft-ietf-openpgp-multsig-02""" + _signature_preamble = \ 'This is an OpepPGP/MIME signed message' \ '(RFC 4880, 3156 and draft-ietf-openpgp-multsig).\n' \ 'see https://tools.ietf.org/html/draft-ietf-openpgp-multsig-02' \ 'for more details.' + def is_signed(self): + """ + Whether the whole message is MIME signed as per draft-ietf-openpgp-multsig-02. + + :return: If the message is MIME signed. + :rtype: bool + """ + if not self._is_mime(): + return False + second_part = self.msg.get_payload(1) + second_type = second_part.get_content_type() + protocol_param = collapse_rfc2231_value(self.msg.get_param('protocol', + '')) + content_subtype = self.msg.get_content_subtype() + + return (second_part.is_multipart() and + second_type == 'multipart/mixed' and + content_subtype == 'signed' and + protocol_param == 'multipart/mixed' and + all(part.get_content_type() == MIMEWrapper._signed_type + for part in second_part.get_payload())) + + def get_signature(self): + """ + + :return: + :rtype: typing.Generator[pgpy.PGPSignature] + """ + for part in self.msg.get_payload(1).get_payload(): + try: + sig = PGPSignature.from_blob(part.get_payload()) + except: + continue + yield sig + def _wrap_signed_multiple(self, msg, signature): """ - As per https://tools.ietf.org/html/draft-ietf-openpgp-multsig-02. + As per draft-ietf-openpgp-multsig-02. :param msg: :param signature: @@ -67,15 +105,48 @@ class MIMEMultiSigWrapper(MIMEWrapper): return out def sign(self, key, hash=None): + """ + Sign a message with key. + + :param key: The key to sign with. + :type key: pgpy.PGPKey + :param hash: + :type hash: pgpy.constants.HashAlgorithm + :return: The signed message. + :rtype: mailman.email.message.Message + """ if self.is_signed(): payload = next(iter(self.get_signed())) - signature = next(iter(self.get_signature())) + signature = PGPDetachedSignature() + for sig in self.get_signature(): + signature |= sig signature |= key.sign(payload, hash=hash) return self._wrap_signed_multiple(self.msg, signature) else: super().sign(key, hash) + def verify(self, key): + """ + Verify the signature of this message with key. + + :param key: The key to verify with. + :type key: pgpy.PGPKey + :return: The verified signature. + :rtype: Generator[pgpy.types.SignatureVerification] + """ + clear_text = next(iter(self.get_signed())) + for signature in self.get_signature(): + yield key.verify(clear_text, signature) + def decrypt(self, key): + """ + Decrypt this message with key. + + :param key: The key to decrypt with. + :type key: pgpy.PGPKey + :return: The decrypted message. + :rtype: mailman.email.message.Message + """ pmsg = next(iter(self.get_encrypted())) decrypted = key.decrypt(pmsg) |
