Example #1
0
    def prep_message_header(self, message, to_domain):
        ''' Prepare the header of a Message. '''

        if message is None:
            self.log_message('no message defined in prep_message_header')
        elif to_domain is None:
            self.log_message('domain is not defined in prep_message_header')
        else:
            message_date = datetime.utcnow().replace(tzinfo=utc)
            from_user = get_metadata_address(domain=get_domain())
            to_user = get_metadata_address(domain=to_domain)

            message.__setitem__(mime_constants.FROM_KEYWORD, from_user)
            message.__setitem__(mime_constants.TO_KEYWORD, to_user)
            message.__setitem__(constants.ORIGINAL_FROM, from_user)
            message.__setitem__(constants.ORIGINAL_TO, to_user)
            message.__setitem__(mime_constants.DATE_KEYWORD,
                                message_date.__str__())
            message.__setitem__(mime_constants.MESSAGE_ID_KEYWORD,
                                utils.get_message_id())
            self.log_message("message's content type: {}".format(
                message.get_content_type()))
            self.log_message("message's boundary: {}".format(
                message.get_boundary()))
            if self.DEBUGGING:
                self.log_message("message's key/value pair")
                for key in message.keys():
                    self.log_message('{}: {}'.format(key, message.get(key)))

        return message
Example #2
0
    def create_encrypted_message(self, inner_message, to_domain):
        ''' Create an encrypted Message. '''

        message = None

        if to_domain is None:
            self.log_message('domain is not defined')
        elif inner_message is None:
            self.log_message('no inner message defined')
        else:
            from_user = get_email(get_metadata_address(domain=get_domain()))
            to_user = get_email(get_metadata_address(domain=to_domain))

            crypto_message = create_protected_message(
                from_user, to_user, inner_message.as_string(),
                utils.get_message_id())
            if crypto_message.is_crypted():

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

                message = crypto_message.get_email_message().get_message()
                self.crypted_with = crypto_message.get_metadata_crypted_with()
                self.log_message('crypted with: {}'.format(self.crypted_with))
                for part in message.walk():
                    self.log_message('Content type of part: {}'.format(
                        part.get_content_type()))
                    if self.DEBUGGING: self.log_message(part.get_payload())
            else:
                report_bad_bundled_encrypted_message(to_domain,
                                                     self.bundled_messages)

        return message
    def start_crypto_message():
        from goodcrypto.mail.message.crypto_message import CryptoMessage

        # start a new crypto message
        from_metadata_user = metadata.get_metadata_address(email=from_user)
        to_metadata_user = metadata.get_metadata_address(email=to_user)

        crypto_message = CryptoMessage()
        crypto_message.get_email_message().add_header(
            mime_constants.FROM_KEYWORD, from_metadata_user)
        crypto_message.get_email_message().add_header(
            mime_constants.TO_KEYWORD, to_metadata_user)
        crypto_message.get_email_message().add_header(mime_constants.MESSAGE_ID_KEYWORD, message_id)
        # include the timestamp because some MTAs/spam filters object if it's not set
        crypto_message.get_email_message().add_header(
            mime_constants.DATE_KEYWORD, datetime.utcnow().isoformat(str(' ')))

        return crypto_message
    def start_crypto_message():
        from goodcrypto.mail.message.crypto_message import CryptoMessage

        # start a new crypto message
        from_metadata_user = metadata.get_metadata_address(email=from_user)
        to_metadata_user = metadata.get_metadata_address(email=to_user)

        crypto_message = CryptoMessage()
        crypto_message.get_email_message().add_header(
            mime_constants.FROM_KEYWORD, from_metadata_user)
        crypto_message.get_email_message().add_header(
            mime_constants.TO_KEYWORD, to_metadata_user)
        crypto_message.get_email_message().add_header(
            mime_constants.MESSAGE_ID_KEYWORD, message_id)
        # include the timestamp because some MTAs/spam filters object if it's not set
        crypto_message.get_email_message().add_header(
            mime_constants.DATE_KEYWORD,
            datetime.utcnow().isoformat(str(' ')))

        return crypto_message
Example #5
0
    def send_bundled_message(self, message, to_domain):
        ''' Send a Message to the domain. '''

        try:
            if message is None:
                result_ok = False
                self.log_message('nothing to send to {}'.format(to_domain))
            else:
                sender = get_email(get_metadata_address(domain=get_domain()))
                recipient = get_email(get_metadata_address(domain=to_domain))
                self.log_message(
                    'starting to send message from {} to {}'.format(
                        sender, recipient))
                result_ok = send_message(sender, recipient,
                                         message.as_string())
                self.log_message('finished sending message')
        except Exception as exception:
            result_ok = False
            self.log_message('error while sending message')
            self.log_message('EXCEPTION - see syr.exception.log for details')
            record_exception()

        return result_ok
Example #6
0
    def _start_check_for_encryption(self, from_user, to_user):
        ''' Start checking if the user uses encryption. '''

        # don't look for keys if the to_user's domain is also using goodcrypto
        if contacts.get(get_metadata_address(email=to_user)) is None:
            # start by adding a record so we don't search for a key again
            contact = contacts.add(to_user, None)

            # search for the keys; if one's found, send email to the from
            # user so they can verify the fingerprint
            self._log_message('queuing search for a key for {}'.format(to_user))
            queue_keyserver_search(to_user, from_user)
        else:
            self._log_message('{} is also using goodcrypto so not searching for a key'.format(to_user))
Example #7
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')
    def encrypt_message(crypto_message, data):

        encryption_ready = False
        encrypted_with = []

        # use the metadata address' encryption
        to_metadata_address = metadata.get_metadata_address(email=to_user)
        encryption_names = contacts.get_encryption_names(to_metadata_address)
        log_message('{} encryption software for: {}'.format(
            encryption_names, to_metadata_address))

        if len(encryption_names) < 1:
            error_message = i18n(
                'Unable to protect metadata because there are no encryption programs for {}.'
                .format(to_metadata_address))
            log_message(error_message)
            raise MessageException(value=error_message)
        else:
            # encrypt with each common encryption program
            for encryption_name in encryption_names:
                ready, to_metadata_address, __ = metadata.get_metadata_user_details(
                    to_user, encryption_name)
                log_message('to metadata ready {} '.format(ready))

                if ready:
                    ready, from_metadata_address, passcode = metadata.get_from_metadata_user_details(
                        from_user, encryption_name)
                    log_message('metadata keys ready {}'.format(ready))

                if ready:
                    log_message(
                        'protecting metadata with {}'.format(encryption_names))

                    # if we're ready with any key, then the encryption is ready
                    encryption_ready = True

                    from_user_id = get_email(from_metadata_address)
                    to_user_id = get_email(to_metadata_address)
                    crypto_message.set_smtp_sender(from_user_id)
                    crypto_message.set_smtp_recipient(to_user_id)

                    # use the default charset to prevent metadata leakage
                    charset, __ = get_charset(constants.DEFAULT_CHAR_SET)
                    users_dict = {
                        TO_KEYWORD: to_user_id,
                        FROM_KEYWORD: from_user_id,
                        PASSCODE_KEYWORD: passcode,
                        CHARSET_KEYWORD: charset
                    }

                    crypto = CryptoFactory.get_crypto(
                        encryption_name, get_classname(encryption_name))
                    ciphertext, error_message = encrypt_byte_array(
                        data, crypto, users_dict)
                    if ciphertext is not None and len(ciphertext) > 0:
                        crypto_message.get_email_message().get_message(
                        ).set_payload(ciphertext)

                        crypto_message.add_public_key_to_header(
                            users_dict[FROM_KEYWORD])
                        set_sigs(crypto_message, from_user_id, passcode)
                        crypto_message.set_filtered(True)
                        crypto_message.set_crypted(True)

                        # use the encrypted data for the next level of encryption
                        data = ciphertext

                        encrypted_with.append(encryption_name)
                    else:
                        log_message(
                            'unable to encrypt the metadata with {}'.format(
                                encryption_name))
                        raise MessageException(value=error_message)
                else:
                    log_message('unable to protect metadata with {}'.format(
                        encryption_name))

            return encryption_ready, encrypted_with
Example #9
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
    def encrypt_message(crypto_message, data):


        encryption_ready = False
        encrypted_with = []

        # use the metadata address' encryption
        to_metadata_address = metadata.get_metadata_address(email=to_user)
        encryption_names = contacts.get_encryption_names(to_metadata_address)
        log_message('{} encryption software for: {}'.format(encryption_names, to_metadata_address))

        if len(encryption_names) < 1:
            error_message = i18n(
              'Unable to protect metadata because there are no encryption programs for {}.'.format(to_metadata_address))
            log_message(error_message)
            raise MessageException(value=error_message)
        else:
            # encrypt with each common encryption program
            for encryption_name in encryption_names:
                ready, to_metadata_address, __ = metadata.get_metadata_user_details(
                    to_user, encryption_name)
                log_message('to metadata ready {} '.format(ready))

                if ready:
                    ready, from_metadata_address, passcode = metadata.get_from_metadata_user_details(
                        from_user, encryption_name)
                    log_message('metadata keys ready {}'.format(ready))

                if ready:
                    log_message('protecting metadata with {}'.format(encryption_names))

                    # if we're ready with any key, then the encryption is ready
                    encryption_ready = True

                    from_user_id = get_email(from_metadata_address)
                    to_user_id = get_email(to_metadata_address)
                    crypto_message.set_smtp_sender(from_user_id)
                    crypto_message.set_smtp_recipient(to_user_id)

                    # use the default charset to prevent metadata leakage
                    charset, __ = get_charset(constants.DEFAULT_CHAR_SET)
                    users_dict = {TO_KEYWORD: to_user_id,
                                  FROM_KEYWORD: from_user_id,
                                  PASSCODE_KEYWORD: passcode,
                                  CHARSET_KEYWORD: charset}

                    crypto = CryptoFactory.get_crypto(encryption_name, get_classname(encryption_name))
                    ciphertext, error_message = encrypt_byte_array(data, crypto, users_dict)
                    if ciphertext is not None and len(ciphertext) > 0:
                        crypto_message.get_email_message().get_message().set_payload(ciphertext)

                        crypto_message.add_public_key_to_header(users_dict[FROM_KEYWORD])
                        set_sigs(crypto_message, from_user_id, passcode)
                        crypto_message.set_filtered(True)
                        crypto_message.set_crypted(True)

                        # use the encrypted data for the next level of encryption
                        data = ciphertext

                        encrypted_with.append(encryption_name)
                    else:
                        log_message('unable to encrypt the metadata with {}'.format(encryption_name))
                        raise MessageException(value=error_message)
                else:
                    log_message('unable to protect metadata with {}'.format(encryption_name))

            return encryption_ready, encrypted_with
Example #11
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