Source code for mailman_pgp.pgp.wrapper
#
# 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/>.
"""A combined PGP/MIME + inline PGP wrapper."""
from pgpy.errors import PGPError
from public import public
from mailman_pgp.pgp.base import BaseWrapper
from mailman_pgp.pgp.inline import InlineWrapper
from mailman_pgp.pgp.mime import MIMEWrapper
from mailman_pgp.pgp.mime_multisig import MIMEMultiSigWrapper
from mailman_pgp.utils.pgp import verifies
@public
[docs]class PGPWrapper(BaseWrapper):
"""A combined PGP/MIME + inline PGP wrapper."""
def __init__(self, msg, copy=False, default=MIMEWrapper):
"""
Wrap the given message.
:param msg: The message to wrap.
:type msg: mailman.email.message.Message
:param copy: Whether to copy the message when wrapping.
:type copy: bool
:param default: The wrapper class used for active operations (sign,
encrypt, attach_keys, attach_revocs)
:type default: Type[MIMEWrapper|MIMEMultiSigWrapper|InlineWrapper]
"""
super().__init__(msg, copy)
self.mime = MIMEWrapper(self.msg)
self.inline = InlineWrapper(self.msg)
self.multisig = MIMEMultiSigWrapper(self.msg)
self.wrappers = (self.mime, self.inline, self.multisig)
if default is MIMEWrapper:
self.default = self.mime
elif default is MIMEMultiSigWrapper:
self.default = self.multisig
elif default is InlineWrapper:
self.default = self.inline
else:
raise ValueError('Default wrapper must be one of ' +
MIMEWrapper.__name__ + ' ' +
MIMEMultiSigWrapper.__name__ + ' ' +
InlineWrapper.__name__ + '.')
def _rewrap(self, wrapper):
if wrapper is not None:
return PGPWrapper(wrapper.msg, default=self.default.__class__)
[docs] def get_payload(self):
return self.default.get_payload()
[docs] def is_signed(self):
"""
Whether this message is signed.
:return: If the message is signed.
:rtype: bool
"""
return any(wrapper.is_signed() for wrapper in self.wrappers)
[docs] def has_signature(self):
"""
Whether some parts of the message are signed.
:return: If some parts of the message are signed.
:rtype: bool
"""
return any(wrapper.has_signature() for wrapper in self.wrappers)
[docs] def get_signed(self):
"""
Get the signed content of the message.
:return: The signed contents of the message.
:rtype: typing.Generator[str]
"""
if self.mime.is_signed():
yield from self.mime.get_signed()
elif self.multisig.is_signed():
yield from self.multisig.get_signed()
elif self.inline.is_signed():
yield from self.inline.get_signed()
[docs] def get_signature(self):
"""
:return:
:rtype: typing.Generator[pgpy.PGPMessage|pgpy.PGPSignature|
pgpy.PGPDetachedSignature]
"""
if self.mime.is_signed():
yield from self.mime.get_signature()
elif self.multisig.is_signed():
yield from self.multisig.get_signature()
elif self.inline.is_signed():
yield from self.inline.get_signature()
[docs] def strip_signature(self):
"""
:return:
:rtype: PGPWrapper
"""
result = None
if self.mime.is_signed():
result = self.mime.strip_signature()
elif self.multisig.is_signed():
result = self.multisig.strip_signature()
elif self.inline.is_signed():
result = self.inline.strip_signature()
return self._rewrap(result)
[docs] def sign(self, key, **kwargs):
"""
Sign a message with key.
:param key: The key to sign with.
:type key: pgpy.PGPKey
:return:
:rtype: PGPWrapper
"""
return self._rewrap(self.default.sign(key, **kwargs))
[docs] def verify(self, key):
"""
Verify the signatures of this message with key.
:param key: The key to verify with.
:type key: pgpy.PGPKey
:return: The verified signatures.
:rtype: typing.Generator[pgpy.types.SignatureVerification]
"""
if self.mime.is_signed():
yield from self.mime.verify(key)
elif self.multisig.is_signed():
yield from self.multisig.verify(key)
elif self.inline.is_signed():
yield from self.inline.verify(key)
[docs] def verifies(self, key):
return verifies(self.verify(key))
[docs] def is_encrypted(self):
"""
Whether the message is encrypted.
:return: If the message is encrypted.
:rtype: bool
"""
return any(wrapper.is_encrypted() for wrapper in self.wrappers)
[docs] def has_encryption(self):
"""
Whether some parts of the message are encrypted.
:return: If some parts of the message are encrypted.
:rtype: bool
"""
return any(wrapper.has_encryption() for wrapper in self.wrappers)
[docs] def get_encrypted(self):
"""
:return:
:rtype: typing.Generator[pgpy.PGPMessage]
"""
if self.mime.is_encrypted():
yield from self.mime.get_encrypted()
elif self.multisig.is_encrypted():
yield from self.mime.get_encrypted()
elif self.inline.is_encrypted():
yield from self.inline.get_encrypted()
[docs] def encrypt(self, *keys, **kwargs):
"""
Encrypt the message with key/s, using cipher.
:param keys: The key/s to encrypt with.
:type keys: pgpy.PGPKey
:return:
:rtype: PGPWrapper
"""
return self._rewrap(self.default.encrypt(*keys, **kwargs))
[docs] def decrypt(self, key):
"""
Decrypt this message with key.
:param key: The key to decrypt with.
:type key: pgpy.PGPKey
:raises: pgpy.errors.PGPError
:return:
:rtype: PGPWrapper
"""
result = None
if self.mime.is_encrypted():
result = self.mime.decrypt(key)
elif self.multisig.is_encrypted():
result = self.multisig.decrypt(key)
elif self.inline.is_encrypted():
result = self.inline.decrypt(key)
return self._rewrap(result)
[docs] def try_decrypt(self, key):
"""
Try decrypting the message with given key.
:param key: The key to decrypt with.
:type key: pgpy.PGPKey
:return: The decrypted message, if successfully decrypted,
else original message.
:rtype: PGPWrapper
"""
try:
return self._rewrap(self.decrypt(key))
except PGPError:
return self
[docs] def sign_encrypt(self, key, *keys, **kwargs):
"""
Sign and encrypt the message, in one go.
:param key: The key to sign with.
:type key: pgpy.PGPKey
:param keys: The key/s to encrypt with.
:type keys: pgpy.PGPKey
:param hash:
:type hash: pgpy.constants.HashAlgorithm
:param cipher:
:type cipher: pgpy.constants.SymmetricKeyAlgorithm
:return:
:rtype: PGPWrapper
"""
return self._rewrap(self.default.sign_encrypt(key, *keys, **kwargs))
[docs] def is_keys(self):
"""
Whether the message is all keys (all parts).
:return: If the message is keys.
:rtype: bool
"""
return any(wrapper.is_keys() for wrapper in self.wrappers)
[docs] def has_keys(self):
"""
Whether the message contains public or private keys.
:return: If the message contains keys.
:rtype: bool
"""
return any(wrapper.has_keys() for wrapper in self.wrappers)
[docs] def keys(self):
"""
Get the collection of keys in this message.
:return: A collection of keys.
:rtype: typing.Generator[pgpy.PGPKey]
"""
if self.mime.has_keys():
yield from self.mime.keys()
elif self.multisig.has_keys():
yield from self.multisig.keys()
elif self.inline.has_keys():
yield from self.inline.keys()
[docs] def has_revocs(self):
"""
:return:
:rtype: bool
"""
return any(wrapper.has_revocs() for wrapper in self.wrappers)
[docs] def is_revocs(self):
"""
:return:
:rtype: bool
"""
return any(wrapper.is_revocs() for wrapper in self.wrappers)
[docs] def revocs(self):
"""
:return:
:rtype: typing.Generator[pgpy.PGPSignature]
"""
if self.mime.has_revocs():
yield from self.mime.revocs()
elif self.multisig.has_revocs():
yield from self.multisig.revocs()
elif self.inline.has_revocs():
yield from self.inline.revocs()