1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
|
# 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/>.
""""""
import copy
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 mailman.email.message import MultipartDigestMessage, Message
from mailman_pgp.pgp.mime import MIMEWrapper
from mailman_pgp.utils.email import copy_headers
class MIMEMultiSigWrapper(MIMEWrapper):
""""""
_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 _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 = MIMEMultiSigWrapper._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):
if self.is_signed():
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:
super().sign(key, hash)
def decrypt(self, key):
pmsg = next(iter(self.get_encrypted()))
decrypted = key.decrypt(pmsg)
dmsg = decrypted.message
if isinstance(dmsg, bytearray):
dmsg = dmsg.decode(decrypted.charset or 'utf-8')
out = message_from_string(dmsg, _class=Message)
if decrypted.is_signed:
if len(decrypted.signatures) != 1:
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
|