Пример #1
0
    def unbundle_wrapped_messages(self):
        ''' Unbundle messages and send them to their intended recipients. '''

        result_ok = False

        self.log_message('unbundling wrapped messages')
        if self.crypto_message is None:
            self.log_message('no crypto message to unbundle')
        else:
            message = self.crypto_message.get_email_message().get_message()
            if self.DEBUGGING:
                self.log_message('DEBUG: logged wrapped headers in goodcrypto.message.utils.log')
                utils.log_message_headers(message, tag='wrapped headers')

            if message.get_content_type() == mime_constants.MULTIPART_MIXED_TYPE:

                result_ok = self.split_and_send_messages(message)

                # no need to re-inject this message as we've already sent the inner messages to users
                self.crypto_message.set_processed(True)

                result_ok = True

            else:
                result_ok = False
                self.crypto_message.drop()
                self.log_message('dropping message because there are no valid bundled messages; content type: {}'.format(
                   message.get_content_type()))

        return result_ok
Пример #2
0
    def decrypt_message(self, filter_msg=True):
        '''
            Decrypt a message and add a tag if unsuccessful (internal use only).
        '''

        encryption_names = self._get_recipient_encryption_software()
        if encryption_names is None or len(
                encryption_names) < 1 or self.crypto_message is None:
            decrypted = False
            decrypted_with = []
            self.log_message('unable to decrypt message when missing data')
        else:
            try:
                self.log_message(
                    'trying to decrypt with: {}'.format(encryption_names))
                decrypted, decrypted_with = self._decrypt_with_all_encryption(
                    encryption_names)

                if decrypted:
                    self.crypto_message.set_crypted_with(decrypted_with)

                #  if the message is still encrypted, log it and tell the user
                elif self.crypto_message.get_email_message().is_probably_pgp():
                    if len(encryption_names) > 1:
                        software = encryption_names.__str__()
                    else:
                        software = str(encryption_names[0])

                    log_msg = "Failed to decrypt with {}".format(software)
                    self.log_message(log_msg)
                    self.log_message(
                        'logged failed message headers in goodcrypto.message.utils.log'
                    )
                    utils.log_message_headers(self.crypto_message,
                                              'failed message headers')
                    record_exception(message=log_msg)

                    tag = i18n(
                        'Unable to decrypt message with {encryption}'.format(
                            encryption=software))
                    self.crypto_message.add_error_tag_once(tag)

                # don't filter bundled messages; each message will be filtered separately
                if filter_msg and options.filter_html():
                    self._filter_html()
                else:
                    self.log_message("html filter disabled")
            except CryptoException as crypto_exception:
                raise CryptoException(crypto_exception.value)
            except Exception:
                decrypted = False
                record_exception()
                self.log_message(
                    'EXCEPTION - see syr.exception.log for details')

        return decrypted, decrypted_with
Пример #3
0
    def _decrypt_open_pgp_mime(self, from_user, crypto, passcode):
        '''
            Decrypt an open PGP MIME message (internal use only).
        '''

        decrypted = False
        plaintext = None
        encrypted_part = None

        try:
            self.log_message("message is in OpenPGP MIME format")
            if self.DEBUGGING:
                self.log_message(
                    'logged OpenPGP mime headers in goodcrypto.message.utils.log'
                )
                utils.log_message_headers(self.crypto_message,
                                          tag='OpenPGP mime headers')

            # remove any clear signed section before decrypting message
            self.crypto_message.get_email_message(
            ).remove_pgp_signature_blocks()

            payloads = self.crypto_message.get_email_message().get_message(
            ).get_payload()
            self.log_message("{} parts in message".format(len(payloads)))

            encrypted_part = payloads[self.ENCRYPTED_BODY_PART_INDEX]
            if isinstance(encrypted_part, Message):
                encrypted_part = encrypted_part.get_payload()
            if Decrypt.DEBUGGING:
                self.log_message("encrypted_part\n{}".format(encrypted_part))

            charset, __ = get_charset(encrypted_part)
            self.log_message('encrypted part char set: {}'.format(charset))
            plaintext = self._decrypt(from_user, encrypted_part, charset,
                                      crypto, passcode)
        except CryptoException as crypto_exception:
            raise CryptoException(crypto_exception.value)
        except Exception:
            record_exception()
            self.log_message('EXCEPTION - see syr.exception.log for details')

        if plaintext == None or encrypted_part is None or plaintext == encrypted_part:
            decrypted = False
            self.log_message("unable to decrypt message")

        else:
            filtered = self._extract_embedded_message(plaintext)
            self.crypto_message.set_filtered(filtered)
            decrypted = self.crypto_message.is_crypted()

        return decrypted
Пример #4
0
    def _extract_embedded_message(self, plaintext):
        '''
            Extract an embedded message.

            If the message includes an Open PGP header, then
            save the plaintext in the email message. Otherwise,
            create a new email message from the embedded message.
        '''

        extracted_embedded_message = False

        try:
            if self.DEBUGGING:
                self.log_message('embbedded message:\n{}'.format(plaintext))
            encrypted_type = get_first_header(
                self.crypto_message.get_email_message().get_message(),
                PGP_ENCRYPTED_CONTENT_TYPE)
            if encrypted_type is None:
                old_message = self.crypto_message.get_email_message(
                ).get_message()
                new_message = plaintext_to_message(old_message, plaintext)
                self.crypto_message.get_email_message().set_message(
                    new_message)
                self.crypto_message.set_crypted(True)
                self.log_message("created a new message from the plaintext")
                if self.DEBUGGING:
                    self.log_message(
                        'logged final embedded message headers in goodcrypto.message.utils.log'
                    )
                    utils.log_message_headers(
                        self.crypto_message,
                        tag='final embedded message headers')
            else:
                #  this assumes an embedded mime message
                self.log_message(
                    "openpgp mime type: {}".format(encrypted_type))
                embedded_message = EmailMessage(plaintext)
                self.crypto_message.set_email_message(embedded_message)
                self.crypto_message.set_crypted(True)
                extracted_embedded_message = True
                self.log_message("embedded message type is {}".format(
                    embedded_message.get_message().get_content_type()))
        except Exception:
            record_exception()
            self.log_message('EXCEPTION - see syr.exception.log for details')

        return extracted_embedded_message
Пример #5
0
    def decrypt_message(self, filter_msg=True):
        '''
            Decrypt a message and add a tag if unsuccessful (internal use only).
        '''

        encryption_names = self._get_recipient_encryption_software()
        if encryption_names is None or len(encryption_names) < 1 or self.crypto_message is None:
            decrypted = False
            decrypted_with = []
            self.log_message('unable to decrypt message when missing data')
        else:
            try:
                self.log_message('trying to decrypt with: {}'.format(encryption_names))
                decrypted, decrypted_with = self._decrypt_with_all_encryption(encryption_names)

                if decrypted:
                    self.crypto_message.set_crypted_with(decrypted_with)

                #  if the message is still encrypted, log it and tell the user
                elif self.crypto_message.get_email_message().is_probably_pgp():
                    if len(encryption_names) > 1:
                        software = encryption_names.__str__()
                    else:
                        software = str(encryption_names[0])

                    log_msg = "Failed to decrypt with {}".format(software)
                    self.log_message(log_msg)
                    self.log_message('logged failed message headers in goodcrypto.message.utils.log')
                    utils.log_message_headers(self.crypto_message, 'failed message headers')
                    record_exception(message=log_msg)

                    tag = i18n('Unable to decrypt message with {encryption}'.format(encryption=software))
                    self.crypto_message.add_error_tag_once(tag)

                # don't filter bundled messages; each message will be filtered separately
                if filter_msg and options.filter_html():
                    self._filter_html()
                else:
                    self.log_message("html filter disabled")
            except CryptoException as crypto_exception:
                raise CryptoException(crypto_exception.value)
            except Exception:
                decrypted = False
                record_exception()
                self.log_message('EXCEPTION - see syr.exception.log for details')

        return decrypted, decrypted_with
Пример #6
0
    def _protect_metadata(self, from_user, to_user, inner_encrypted_with):
        '''
            Protect the message's metadata and resist traffic analysis.
        '''

        if options.bundle_and_pad():
            tags_added = add_encrypted_tags_to_message(self.crypto_message)
            self._log_message('tags added to encrypted message before bundling: {}'.format(tags_added))

            # add the DKIM signature if user opted for it
            self.crypto_message = encrypt_utils.add_dkim_sig_optionally(self.crypto_message)

            message_name = packetize(
               self.crypto_message, inner_encrypted_with, self.verification_code)
            if os.stat(message_name).st_size > options.bundled_message_max_size():
                self._log_message('Message too large to bundle so throwing MessageException')
                if os.path.exists(message_name):
                    os.remove(message_name)
                error_message = self.MESSAGE_TOO_LARGE.format(kb_size=options.bundle_message_kb())
                self._log_message(error_message)
                raise MessageException(value=error_message)
            else:
                self._log_message('message waiting for bundling: {}'.format(
                  self.crypto_message.is_processed()))

        else:
            self._log_message('protecting metadata')

            # add the original sender and recipient in the header
            self.crypto_message.get_email_message().add_header(constants.ORIGINAL_FROM, from_user)
            self.crypto_message.get_email_message().add_header(constants.ORIGINAL_TO, to_user)

            # add the DKIM signature to the inner message if user opted for it
            self.crypto_message = encrypt_utils.add_dkim_sig_optionally(self.crypto_message)

            self._log_message('DEBUG: logged headers before encrypting with metadata key in goodcrypto.message.utils.log')
            log_message_headers(self.crypto_message,
                                tag='headers before encrypting with metadata key')

            # even if the inner message isn't encrypted, encrypt
            # the entire message to protect the metadata
            message_id = get_message_id(self.crypto_message.get_email_message())
            self.crypto_message = encrypt_utils.create_protected_message(
                self.crypto_message.smtp_sender(), self.crypto_message.smtp_recipient(),
                self.crypto_message.get_email_message().to_string(), message_id)
            self._log_message('added protective layer for metadata')
Пример #7
0
    def _decrypt_open_pgp_mime(self, from_user, crypto, passcode):
        '''
            Decrypt an open PGP MIME message (internal use only).
        '''

        decrypted = False
        plaintext = None
        encrypted_part = None

        try:
            self.log_message("message is in OpenPGP MIME format")
            if self.DEBUGGING:
                self.log_message('logged OpenPGP mime headers in goodcrypto.message.utils.log')
                utils.log_message_headers(self.crypto_message, tag='OpenPGP mime headers')

            # remove any clear signed section before decrypting message
            self.crypto_message.get_email_message().remove_pgp_signature_blocks()

            payloads = self.crypto_message.get_email_message().get_message().get_payload()
            self.log_message("{} parts in message".format(len(payloads)))

            encrypted_part = payloads[self.ENCRYPTED_BODY_PART_INDEX]
            if isinstance(encrypted_part, Message):
                encrypted_part = encrypted_part.get_payload()
            if Decrypt.DEBUGGING:
                self.log_message("encrypted_part\n{}".format(encrypted_part))

            charset, __ = get_charset(encrypted_part)
            self.log_message('encrypted part char set: {}'.format(charset))
            plaintext = self._decrypt(from_user, encrypted_part, charset, crypto, passcode)
        except CryptoException as crypto_exception:
            raise CryptoException(crypto_exception.value)
        except Exception:
            record_exception()
            self.log_message('EXCEPTION - see syr.exception.log for details')

        if plaintext == None or encrypted_part is None or plaintext == encrypted_part:
            decrypted = False
            self.log_message("unable to decrypt message")

        else:
            filtered = self._extract_embedded_message(plaintext)
            self.crypto_message.set_filtered(filtered)
            decrypted = self.crypto_message.is_crypted()

        return decrypted
Пример #8
0
    def split_and_send_messages(self, message):
        ''' Split a message apart and send each to the intended recipient. '''

        result_ok = False
        self.messages_sent = 0
        try:
            for part in message.walk():
                try:
                    if part.get_content_type() == mime_constants.APPLICATION_ALT_TYPE:
                        content = b64decode(part.get_payload(decode=True))
                        inner_crypto_message = self.create_inner_message(content)

                        if inner_crypto_message is not None:
                            self.log_message('logged inner message headers in goodcrypto.message.utils.log')
                            utils.log_message_headers(inner_crypto_message, tag='inner message headers')
                            ok, __ = self.decrypt_and_send_message(inner_crypto_message)
                            if ok:
                                self.messages_sent += 1

                except CryptoException as crypto_exception:
                    raise MessageException(value=crypto_exception.value)

                except MessageException as message_exception:
                    raise MessageException(value=message_exception.value)

                except:
                    self.log_message('bad part of message discarded')
                    self.log_message('EXCEPTION - see syr.exception.log for details')
                    record_exception()
            result_ok = True
            self.log_message('good bundled message contains {} inner message(s)'.format(self.messages_sent))

        except CryptoException as crypto_exception:
            raise MessageException(value=crypto_exception.value)

        except MessageException as message_exception:
            raise MessageException(value=message_exception.value)

        except:
            record_exception()
            self.log_message('EXCEPTION - see syr.exception.log for details')
            result_ok = False

        return result_ok
Пример #9
0
def add_tags_to_message(crypto_message, tags):
    '''
        Add tags to a message.

        >>> add_tags_to_message(None, None)
        False
    '''

    # update the tags
    if tags is None or len(tags) <= 0:
        tags_added = False
        log_message('no tags need to be added to message')
    else:
        tags_added = crypto_message.add_tags_to_message(tags)
        log_message('tags added to message: {}'.format(tags_added))
        if tags_added:
            log_message(tags)
            if DEBUGGING:
                log_message('DEBUG: logged taggged message headers in goodcrypto.message.utils.log')
                log_message_headers(crypto_message, tag='tagged message headers')

    return tags_added
Пример #10
0
    def get_bundled_message(self, crypto_message):
        '''
            Get the message which contains one or more bundled messages.
        '''
        try:
            self.log_message('getting message which contains one or more messages')
            if self.DEBUGGING:
                self.log_message('DEBUG: logged bundled crypto headers in goodcrypto.message.utils.log')
                utils.log_message_headers(crypto_message, 'bundled crypto headers')

            if self.DEBUGGING:
                self.log_message('crypto message before getting inner message\n{}'.format(crypto_message.get_email_message().to_string()))
            inner_message = crypto_message.get_email_message().get_content()
            if self.DEBUGGING:
                self.log_message('raw inner message\n{}'.format(inner_message))
            inner_crypto_message = CryptoMessage(email_message=EmailMessage(inner_message))

            if options.verify_dkim_sig():
                # verify dkim sig before any changes to message happen
                inner_crypto_message, dkim_sig_verified = decrypt_utils.verify_dkim_sig(inner_crypto_message)
                if options.dkim_delivery_policy() == DKIM_DROP_POLICY:
                    self.log_message('verified bundled dkim signature ok: {}'.format(dkim_sig_verified))
                elif dkim_sig_verified:
                    self.log_message('verified bundled dkim signature')
                else:
                    self.log_message('unable to verify bundled dkim signature, but dkim policy is to just warn')

            if self.DEBUGGING:
                self.log_message('DEBUG: logged bundled inner headers in goodcrypto.message.utils.log')
                utils.log_message_headers(crypto_message, 'bundled inner headers')

            original_sender = inner_crypto_message.get_email_message().get_header(ORIGINAL_FROM)
            original_recipient = inner_crypto_message.get_email_message().get_header(ORIGINAL_TO)
            original_subject = inner_crypto_message.get_email_message().get_header(mime_constants.SUBJECT_KEYWORD)

            # if this message is an internal message with a subject, then send it to the admin
            if (original_sender == inner_crypto_message.smtp_sender() and
                original_recipient == inner_crypto_message.smtp_recipient() and
                original_subject is not None):
                admin = get_admin_email()
                inner_crypto_message.set_smtp_recipient(admin)
            else:
                inner_crypto_message.set_smtp_sender(original_sender)
                inner_crypto_message.set_smtp_recipient(original_recipient)

            # remove the original keywords from the message
            inner_crypto_message.get_email_message().delete_header(ORIGINAL_FROM)
            inner_crypto_message.get_email_message().delete_header(ORIGINAL_TO)

            if self.DEBUGGING:
                self.log_message('DEBUG: logged inner crypto headers in goodcrypto.message.utils.log')
                utils.log_message_headers(inner_crypto_message, 'inner crypto headers')
        except Exception:
            record_exception()
            inner_crypto_message = None
            self.log_message('EXCEPTION - see syr.exception.log for details')

        return inner_crypto_message
Пример #11
0
    def _extract_embedded_message(self, plaintext):
        '''
            Extract an embedded message.

            If the message includes an Open PGP header, then
            save the plaintext in the email message. Otherwise,
            create a new email message from the embedded message.
        '''

        extracted_embedded_message = False

        try:
            if self.DEBUGGING: self.log_message('embbedded message:\n{}'.format(plaintext))
            encrypted_type = get_first_header(
                self.crypto_message.get_email_message().get_message(), PGP_ENCRYPTED_CONTENT_TYPE)
            if encrypted_type is None:
                old_message = self.crypto_message.get_email_message().get_message()
                new_message = plaintext_to_message(old_message, plaintext)
                self.crypto_message.get_email_message().set_message(new_message)
                self.crypto_message.set_crypted(True)
                self.log_message("created a new message from the plaintext")
                if self.DEBUGGING:
                    self.log_message('logged final embedded message headers in goodcrypto.message.utils.log')
                    utils.log_message_headers(self.crypto_message, tag='final embedded message headers')
            else:
                #  this assumes an embedded mime message
                self.log_message("openpgp mime type: {}".format(encrypted_type))
                embedded_message = EmailMessage(plaintext)
                self.crypto_message.set_email_message(embedded_message)
                self.crypto_message.set_crypted(True)
                extracted_embedded_message = True
                self.log_message("embedded message type is {}".format(
                   embedded_message.get_message().get_content_type()))
        except Exception:
            record_exception()
            self.log_message('EXCEPTION - see syr.exception.log for details')

        return extracted_embedded_message
Пример #12
0
    def add_history_and_remove(self, to_domain):
        ''' Add history records for the messages sent and then remove the associated file. '''
        def get_addendum_value(addendum, keyword):
            value = addendum[keyword]
            if is_string(value):
                value = value.strip()
            if value == 'True':
                value = True
            elif value == 'False':
                value = False

            return value

        if len(self.bundled_messages) > 0:
            for bundled_message in self.bundled_messages:
                self.log_message(
                    'message included in bundled: {}'.format(bundled_message))
                with open(bundled_message) as f:
                    original_message, addendum = parse_bundled_message(
                        f.read())
                    self.log_message('addendum: {}'.format(addendum))
                    encrypted = get_addendum_value(addendum,
                                                   constants.CRYPTED_KEYWORD)
                    private_signed = get_addendum_value(
                        addendum, constants.PRIVATE_SIGNED_KEYWORD)
                    clear_signed = get_addendum_value(
                        addendum, constants.CLEAR_SIGNED_KEYWORD)
                    dkim_signed = get_addendum_value(
                        addendum, constants.DKIM_SIGNED_KEYWORD)
                    if encrypted or private_signed or clear_signed or dkim_signed:
                        sender = get_addendum_value(
                            addendum, mime_constants.FROM_KEYWORD)
                        recipient = get_addendum_value(
                            addendum, mime_constants.TO_KEYWORD)
                        verification_code = get_addendum_value(
                            addendum, constants.VERIFICATION_KEYWORD)
                        crypto_message = CryptoMessage(
                            email_message=EmailMessage(original_message))
                        crypto_message.set_smtp_sender(sender)
                        crypto_message.set_smtp_recipient(recipient)
                        crypto_message.set_crypted(encrypted)
                        crypto_message.set_crypted_with(
                            addendum[constants.CRYPTED_WITH_KEYWORD])
                        crypto_message.set_metadata_crypted(True)
                        crypto_message.set_metadata_crypted_with(
                            self.crypted_with)
                        crypto_message.set_private_signed(private_signed)
                        if private_signed:
                            if encrypted:
                                crypto_message.add_private_signer({
                                    constants.SIGNER:
                                    sender,
                                    constants.SIGNER_VERIFIED:
                                    True
                                })
                            crypto_message.add_private_signer({
                                constants.SIGNER:
                                get_metadata_address(sender),
                                constants.SIGNER_VERIFIED:
                                True
                            })
                        crypto_message.set_clear_signed(clear_signed)
                        if clear_signed:
                            if encrypted:
                                crypto_message.add_clear_signer({
                                    constants.SIGNER:
                                    sender,
                                    constants.SIGNER_VERIFIED:
                                    True
                                })
                            else:
                                crypto_message.add_clear_signer({
                                    constants.SIGNER:
                                    get_metadata_address(sender),
                                    constants.SIGNER_VERIFIED:
                                    True
                                })
                        crypto_message.set_dkim_signed(dkim_signed)
                        if dkim_signed:
                            crypto_message.set_dkim_sig_verified(True)
                        history.add_outbound_record(crypto_message,
                                                    verification_code)
                        self.log_message(
                            'added outbound history record from {}'.format(
                                sender))
                        if self.DEBUGGING:
                            self.log_message(
                                'logged headers in goodcrypto.message.utils.log'
                            )
                            utils.log_message_headers(crypto_message,
                                                      tag='bundled headers')

                if os.path.exists(bundled_message):
                    os.remove(bundled_message)
                else:
                    self.log_message(
                        'tried to delete message after bundling it, but message no longer exists on disk'
                    )
        else:
            self.log_message('no bundled messages')
Пример #13
0
    def process_message(self):
        '''
            If the message is encrypted, try to decrypt it.
            If it's not encrypted, add a warning about the dangers of unencrypted messages.

            See unittests for usage as the test set up is too complex for a doctest.
        '''

        try:
            filtered = decrypted = False

            from_user = self.crypto_message.smtp_sender()
            to_user = self.crypto_message.smtp_recipient()

            if options.verify_dkim_sig():
                self.crypto_message, dkim_sig_verified = decrypt_utils.verify_dkim_sig(
                    self.crypto_message)
                if options.dkim_delivery_policy() == DKIM_DROP_POLICY:
                    self.log_message('verified dkim signature ok: {}'.format(
                        dkim_sig_verified))
                elif dkim_sig_verified:
                    self.log_message('verified dkim signature')
                else:
                    self.log_message(
                        'unable to verify dkim signature, but dkim policy is to just warn'
                    )

            self.log_message(
                "checking if message from {} to {} needs decryption".format(
                    from_user, to_user))
            if Decrypt.DEBUGGING:
                self.log_message(
                    'logged original message headers in goodcrypto.message.utils.log'
                )
                utils.log_message_headers(self.crypto_message,
                                          tag='original message headers')

            header_keys = HeaderKeys()
            if options.auto_exchange_keys():
                header_contains_key_info = header_keys.manage_keys_in_header(
                    self.crypto_message)
            else:
                header_contains_key_info = header_keys.keys_in_header(
                    self.crypto_message)

            if self.crypto_message.is_dropped():
                decrypted = False
                self.log_message(
                    "message dropped because of bad key in header")
                self.log_message(
                    'logged dropped message headers in goodcrypto.message.utils.log'
                )
                utils.log_message_headers(self.crypto_message,
                                          tag='dropped message headers')
            else:
                message_char_set, __ = get_charset(
                    self.crypto_message.get_email_message())
                self.log_message(
                    'message char set: {}'.format(message_char_set))
                if self.crypto_message.get_email_message().is_probably_pgp():
                    decrypted, decrypted_with = self.decrypt_message()
                    if not decrypted and self.crypto_message.is_metadata_crypted(
                    ):
                        tags.add_metadata_tag(self.crypto_message)
                        self.log_message(
                            "message only encrypted with metadata key")
                else:
                    decrypt_utils.verify_clear_signed(from_user,
                                                      self.crypto_message)
                    if self.crypto_message.is_metadata_crypted():
                        tags.add_metadata_tag(self.crypto_message)
                        self.log_message(
                            "message only encrypted with metadata key")
                    else:
                        tags.add_unencrypted_warning(self.crypto_message)
                        filtered = True
                        self.log_message(
                            "message doesn't appear to be encrypted at all")

                    # create a private key for the recipient if there isn't one already
                    add_private_key(to_user)

            self.need_to_send_metadata_key = (
                # if the metadata wasn't encrypted
                not self.crypto_message.is_metadata_crypted() and
                # but the sender's key was in the header so we know the sender uses GoodCrypto private server
                header_contains_key_info and
                # and we don't have the sender's metadata key
                len(
                    contacts.get_encryption_names(
                        get_metadata_address(email=from_user))) < 1)
            self.log_message('need to send metadata key: {}'.format(
                self.need_to_send_metadata_key))

            self.log_message('message content decrypted: {}'.format(decrypted))

            # finally save a record so the user can verify the message was received securely
            if (decrypted or self.crypto_message.is_metadata_crypted()
                    or self.crypto_message.is_private_signed()
                    or self.crypto_message.is_clear_signed()):

                decrypt_utils.add_history_and_verification(self.crypto_message)

            if decrypted or self.crypto_message.is_metadata_crypted():
                self.crypto_message.set_crypted(True)
                if not decrypted:
                    self.log_message('metadata tag: {}'.format(
                        self.crypto_message.get_tag()))

                analyzer = OpenPGPAnalyzer()
                if analyzer.is_encrypted(
                        self.crypto_message.get_email_message().get_content()):
                    tags.add_extra_layer_warning(self.crypto_message)
            else:
                self.crypto_message.set_crypted(False)

            decrypted_tags_filtered = tags.add_decrypted_tags_to_message(
                self.crypto_message)
            if decrypted_tags_filtered:
                filtered = True
            if filtered and not self.crypto_message.is_filtered():
                self.crypto_message.set_filtered(True)
            self.log_message(
                "finished adding tags to decrypted message; filtered: {}".
                format(self.crypto_message.is_filtered()))
            self.log_message("message originally encrypted with: {}".format(
                self.crypto_message.get_crypted_with()))
            self.log_message("metadata originally encrypted with: {}".format(
                self.crypto_message.get_metadata_crypted_with()))

            decrypt_utils.re_mime_encode(self.crypto_message)

            if self.DEBUGGING:
                self.log_message(
                    'logged final decrypted headers in goodcrypto.message.utils.log'
                )
                utils.log_message_headers(self.crypto_message,
                                          tag='final decrypted headers')

            self.log_message(
                '  final status: filtered: {} decrypted: {}'.format(
                    self.crypto_message.is_filtered(),
                    self.crypto_message.is_crypted()))

        except MessageException as message_exception:
            self.log_message(message_exception)
            raise MessageException(value=message_exception.value)

        except:
            record_exception()
            self.log_message('EXCEPTION - see syr.exception.log for details')

        return self.crypto_message
Пример #14
0
    def decrypt_metadata(self):
        '''
            Decrypt the wrapper message that protects metadata.
        '''

        dkim_sig_verified = False
        from_user = self.crypto_message.smtp_sender()
        to_user = self.crypto_message.smtp_recipient()

        self.log_message("decrypting metadata protected message from {} to {}".format(from_user, to_user))
        if self.DEBUGGING:
            self.log_message('DEBUG: logged original metadata headers in goodcrypto.message.utils.log')
            utils.log_message_headers(self.crypto_message, tag='original metadata headers')

        if options.verify_dkim_sig():
            # verify dkim sig before any changes to message happen
            self.crypto_message, dkim_sig_verified = decrypt_utils.verify_dkim_sig(self.crypto_message)
            if options.dkim_delivery_policy() == DKIM_DROP_POLICY:
                self.log_message('verified metadata dkim signature ok: {}'.format(dkim_sig_verified))
            elif dkim_sig_verified:
                self.log_message('verified metadata dkim signature')
            else:
                self.log_message('unable to verify metadata dkim signature, but dkim policy is to just warn')

        # the metadata wrapper always includes its own pub key in the header
        # and it must be imported if we don't already have it
        auto_exchange_keys = options.auto_exchange_keys()
        options.set_auto_exchange_keys(True)
        header_keys = HeaderKeys()
        # import the key if it's new, and
        # verify the key matches the sender's email address and our database
        header_keys.manage_keys_in_header(self.crypto_message)
        new_metadata_key = header_keys.new_key_imported_from_header()
        options.set_auto_exchange_keys(auto_exchange_keys)

        # before we change anything, send our metadata key to the recipient
        # this could result in a sending the key twice, but until we keep a
        # long term record of who we've sent the key, this is better than not sending it
        if new_metadata_key:
            metadata_key_sent = send_metadata_key(from_user, to_user)
            self.log_message('sent metadata key: {}'.format(metadata_key_sent))

        decrypt = Decrypt(self.crypto_message)
        decrypted, decrypted_with = decrypt.decrypt_message(filter_msg=False)
        inner_crypto_message = decrypt.get_crypto_message()
        if inner_crypto_message.is_dropped():
            self.log_message('public metadata wrapper dropped')
            wrapped_crypto_message = inner_crypto_message
        else:
            if decrypted:
                wrapped_crypto_message = self.get_bundled_message(inner_crypto_message)
                wrapped_crypto_message.set_metadata_crypted(True)
                wrapped_crypto_message.set_metadata_crypted_with(decrypted_with)
                self.log_message('created decrypted wrapped message')
                if self.DEBUGGING:
                    self.log_message('DEBUG: logged decrypted wrapped headers in goodcrypto.message.utils.log')
                    utils.log_message_headers(wrapped_crypto_message, tag='decrypted wrapped headers')
            else:
                # if it's not encypted, then drop the message
                inner_crypto_message.drop(True)
                wrapped_crypto_message = inner_crypto_message
                self.log_message('public metadata wrapper message not encrypted so dropping message')

        # use the new inner crypto message
        self.crypto_message = wrapped_crypto_message
        self.log_message("metadata decrypted with: {}".format(
            self.crypto_message.get_metadata_crypted_with()))

        return dkim_sig_verified
Пример #15
0
def get_tags(crypto_message, filtered=False):
    '''
        Get tags to add to message.

        Test extreme cases.
        >>> tags, filtered = get_tags(None)
        >>> tags is None
        True
        >>> filtered
        False
    '''

    def get_short_tags(tags, possible_tags):
        ''' Get the short tags for a message. '''

        # only keep the basic tags
        for tag in possible_tags:
            tag_ready = False
            if (MESSAGE_VERIFICATION_PREFIX in tag or
                MESSAGE_VERIFY_PREFIX in tag):
                tag_ready = True
            elif (RECEIVED_CONTENT_PRIVATELY in tag or
                  RECEIVED_FULL_MESSAGE_PRIVATELY in tag):
                # keep the tag simple
                m = re.match('^(.*?), but.*', tag)
                if m:
                    tag = m.group(1)
                tag_ready = True

            if tag_ready and tag not in tags:
                tags.append(tag)

        return tags

    crypt_tags = None
    add_long_tags = options.add_long_tags()

    try:
        #  if we have something to say, it's still been filtered
        if filtered and not crypto_message.is_filtered():
            crypto_message.set_filtered(True)
        log_message("filtered: {}".format(crypto_message.is_filtered()))
        if DEBUGGING:
            log_message('DEBUG: logged tagged and filtered headers in goodcrypto.message.utils.log')
            log_message_headers(crypto_message, tag='tagged and filtered headers')

        tags = crypto_message.get_error_tags()
        if add_long_tags:
            long_tags = crypto_message.get_tags()
            for tag in long_tags:
                if tag not in tags:
                    tags.append(tag)
        else:
            possible_tags = crypto_message.get_tags()
            if len(possible_tags) > 0:
                tags = get_short_tags(tags, possible_tags)

        log_message('tags: {}'.format(tags))
        total_tags = len(tags)
        if total_tags > 0:
            # look for the verification tag
            verify_tag = None
            reordered_tags = []
            for count in range(total_tags):
                tag = tags[count]
                if (MESSAGE_VERIFICATION_PREFIX in tag or
                    MESSAGE_VERIFY_PREFIX in tag):
                    verify_tag = tag
                else:
                    reordered_tags.append(tag)
            # add the verify tag last in the list
            if verify_tag is not None:
                reordered_tags.append(verify_tag)
                tags = reordered_tags

            if len(tags) > 2:
                new_tag = '{}:\n'.format(TAG_PREFIX)
            else:
                new_tag = '{} -'.format(TAG_PREFIX)
            for count in range(total_tags):
                tag = tags[count]
                if len(tags) > 2:
                    new_tag += '   {}) '.format(count + 1)
                else:
                    new_tag += ' '
                new_tag += tag
                if (not new_tag.endswith('.') and
                    MESSAGE_VERIFICATION_PREFIX not in tag and
                    MESSAGE_VERIFY_PREFIX not in tag):
                    new_tag += '.'
                if len(tags) > 2:
                    new_tag += '\n'
        else:
            new_tag = ''

        if len(new_tag) > 0:
            crypt_tags = new_tag

        filtered = crypto_message.is_filtered()

    except Exception:
        record_exception()
        log_message('EXCEPTION - see syr.exception.log for details')

    log_message('crypt tags: {}'.format(crypt_tags))

    return crypt_tags, filtered
Пример #16
0
    def process_message(self):
        '''
            If the message is encrypted, try to decrypt it.
            If it's not encrypted, add a warning about the dangers of unencrypted messages.

            See unittests for usage as the test set up is too complex for a doctest.
        '''

        try:
            filtered = decrypted = False

            from_user = self.crypto_message.smtp_sender()
            to_user = self.crypto_message.smtp_recipient()

            if options.verify_dkim_sig():
                self.crypto_message, dkim_sig_verified = decrypt_utils.verify_dkim_sig(self.crypto_message)
                if options.dkim_delivery_policy() == DKIM_DROP_POLICY:
                    self.log_message('verified dkim signature ok: {}'.format(dkim_sig_verified))
                elif dkim_sig_verified:
                    self.log_message('verified dkim signature')
                else:
                    self.log_message('unable to verify dkim signature, but dkim policy is to just warn')

            self.log_message("checking if message from {} to {} needs decryption".format(from_user, to_user))
            if Decrypt.DEBUGGING:
                self.log_message('logged original message headers in goodcrypto.message.utils.log')
                utils.log_message_headers(self.crypto_message, tag='original message headers')

            header_keys = HeaderKeys()
            if options.auto_exchange_keys():
                header_contains_key_info = header_keys.manage_keys_in_header(self.crypto_message)
            else:
                header_contains_key_info = header_keys.keys_in_header(self.crypto_message)

            if self.crypto_message.is_dropped():
                decrypted = False
                self.log_message("message dropped because of bad key in header")
                self.log_message('logged dropped message headers in goodcrypto.message.utils.log')
                utils.log_message_headers(self.crypto_message, tag='dropped message headers')
            else:
                message_char_set, __ = get_charset(self.crypto_message.get_email_message())
                self.log_message('message char set: {}'.format(message_char_set))
                if self.crypto_message.get_email_message().is_probably_pgp():
                    decrypted, decrypted_with = self.decrypt_message()
                    if not decrypted and self.crypto_message.is_metadata_crypted():
                        tags.add_metadata_tag(self.crypto_message)
                        self.log_message("message only encrypted with metadata key")
                else:
                    decrypt_utils.verify_clear_signed(from_user, self.crypto_message)
                    if self.crypto_message.is_metadata_crypted():
                        tags.add_metadata_tag(self.crypto_message)
                        self.log_message("message only encrypted with metadata key")
                    else:
                        tags.add_unencrypted_warning(self.crypto_message)
                        filtered = True
                        self.log_message("message doesn't appear to be encrypted at all")

                    # create a private key for the recipient if there isn't one already
                    add_private_key(to_user)

            self.need_to_send_metadata_key = (
                # if the metadata wasn't encrypted
                not self.crypto_message.is_metadata_crypted() and
                # but the sender's key was in the header so we know the sender uses GoodCrypto private server
                header_contains_key_info and
                # and we don't have the sender's metadata key
                len(contacts.get_encryption_names(get_metadata_address(email=from_user))) < 1)
            self.log_message('need to send metadata key: {}'.format(self.need_to_send_metadata_key))

            self.log_message('message content decrypted: {}'.format(decrypted))

            # finally save a record so the user can verify the message was received securely
            if (decrypted or
                self.crypto_message.is_metadata_crypted() or
                self.crypto_message.is_private_signed() or
                self.crypto_message.is_clear_signed() ):

                decrypt_utils.add_history_and_verification(self.crypto_message)

            if decrypted or self.crypto_message.is_metadata_crypted():
                self.crypto_message.set_crypted(True)
                if not decrypted:
                    self.log_message('metadata tag: {}'.format(self.crypto_message.get_tag()))

                analyzer = OpenPGPAnalyzer()
                if analyzer.is_encrypted(self.crypto_message.get_email_message().get_content()):
                    tags.add_extra_layer_warning(self.crypto_message)
            else:
                self.crypto_message.set_crypted(False)

            decrypted_tags_filtered = tags.add_decrypted_tags_to_message(self.crypto_message)
            if decrypted_tags_filtered:
                filtered = True
            if filtered and not self.crypto_message.is_filtered():
                self.crypto_message.set_filtered(True)
            self.log_message("finished adding tags to decrypted message; filtered: {}".format(self.crypto_message.is_filtered()))
            self.log_message("message originally encrypted with: {}".format(self.crypto_message.get_crypted_with()))
            self.log_message("metadata originally encrypted with: {}".format(self.crypto_message.get_metadata_crypted_with()))

            decrypt_utils.re_mime_encode(self.crypto_message)

            if self.DEBUGGING:
                self.log_message('logged final decrypted headers in goodcrypto.message.utils.log')
                utils.log_message_headers(self.crypto_message, tag='final decrypted headers')

            self.log_message('  final status: filtered: {} decrypted: {}'.format(
                self.crypto_message.is_filtered(), self.crypto_message.is_crypted()))

        except MessageException as message_exception:
            self.log_message(message_exception)
            raise MessageException(value=message_exception.value)

        except:
            record_exception()
            self.log_message('EXCEPTION - see syr.exception.log for details')

        return self.crypto_message