Example #1
0
    def _encrypt(self, mail, encrypt=True, sign=True, inline=False, recipients=None, toself=True,
         passphrase=None, verify=False, **kwargs):
        import email.utils, six
        from email.header import decode_header
        from email.message import Message
        from email.utils import parseaddr
        from .mail import _mail_addreplace_header, protect_mail, _protected
        def find_sender(mail):
            sender = mail.get('from','')
            sender = parseaddr(decode_header(sender)[0][0])[1]
            sender = self.find_key(sender,secret=True)
            if not sender: raise KeyMissingError("sender")
            return sender

        if not isinstance(mail,(Message,)+six.string_types):
            raise TypeError("mail must be Message or str")
        if encrypt:
            mail = protect_mail(mail,linesep=None,sevenbit=False)
            if not recipients:
                tos = mail.get_all('to', []) + mail.get_all('cc', [])
                recipients = [parseaddr(decode_header(to)[0][0])[1] for to in tos]
            elif isinstance(recipients, six.string_types):
                recipients = [recipients]
            recipients = list(self.find_key(recipients).values())
            if None in recipients:
                raise KeyMissingError("public keys for recipients")
            if sign==True or toself: sender = find_sender(mail) # use default_key?
            if toself and not sender in recipients: recipients.append(sender)
        else:
            mail = protect_mail(mail,linesep='\r\n',sevenbit=True) # fix line separators + 7bit RFC2822
            if sign==True: sender = find_sender(mail) # use default_key?
        if sign:
            if sign==True:
                sign = sender
                if not passphrase and self.default_key and self.default_key[0]==sign:
                    passphrase = self.default_key[1]
            assert not type(sign) in (tuple,list), "multiple signers not yet supported"
            kwargs['default_key'] = sign
            kwargs['passphrase'] = passphrase
        plaintext, submsg = self._plaintext(mail, inline)
        if encrypt:
            # Do encryption, report errors
            try: result = self._encrypt_str(plaintext, recipients, armor=True, **kwargs)
            except: return None, None
            if not result: return None, result
            payload = str(result)
            if verify and toself:
                if sign: del kwargs['default_key']
                if self.default_key and kwargs.get('passphrase') is None:
                    kwargs['passphrase']= self.default_key[1]
                vresult = self.decrypt_str(payload, **kwargs)
                if not vresult.ok or (sign and not vresult.valid): return None, result
        else:
            # Generate signature, report errors
            try:
                if not mail.is_multipart() and inline:
                    result = self._sign_str(plaintext, clearsign=True, detach=False, **kwargs)
                else:
                    result = self._sign_str(plaintext, clearsign=False, detach=True, **kwargs)
            except: return None, None
            if not result: return None, result
            payload = str(result) #signature
            if verify:
                if not mail.is_multipart() and inline:
                    vresult = self.verify_str(payload)
                else:
                    vresult = self.verify_str(submsg.as_string(),payload)
                if not vresult.valid: return None, result
        # Compile encrypted message
        if not mail.is_multipart() and inline:
            mail.set_payload(payload)
            if encrypt:
                _mail_addreplace_header(mail,'Content-Transfer-Encoding','7bit')
                mail.set_param('x-action','pgp-encrypted')
            else:
                mail.set_param('x-action','pgp-signed')
        else:
            # workaround to preserve header order
            tmp = Message()
            tmp['Content-Type'] = mail['Content-Type']
            if encrypt:
                tmp.set_type('multipart/encrypted')
                tmp.set_param('protocol','application/pgp-encrypted')
            else:
                tmp.set_type('multipart/signed')
                tmp.del_param('boundary') # delete boundary as we need a new one
                tmp.set_param('protocol','application/pgp-signature')
                tmp.set_param('micalg','pgp-sha1;')
            mail.replace_header('Content-Type',tmp['Content-Type'])
            if six.PY3: mail = _protected(mail,headersonly=True)
            mail.set_payload(None)
            if encrypt:
                mail.preamble = 'This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156)'
                submsg = Message()
                submsg.add_header('Content-Type','application/pgp-encrypted')
                submsg.set_payload('Version: 1\n')
                mail.attach(submsg)
                submsg = Message()
                submsg.add_header('Content-Type','application/octet-stream; name="encrypted.asc"')
                submsg.add_header('Content-Description', 'OpenPGP encrypted message')
                submsg.add_header('Content-Disposition','inline; filename="encrypted.asc"')
                submsg.set_payload(payload)
                mail.attach(submsg)
            else:
                mail.preamble = 'This is an OpenPGP/MIME signed message (RFC 4880 and 3156)'
                assert submsg.as_string()==plaintext, "plaintext was broken"
                mail.attach(submsg)
                submsg = Message()
                submsg.add_header('Content-Type','application/pgp-signature; name="signature.asc"')
                submsg.add_header('Content-Description', 'OpenPGP digital signature')
                submsg.add_header('Content-Disposition','attachment; filename="signature.asc"')
                submsg.set_payload(payload)
                mail.attach(submsg)
        return mail, result
Example #2
0
    def _encrypt(self,
                 mail,
                 encrypt=True,
                 sign=True,
                 inline=False,
                 recipients=None,
                 toself=True,
                 passphrase=None,
                 verify=False,
                 **kwargs):
        import email.utils, six
        from email.header import decode_header
        from email.message import Message
        from email.utils import parseaddr
        from .mail import _mail_addreplace_header, protect_mail, _protected

        def find_sender(mail):
            sender = mail.get('from', '')
            sender = parseaddr(decode_header(sender)[0][0])[1]
            sender = self.find_key(sender, secret=True)
            if not sender: raise KeyMissingError("sender")
            return sender

        if not isinstance(mail, (Message, ) + six.string_types):
            raise TypeError("mail must be Message or str")
        if encrypt:
            mail = protect_mail(mail, linesep=None, sevenbit=False)
            if not recipients:
                tos = mail.get_all('to', []) + mail.get_all('cc', [])
                recipients = [
                    parseaddr(decode_header(to)[0][0])[1] for to in tos
                ]
            elif isinstance(recipients, six.string_types):
                recipients = [recipients]
            recipients = list(self.find_key(recipients).values())
            if None in recipients:
                raise KeyMissingError("public keys for recipients")
            if sign == True or toself:
                sender = find_sender(mail)  # use default_key?
            if toself and not sender in recipients: recipients.append(sender)
        else:
            mail = protect_mail(
                mail, linesep='\r\n',
                sevenbit=True)  # fix line separators + 7bit RFC2822
            if sign == True: sender = find_sender(mail)  # use default_key?
        if sign:
            if sign == True:
                sign = sender
                if not passphrase and self.default_key and self.default_key[
                        0] == sign:
                    passphrase = self.default_key[1]
            assert not type(sign) in (
                tuple, list), "multiple signers not yet supported"
            kwargs['default_key'] = sign
            kwargs['passphrase'] = passphrase
        plaintext, submsg = self._plaintext(mail, inline)
        if encrypt:
            # Do encryption, report errors
            try:
                result = self._encrypt_str(plaintext,
                                           recipients,
                                           armor=True,
                                           **kwargs)
            except:
                return None, None
            if not result: return None, result
            payload = str(result)
            if verify and toself:
                if sign: del kwargs['default_key']
                if self.default_key and kwargs.get('passphrase') is None:
                    kwargs['passphrase'] = self.default_key[1]
                vresult = self.decrypt_str(payload, **kwargs)
                if not vresult.ok or (sign and not vresult.valid):
                    return None, result
        else:
            # Generate signature, report errors
            try:
                if not mail.is_multipart() and inline:
                    result = self._sign_str(plaintext,
                                            clearsign=True,
                                            detach=False,
                                            **kwargs)
                else:
                    result = self._sign_str(plaintext,
                                            clearsign=False,
                                            detach=True,
                                            **kwargs)
            except:
                return None, None
            if not result: return None, result
            payload = str(result)  #signature
            if verify:
                if not mail.is_multipart() and inline:
                    vresult = self.verify_str(payload)
                else:
                    vresult = self.verify_str(submsg.as_string(), payload)
                if not vresult.valid: return None, result
        # Compile encrypted message
        if not mail.is_multipart() and inline:
            mail.set_payload(payload)
            if encrypt:
                _mail_addreplace_header(mail, 'Content-Transfer-Encoding',
                                        '7bit')
                mail.set_param('x-action', 'pgp-encrypted')
            else:
                mail.set_param('x-action', 'pgp-signed')
        else:
            # workaround to preserve header order
            tmp = Message()
            tmp['Content-Type'] = mail['Content-Type']
            if encrypt:
                tmp.set_type('multipart/encrypted')
                tmp.set_param('protocol', 'application/pgp-encrypted')
            else:
                tmp.set_type('multipart/signed')
                tmp.del_param(
                    'boundary')  # delete boundary as we need a new one
                tmp.set_param('protocol', 'application/pgp-signature')
                tmp.set_param('micalg', 'pgp-sha1;')
            mail.replace_header('Content-Type', tmp['Content-Type'])
            if six.PY3: mail = _protected(mail, headersonly=True)
            mail.set_payload(None)
            if encrypt:
                mail.preamble = 'This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156)'
                submsg = Message()
                submsg.add_header('Content-Type', 'application/pgp-encrypted')
                submsg.set_payload('Version: 1\n')
                mail.attach(submsg)
                submsg = Message()
                submsg.add_header(
                    'Content-Type',
                    'application/octet-stream; name="encrypted.asc"')
                submsg.add_header('Content-Description',
                                  'OpenPGP encrypted message')
                submsg.add_header('Content-Disposition',
                                  'inline; filename="encrypted.asc"')
                submsg.set_payload(payload)
                mail.attach(submsg)
            else:
                mail.preamble = 'This is an OpenPGP/MIME signed message (RFC 4880 and 3156)'
                assert submsg.as_string() == plaintext, "plaintext was broken"
                mail.attach(submsg)
                submsg = Message()
                submsg.add_header(
                    'Content-Type',
                    'application/pgp-signature; name="signature.asc"')
                submsg.add_header('Content-Description',
                                  'OpenPGP digital signature')
                submsg.add_header('Content-Disposition',
                                  'attachment; filename="signature.asc"')
                submsg.set_payload(payload)
                mail.attach(submsg)
        return mail, result