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
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