diff options
| author | J08nY | 2017-07-27 20:25:34 +0200 |
|---|---|---|
| committer | J08nY | 2017-07-27 20:25:34 +0200 |
| commit | aa00267275f5d8fb7a5c44490cd849c747454791 (patch) | |
| tree | 0111fe79017f87f388dda8f17b4e4690e4c43502 /src/mailman_pgp | |
| parent | 9ebac4eca88193f5ad2a3686f0ff3b3bddac00d8 (diff) | |
| download | mailman-pgp-aa00267275f5d8fb7a5c44490cd849c747454791.tar.gz mailman-pgp-aa00267275f5d8fb7a5c44490cd849c747454791.tar.zst mailman-pgp-aa00267275f5d8fb7a5c44490cd849c747454791.zip | |
Diffstat (limited to 'src/mailman_pgp')
| -rw-r--r-- | src/mailman_pgp/pgp/inline.py | 7 | ||||
| -rw-r--r-- | src/mailman_pgp/pgp/mime.py | 74 | ||||
| -rw-r--r-- | src/mailman_pgp/pgp/tests/mime_multisig.py | 16 | ||||
| -rw-r--r-- | src/mailman_pgp/pgp/wrapper.py | 3 |
4 files changed, 89 insertions, 11 deletions
diff --git a/src/mailman_pgp/pgp/inline.py b/src/mailman_pgp/pgp/inline.py index 81e0839..2fe22cf 100644 --- a/src/mailman_pgp/pgp/inline.py +++ b/src/mailman_pgp/pgp/inline.py @@ -218,8 +218,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, cleartext=True) + if self._is_signed(part): + pmsg = PGPMessage.from_blob(part.get_payload()) + else: + payload = str(part.get_payload()) + pmsg = PGPMessage.new(payload, cleartext=True) smsg = self._sign(pmsg, key, hash) part.set_payload(str(smsg)) return out diff --git a/src/mailman_pgp/pgp/mime.py b/src/mailman_pgp/pgp/mime.py index bdb7943..a791a1d 100644 --- a/src/mailman_pgp/pgp/mime.py +++ b/src/mailman_pgp/pgp/mime.py @@ -21,10 +21,11 @@ from email import message_from_string from email.encoders import encode_7or8bit from email.iterators import walk from email.mime.application import MIMEApplication +from email.mime.multipart import MIMEMultipart from email.utils import collapse_rfc2231_value from mailman.email.message import Message, MultipartDigestMessage -from pgpy import PGPKey, PGPMessage, PGPSignature +from pgpy import PGPDetachedSignature, PGPKey, PGPMessage from pgpy.constants import HashAlgorithm, SymmetricKeyAlgorithm from public import public @@ -47,15 +48,24 @@ class MIMEWrapper: 'This is an OpenPGP/MIME signed message (RFC 4880 and 3156)' _encryption_preamble = \ 'This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156)' + _multiple_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 __init__(self, msg): + def __init__(self, msg, allow_draft_multisig=True): """ Wrap the given message. :param msg: The message to wrap. :type msg: mailman.email.message.Message + :param allow_draft_multisig: Whether to allow creating multisigs as per + https://tools.ietf.org/html/draft-ietf-openpgp-multsig-02. + :type allow_draft_multisig: bool """ self.msg = msg + self.allow_draft_multisig = allow_draft_multisig def get_payload(self): yield self.msg.as_string() @@ -98,10 +108,11 @@ class MIMEWrapper: """ :return: - :rtype: typing.Generator[pgpy.PGPSignature] + :rtype: typing.Generator[pgpy.PGPDetachedSignature] """ try: - msg = PGPSignature.from_blob(self.msg.get_payload(1).get_payload()) + msg = PGPDetachedSignature.from_blob( + self.msg.get_payload(1).get_payload()) except: return yield msg @@ -229,6 +240,13 @@ class MIMEWrapper: return 'pgp-' + algs[hash_algo] def _wrap_signed(self, msg, signature): + """ + As per RFC1847 and RFC3156. + + :param msg: + :param signature: + :return: + """ micalg = self._micalg(signature.hash_algorithm) out = MultipartDigestMessage('signed', micalg=micalg, protocol=MIMEWrapper._signed_type) @@ -248,6 +266,36 @@ class MIMEWrapper: copy_headers(msg, out) return out + def _wrap_signed_multiple(self, msg, signature): + """ + As per https://tools.ietf.org/html/draft-ietf-openpgp-multsig-02. + + :param msg: + :param signature: + :return: + """ + micalg = ', '.join(self._micalg(sig.hash_algorithm) + for sig in signature) + out = MultipartDigestMessage('signed', micalg=micalg, + protocol=MIMEWrapper._signed_type) + out.preamble = MIMEWrapper._signature_preambleň + + second_part = MIMEMultipart() + for sig in signature: + sig_part = MIMEApplication(_data=str(sig), + _subtype=MIMEWrapper._signature_subtype, + _encoder=encode_7or8bit, + name='signature.asc') + sig_part.add_header('Content-Description', + 'OpenPGP digital signature') + sig_part.add_header('Content-Disposition', 'attachment', + filename='signature.asc') + second_part.attach(sig_part) + out.attach(copy.deepcopy(msg)) + out.attach(second_part) + copy_headers(msg, out) + return out + def sign(self, key, hash=None): """ Sign a message with key. @@ -259,9 +307,15 @@ class MIMEWrapper: :return: The signed message. :rtype: mailman.email.message.Message """ - payload = next(iter(self.get_payload())) - signature = key.sign(payload, hash=hash) - return self._wrap_signed(self.msg, signature) + if self.is_signed() and self.allow_draft_multisig: + payload = next(iter(self.get_signed())) + signature = next(iter(self.get_signature())) + signature |= key.sign(payload, hash=hash) + return self._wrap_signed_multiple(self.msg, signature) + else: + payload = next(iter(self.get_payload())) + signature = key.sign(payload, hash=hash) + return self._wrap_signed(self.msg, signature) def decrypt(self, key): """ @@ -281,7 +335,11 @@ class MIMEWrapper: out = message_from_string(dmsg, _class=Message) if decrypted.is_signed: - out = self._wrap_signed(out, decrypted.signatures.pop()) + if len(decrypted.signatures) != 1 and self.allow_draft_multisig: + out = self._wrap_signed_multiple(out, + decrypted.detached_signature) + else: + out = self._wrap_signed(out, decrypted.signatures.pop()) copy_headers(self.msg, out) return out diff --git a/src/mailman_pgp/pgp/tests/mime_multisig.py b/src/mailman_pgp/pgp/tests/mime_multisig.py new file mode 100644 index 0000000..56dd01d --- /dev/null +++ b/src/mailman_pgp/pgp/tests/mime_multisig.py @@ -0,0 +1,16 @@ +# 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/>.
\ No newline at end of file diff --git a/src/mailman_pgp/pgp/wrapper.py b/src/mailman_pgp/pgp/wrapper.py index af6ce44..59f0fdd 100644 --- a/src/mailman_pgp/pgp/wrapper.py +++ b/src/mailman_pgp/pgp/wrapper.py @@ -85,7 +85,8 @@ class PGPWrapper(): """ :return: - :rtype: typing.Generator[pgpy.PGPMessage|pgpy.PGPSignature] + :rtype: typing.Generator[pgpy.PGPMessage|pgpy.PGPSignature| + pgpy.PGPDetachedSignature] """ if self.mime.is_signed(): yield from self.mime.get_signature() |
