Example #1
0
    def _prep_from_user(self, private_user_ids, encryption_name):

        result_ok = private_user_ids is not None and len(private_user_ids) > 0
        if result_ok or options.create_private_keys():

            if result_ok:
                from_user_id = utils.get_user_id_matching_email(
                  self.crypto_message.smtp_sender(), private_user_ids)
            else:
                from_user_id = None

            if from_user_id is None:
                # add a key if there isn't one for the sender and we're creating keys
                if options.create_private_keys():
                    add_private_key(
                       self.crypto_message.smtp_sender(), encryption_software=encryption_name)
                else:
                    self._log_message("not creating a new {} key for {} because auto-create disabled".format(
                        encryption_name, from_user_id))
            else:
                self._log_message('found matching user id: {}'.format(from_user_id))

        self._log_message('_prep_from_user: {}'.format(result_ok))

        return result_ok
Example #2
0
    def _get_recipient_encryption_software(self):
        '''
            Get the software to decrypt a message for the recipient (internal use only).

            If the user doesn't have a key, then configure one and return None.
        '''

        encryption_software = None

        if self.crypto_message is None:
            self.log_message("missing crypto_message".format(
                self.crypto_message))
        else:
            try:
                from_user = self.crypto_message.smtp_sender()
                to_user = self.crypto_message.smtp_recipient()
                encryption_software = contacts.get_encryption_names(to_user)
                if len(encryption_software) > 0:
                    self.log_message(
                        "encryption software: {}".format(encryption_software))
                elif email_in_domain(
                        to_user) and options.create_private_keys():
                    add_private_key(to_user,
                                    encryption_software=encryption_software)
                    self.log_message(
                        "started to create a new {} key for {}".format(
                            encryption_software, to_user))
                else:
                    self.log_message(
                        "no encryption software for {}".format(to_user))
                    self.crypto_message.add_error_tag_once(
                        i18n(
                            'Message appears encrypted, but {email} does not use any known encryption'
                            .format(email=to_user)))
                    report_unable_to_decrypt(
                        to_user,
                        self.crypto_message.get_email_message().to_string())
            except CryptoException as crypto_exception:
                raise CryptoException(crypto_exception.value)
            except Exception as IOError:
                utils.log_crypto_exception(MessageException(format_exc()))
                record_exception()
                self.log_message(
                    'EXCEPTION - see syr.exception.log for details')
                try:
                    self.crypto_message.add_error_tag_once(
                        SERIOUS_ERROR_PREFIX)
                except Exception:
                    record_exception()
                    self.log_message(
                        'EXCEPTION - see syr.exception.log for details')

        return encryption_software
Example #3
0
def prep_metadata_key_message(from_user, to_user):
    '''
        Prepare a Message that contains notice about a new metadata key.
    '''
    def get_extra_headers():

        from goodcrypto.mail.message.utils import make_public_key_block

        extra_headers = None

        key_block = make_public_key_block(local_metadata_address)
        if len(key_block) > 0:
            extra_headers = []
            for line in key_block:
                name, __, value = line.partition(': ')
                extra_headers.append((name, value))
            extra_headers.append((constants.ACCEPTED_CRYPTO_SOFTWARE_HEADER, ','.join(encryption_software)))

        return extra_headers

    try:
        from goodcrypto.mail.message.utils import add_private_key

        message = local_metadata_address = remote_metadata_address = None
        if from_user is None or to_user is None:
            log_message('missing user data so unable to prepare metadata key message')

        else:
            # we want to send a message from the original recipient's "no metadata" address
            # to the original sender's "no metadata" address
            remote_metadata_address = get_metadata_address(email=from_user)
            local_metadata_address = get_metadata_address(email=to_user)
            encryption_software = contacts.get_encryption_names(local_metadata_address)

            extra_headers = get_extra_headers()
            if extra_headers is None:
                log_message('"no metadata" key is not ready yet')
                for encryption_name in encryption_software:
                    add_private_key(local_metadata_address, encryption_name)
                    log_message('adding a "no metadata" {} key'.format(encryption_name))
            else:
                log_message('preparing a "no metadata" key message')
                # the message should only contain the key in the header
                message = prep_mime_message(
                           local_metadata_address, remote_metadata_address, 'Message',
                           text='', extra_headers=extra_headers)
    except:
        message = None
        record_exception()
        log_message('EXCEPTION - see syr.exception.log for details')

    return message, local_metadata_address, remote_metadata_address
    def get_public_key_header(self, from_user):
        '''
            Get the public key header lines.

            >>> from goodcrypto_tests.mail.message_utils import get_plain_message_name
            >>> from goodcrypto.oce.test_constants import EDWARD_LOCAL_USER
            >>> auto_exchange = options.auto_exchange_keys()
            >>> options.set_auto_exchange_keys(True)
            >>> filename = get_plain_message_name('basic.txt')
            >>> with open(filename) as input_file:
            ...     crypto_message = CryptoMessage(email_message=EmailMessage(input_file))
            ...     key_block = crypto_message.get_public_key_header(EDWARD_LOCAL_USER)
            ...     key_block is not None
            ...     len(key_block) > 0
            True
            True
            >>> options.set_auto_exchange_keys(auto_exchange)
        '''

        header_lines = []
        if options.auto_exchange_keys():
            encryption_software_list = contacts.get_encryption_names(from_user)

            # if no crypto and we're creating keys, then do so now
            if (len(encryption_software_list) <= 0
                    and email_in_domain(from_user)
                    and options.create_private_keys()):

                add_private_key(from_user)
                self.log_message(
                    "started to create a new key for {}".format(from_user))
                encryption_software_list = contacts.get_encryption_names(
                    from_user)

            if len(encryption_software_list) > 0:
                self.log_message(
                    "getting header with public keys for {}: {}".format(
                        from_user, encryption_software_list))

                for encryption_software in encryption_software_list:
                    key_block = self.create_public_key_block(
                        encryption_software, from_user)
                    if len(key_block) > 0:
                        header_lines += key_block
        else:
            self.log_message("Warning: auto-exchange of keys is not active")

        return header_lines
    def get_public_key_header(self, from_user):
        '''
            Get the public key header lines.

            >>> from goodcrypto_tests.mail.message_utils import get_plain_message_name
            >>> from goodcrypto.oce.test_constants import EDWARD_LOCAL_USER
            >>> auto_exchange = options.auto_exchange_keys()
            >>> options.set_auto_exchange_keys(True)
            >>> filename = get_plain_message_name('basic.txt')
            >>> with open(filename) as input_file:
            ...     crypto_message = CryptoMessage(email_message=EmailMessage(input_file))
            ...     key_block = crypto_message.get_public_key_header(EDWARD_LOCAL_USER)
            ...     key_block is not None
            ...     len(key_block) > 0
            True
            True
            >>> options.set_auto_exchange_keys(auto_exchange)
        '''

        header_lines = []
        if options.auto_exchange_keys():
            encryption_software_list = contacts.get_encryption_names(from_user)

            # if no crypto and we're creating keys, then do so now
            if (len(encryption_software_list) <= 0 and
                email_in_domain(from_user) and
                options.create_private_keys()):

                add_private_key(from_user)
                self.log_message("started to create a new key for {}".format(from_user))
                encryption_software_list = contacts.get_encryption_names(from_user)

            if len(encryption_software_list) > 0:
                self.log_message("getting header with public keys for {}: {}".format(
                   from_user, encryption_software_list))

                for encryption_software in encryption_software_list:
                    key_block = self.create_public_key_block(encryption_software, from_user)
                    if len(key_block) > 0:
                        header_lines += key_block
        else:
            self.log_message("Warning: auto-exchange of keys is not active")

        return header_lines
    def add_public_key_to_header(self, from_user):
        '''
            Add public key and accepted crypto to header if automatically exchanging keys.
        '''

        if options.auto_exchange_keys():
            header_lines = self.get_public_key_header(from_user)
            if header_lines and len(header_lines) > 0:
                for line in header_lines:
                    # we can't just use split() because some lines have no value
                    index = line.find(CryptoMessage.SEPARATOR)
                    if index > 0:
                        header_name = line[0:index]

                        value_index = index + len(CryptoMessage.SEPARATOR)
                        if len(line) > value_index:
                            value = line[value_index:]
                        else:
                            value = ''
                    else:
                        header_name = line
                        value = ''

                    self.email_message.add_header(header_name, value)

                self.add_accepted_crypto_software(from_user)
                self.add_fingerprint(from_user)
                self.log_message(
                    "added key for {} to header".format(from_user))
            else:
                encryption_name = CryptoFactory.DEFAULT_ENCRYPTION_NAME
                if options.create_private_keys():
                    add_private_key(from_user,
                                    encryption_software=encryption_name)
                    self.log_message("creating a new {} key for {}".format(
                        encryption_name, from_user))
                else:
                    self.log_message(
                        "not creating a new {} key for {} because auto-create disabled"
                        .format(encryption_name, from_user_id))
        else:
            self.log_message(
                "not adding key for {} to header because auto-exchange disabled"
                .format(from_user))
Example #7
0
    def _get_recipient_encryption_software(self):
        '''
            Get the software to decrypt a message for the recipient (internal use only).

            If the user doesn't have a key, then configure one and return None.
        '''

        encryption_software = None

        if self.crypto_message is None:
            self.log_message("missing crypto_message".format(self.crypto_message))
        else:
            try:
                from_user = self.crypto_message.smtp_sender()
                to_user = self.crypto_message.smtp_recipient()
                encryption_software = contacts.get_encryption_names(to_user)
                if len(encryption_software) > 0:
                    self.log_message("encryption software: {}".format(encryption_software))
                elif email_in_domain(to_user) and options.create_private_keys():
                    add_private_key(to_user, encryption_software=encryption_software)
                    self.log_message("started to create a new {} key for {}".format(encryption_software, to_user))
                else:
                    self.log_message("no encryption software for {}".format(to_user))
                    self.crypto_message.add_error_tag_once(
                        i18n('Message appears encrypted, but {email} does not use any known encryption'.format(email=to_user)))
                    report_unable_to_decrypt(to_user, self.crypto_message.get_email_message().to_string())
            except CryptoException as crypto_exception:
                raise CryptoException(crypto_exception.value)
            except Exception as IOError:
                utils.log_crypto_exception(MessageException(format_exc()))
                record_exception()
                self.log_message('EXCEPTION - see syr.exception.log for details')
                try:
                    self.crypto_message.add_error_tag_once(SERIOUS_ERROR_PREFIX)
                except Exception:
                    record_exception()
                    self.log_message('EXCEPTION - see syr.exception.log for details')

        return encryption_software
    def add_public_key_to_header(self, from_user):
        '''
            Add public key and accepted crypto to header if automatically exchanging keys.
        '''

        if options.auto_exchange_keys():
            header_lines = self.get_public_key_header(from_user)
            if header_lines and len(header_lines) > 0:
                for line in header_lines:
                    # we can't just use split() because some lines have no value
                    index = line.find(CryptoMessage.SEPARATOR)
                    if index > 0:
                        header_name = line[0:index]

                        value_index = index + len(CryptoMessage.SEPARATOR)
                        if len(line) > value_index:
                            value = line[value_index:]
                        else:
                            value = ''
                    else:
                        header_name = line
                        value = ''

                    self.email_message.add_header(header_name, value)

                self.add_accepted_crypto_software(from_user)
                self.add_fingerprint(from_user)
                self.log_message("added key for {} to header".format(from_user))
            else:
                encryption_name = CryptoFactory.DEFAULT_ENCRYPTION_NAME
                if options.create_private_keys():
                    add_private_key(from_user, encryption_software=encryption_name)
                    self.log_message("creating a new {} key for {}".format(encryption_name, from_user))
                else:
                    self.log_message("not creating a new {} key for {} because auto-create disabled".format(
                        encryption_name, from_user_id))
        else:
            self.log_message("not adding key for {} to header because auto-exchange disabled".format(from_user))
Example #9
0
    def _process_plain_message(self, from_user, to_user, never_encrypt, encryption_names):
        '''
            Handle the initial processing of a message that does not need encryption.
        '''

        self._log_message('{} uses {} known encryption'.format(to_user, len(encryption_names)))

        if not self.ready_to_protect_metadata:
            if never_encrypt:
                self._log_message('{} set not to use any encryption'.format(to_user))

            # fail if encryption is required globally or for this individual
            elif options.require_outbound_encryption():
                self._log_message('message not sent because global encryption required')
                raise MessageException(value=self.MUST_ENCRYPT_ALL_MAIL.format(to_email=to_user))

            elif contacts.always_encrypt_outbound(to_user):
                self._log_message('message not sent because encryption required for {}'.format(to_user))
                raise MessageException(value=self.MUST_ENCRYPT_MAIL_TO_USER.format(to_email=to_user))

        # see if message must be clear signed
        if options.clear_sign_email():
            self._clear_sign_crypto_message(from_user)

        # add the public key so the receiver can use crypto with us in the future
        if options.auto_exchange_keys():
            self.crypto_message.add_public_key_to_header(from_user)
            self._log_message('added {} public key to header'.format(from_user))

        # if we're not exchanging keys, but we are creating them,
        # then start the process in background
        elif options.create_private_keys():
            add_private_key(from_user)
            self._log_message('created private key for {} if needed'.format(from_user))

        self.crypto_message.set_filtered(True)
Example #10
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
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