コード例 #1
0
    def get_message_key_array_and_counter(self):
        """
        As the captain, we need to generate a new room message key, encrypt it, put
        it in the message key array and pass it to all the room members.
        Return the message key array and the new message key counter.
        """

        assert(self.i_am_captain)
        fresh_room_message_key = crypto.gen_symmetric_key()
        new_key_counter = self.key_transport_counter + 1
        self.set_room_message_key(fresh_room_message_key, new_key_counter)

        util.debug("Generated new room message key for %s: %s" %
                   (self.name, crypto.get_hexed_key(self.key_cache.get_current_key())))

        message_key_array = []

        for member in self.members.values():
            # Encrypt the room message key for each member.
            key_ciphertext = member.get_message_key_ciphertext(self.participant_priv_key,
                                                               self.key_cache.get_current_key())
            assert(len(key_ciphertext) == 32 + 40) # XXX
            message_key_array.append(key_ciphertext)

        # Concatenate all bytes and return
        message_key_array_str = "".join(message_key_array)
        return message_key_array_str, new_key_counter
コード例 #2
0
ファイル: introduction.py プロジェクト: piratas-ar/viola
def handle_introduction_packet(packet_payload, parsed):
    util.debug("Parsing introduction packet")

    payload = base64.b64decode(packet_payload)

    if len(payload) < INTRODUCTION_PAYLOAD_LEN:
        raise IncompletePacket

    # Get packet fields
    signature = payload[:64]
    rcvd_pubkey = crypto.parse_signing_pubkey(payload[64:64+32])

    # Verify signature
    try:
        rcvd_pubkey.verify(payload)
    except nacl.exceptions.BadSignatureError:
        util.control_msg("Could not verify signature of INTRODUCTION packet.")
        return ""

    hexed_key = crypto.get_hexed_key(rcvd_pubkey)
    irc_nick = parsed['from_nick']
    util.control_msg("You received an introduction from %s with identity key:" % irc_nick)
    util.control_msg("\t%s" % otrlib.colorize(hexed_key, "green"))
    util.control_msg("If you trust that key, please type:")
    util.control_msg("\t /viola trust-key <name> %s" % hexed_key)
    util.control_msg("where <name> is the nickname you want to assign to the key.")
    util.control_msg("Example: /viola trust-key alice %s" % hexed_key)
    util.control_msg("-" * 100)

    return ""
コード例 #3
0
ファイル: accounts.py プロジェクト: h1r00/flute
 def print_fingerprint(self):
     identity_key = self.get_identity_pubkey()
     hexed_key = crypto.get_hexed_key(identity_key)
     util.control_msg("Our identity key is:")
     util.control_msg("\t%s" % otrlib.colorize(hexed_key, "green"))
     util.control_msg(
         "A friend can trust it using the '/flute trust-key' command.")
コード例 #4
0
ファイル: introduction.py プロジェクト: h1r00/flute
def handle_introduction_packet(packet_payload, parsed):
    util.debug("Parsing introduction packet")

    payload = base64.b64decode(packet_payload)

    if len(payload) < INTRODUCTION_PAYLOAD_LEN:
        raise IncompletePacket

    # Get packet fields
    signature = payload[:64]
    rcvd_pubkey = crypto.parse_signing_pubkey(payload[64:64+32])

    # Verify signature
    try:
        rcvd_pubkey.verify(payload)
    except nacl.exceptions.BadSignatureError:
        util.control_msg("Could not verify signature of INTRODUCTION packet.")
        return ""

    hexed_key = crypto.get_hexed_key(rcvd_pubkey)
    irc_nick = parsed['from_nick']
    util.control_msg("You received an introduction from %s with identity key:" % irc_nick)
    util.control_msg("\t%s" % otrlib.colorize(hexed_key, "green"))
    util.control_msg("If you trust that key, please type:")
    util.control_msg("\t /flute trust-key <name> %s" % hexed_key)
    util.control_msg("where <name> is the nickname you want to assign to the key.")
    util.control_msg("Example: /flute trust-key alice %s" % hexed_key)
    util.control_msg("-" * 100)

    return ""
コード例 #5
0
ファイル: accounts.py プロジェクト: adrianhust/viola
    def init_crypto(self):
        """Initialize per-account crypto: Create an identity keypair."""
        try:
            self.identity_privkey = crypto.load_identity_privkey_from_disk(self.identity_privkey_fname)
        except IOError: # file not found (?)
            self.identity_privkey = crypto.gen_signing_privkey()
            util.debug("Generated identity keypair. Storing in '%s'.", self.identity_privkey_fname)
            crypto.store_identity_privkey_to_disk(self.identity_privkey, self.identity_privkey_fname)
        except ValueError:
            util.debug("Could not parse privkey.")

        util.debug("Using identity public key: %s" % crypto.get_hexed_key(self.identity_privkey.verify_key))
コード例 #6
0
ファイル: accounts.py プロジェクト: h1r00/flute
    def init_crypto(self):
        """Initialize per-account crypto: Create an identity keypair."""
        try:
            self.identity_privkey = crypto.load_identity_privkey_from_disk(
                self.identity_privkey_fname)
        except IOError:  # file not found (?)
            self.identity_privkey = crypto.gen_signing_privkey()
            util.debug("Generated identity keypair. Storing in '%s'." %
                       self.identity_privkey_fname)
            crypto.store_identity_privkey_to_disk(self.identity_privkey,
                                                  self.identity_privkey_fname)
        except ValueError:
            util.debug("Could not parse privkey.")

        util.debug("Using identity public key: %s" %
                   crypto.get_hexed_key(self.identity_privkey.verify_key))
コード例 #7
0
ファイル: room.py プロジェクト: piratas-ar/viola
    def get_message_key_array(self):
        """
        As the captain, we need to generate a new room message key, encrypt it, put
        it in the message key array and pass it to all the room members.
        """

        assert(self.i_am_captain)
        self.room_message_key = crypto.gen_symmetric_key() # XXX

        util.debug("Generated new room message key for %s: %s" %
                   (self.name, crypto.get_hexed_key(self.room_message_key)))

        message_key_array = []

        for member in self.members.values():
            # Encrypt the room message key for each member.
            key_ciphertext = member.get_message_key_ciphertext(self.participant_priv_key,
                                                               self.room_message_key)
            assert(len(key_ciphertext) == 32 + 40) # XXX
            message_key_array.append(key_ciphertext)

        # Concatenate all bytes and return
        return "".join(message_key_array)
コード例 #8
0
ファイル: viola.py プロジェクト: adrianhust/viola
def handle_room_join_packet(packet_payload, parsed, server):
    sender_nick = parsed['from_nick']
    channel = parsed['to_channel']
    account = accounts.get_my_account()

    if not parsed['to_channel']: # XXX functionify
        util.debug("Received ROOM_JOIN not in channel from %s. Ignoring." % sender_nick)
        return ""

    # Is this channel a viola room for us?
    try:
        viola_room = account.get_viola_room(channel, server)
    except accounts.NoSuchRoom:
        util.debug("Received ROOM_JOIN in a regular channel (%s). Ignoring." % channel)
        return ""

    payload = base64.b64decode(packet_payload)

    if len(payload) < ROOM_JOIN_PAYLOAD_LEN:
        raise IncompletePacket

    signature = payload[:SIG_LEN]
    identity_pubkey = crypto.parse_signing_pubkey(payload[SIG_LEN:SIG_LEN+PUBKEY_LEN])
    room_pubkey = crypto.parse_pub_key(payload[SIG_LEN+PUBKEY_LEN:SIG_LEN+PUBKEY_LEN+PUBKEY_LEN])

    # Verify signature
    # XXX is this the right logic for this particular packet?
    # XXX catch exception
    identity_pubkey.verify(payload)

    # XXX should we add all members even if we don't know them?
    viola_room.add_member(sender_nick, identity_pubkey, room_pubkey)

    # No need to do anything more if we are not captain in this channel.
    if not viola_room.i_am_captain:
        util.debug("Received ROOM_JOIN in %s but not captain. Ignoring." % channel)
        return ""

    util.debug("Received ROOM_JOIN in %s. Sending KEY_TRANSPORT (%d members)!" % (channel, len(viola_room.members)))

    # We are the captain. Check if we trust this key. Reject member otherwise.
    try:
        joining_friend_name = account.get_friend_from_identity_key(identity_pubkey)
    except accounts.IdentityKeyNotTrusted:
        buf = viola_room.buf
        util.viola_channel_msg(buf, "%s nickname %s is trying to join the channel with key %s." %
                               (otrlib.colorize("Untrusted", "red"), sender_nick,
                                crypto.get_hexed_key(identity_pubkey)), "red")
        util.viola_channel_msg(buf, "If you trust that key and you want them to join the channel, "
                               "please issue the following command and ask them to rejoin the channel:\n"
                               "\t /viola trust-key <name> %s\n"
                               "where <name> is the nickname you want to assign to the key."  %
                               crypto.get_hexed_key(identity_pubkey), "red")

        util.viola_channel_msg(buf, "Example: /viola trust-key alice %s" % crypto.get_hexed_key(identity_pubkey), "red")
        return ""

    # XXX Security: Maybe we should ask the captain before autoadding them!
    buf = viola_room.buf
    util.viola_channel_msg(buf, "Friend '%s' was added to the viola room!" % joining_friend_name)

    # We are captains in the channel. Act like it!
    # There is a new room member! Refresh and send new key!
    send_key_transport_packet(viola_room)
    return ""
コード例 #9
0
ファイル: viola.py プロジェクト: adrianhust/viola
def handle_key_transport_packet(packet_payload, parsed, server):
    sender = parsed['from_nick']
    channel = parsed['to_channel']
    account = accounts.get_my_account()

    if not channel: # XXX functionify
        util.debug("Received KEY_TRANSPORT not in channel from %s. Ignoring." % sender)
        return ""

    # Is this channel a viola room for us?
    # XXX Code duplication
    try:
        viola_room = account.get_viola_room(channel, server)
    except accounts.NoSuchRoom:
        util.debug("Received ROOM_JOIN in a regular channel (%s). Ignoring." % channel)
        return ""

    payload = base64.b64decode(packet_payload)

    if len(payload) < MINIMUM_KEY_TRANSPORT_PAYLOAD_LEN:
        raise IncompletePacket

    if viola_room.i_am_captain:
        util.debug("Received KEY_TRANSPORT in %s by %s but I am captain! Ignoring." % (channel, sender))
        return ""

    util.debug("Received KEY_TRANSPORT in %s! Handling it." % channel)

    # Start parsing the packet
    signature = payload[:SIG_LEN]

    captain_identity_pubkey = payload[SIG_LEN:SIG_LEN+PUBKEY_LEN]
    captain_identity_pubkey = crypto.parse_signing_pubkey(captain_identity_pubkey)

    captain_transport_pubkey = payload[SIG_LEN+PUBKEY_LEN : SIG_LEN+PUBKEY_LEN+PUBKEY_LEN]
    captain_transport_pubkey = crypto.parse_pub_key(captain_transport_pubkey)

    encrypted_message_key_array = payload[SIG_LEN+PUBKEY_LEN+PUBKEY_LEN:]

    # Check if we trust the captain.
    try:
        captain_friend_name = account.get_friend_from_identity_key(captain_identity_pubkey)
    except accounts.IdentityKeyNotTrusted:
        buf = viola_room.buf
        util.viola_channel_msg(buf, "Untrusted nickname %s is the captain of this channel with key: %s" % (sender, captain_identity_pubkey),
                               "red")
        util.viola_channel_msg(buf, "Ignoring KEY_TRANSPORT by untrusted captain. If you trust that key and"
                               "you want to join the channel, please issue the following command and rejoin:\n"
                               "\t /viola trust-key <name> %s\n"
                               "where <name> is the nickname you want to assign to the key."  %
                               crypto.get_hexed_key(captain_identity_pubkey), "red")
        util.viola_channel_msg(buf, "Example: /viola trust-key alice %s" % crypto.get_hexed_key(captain_identity_pubkey), "red")
        return ""

    # Verify captain signature
    captain_identity_pubkey.verify(payload)    # XXX catch exception

    # Try to decrypt the message key array
    try:
        room_message_key = crypto.decrypt_room_message_key(encrypted_message_key_array,
                                                           captain_transport_pubkey,
                                                           viola_room.get_room_participant_privkey())
    except crypto.NoKeyFound:
        util.debug("Received KEY_TRANSPORT but did not find my key. F**k.")
        return ""

    # We got the room message key!
    viola_room.set_room_message_key(room_message_key)
    viola_room.status = "done"

    # We found our captain. Add them to the room!
    # XXX should we do this here or before the checks?
    viola_room.add_member(sender, captain_identity_pubkey, captain_transport_pubkey)

    # Print some messages to the user
    buf = util.viola_channel_msg(viola_room.buf, "Joined room %s with captain %s!" % (channel, sender))
    util.debug("Got a new room message key from captain %s: %s" % \
               (sender, crypto.get_hexed_key(room_message_key)))

    return ""
コード例 #10
0
ファイル: accounts.py プロジェクト: h1r00/flute
class Account(object):
    def __init__(self, flute_dir):
        # Flute home dir
        self.flute_dir = flute_dir

        # Filenames of disk state
        self.identity_privkey = None
        self.identity_privkey_fname = self.get_identity_privkey_fname(
            "flute_account")  # XXX put account name
        self.friends_db_fname = self.get_friends_db_fname("flute_account")

        # Dictionary of active flute rooms.
        # e.g. { "#social" : FluteRoom1 ; "#skateboarding" : FluteRoom2 }
        self.active_flute_rooms = {}

        # Initialize crypto!
        self.init_crypto()

    def get_friends_db_fname(self, account_name):
        return os.path.join(self.flute_dir, '{}.friends'.format(account_name))

    def get_identity_privkey_fname(self, account_name):
        """Return the private key file path for an account."""
        return os.path.join(self.flute_dir, '{}.priv_key'.format(account_name))

    def print_friend_list(self):
        if not os.path.exists(self.friends_db_fname):
            util.control_msg("Friend list file does not exist.")
            return

        with open(self.friends_db_fname, 'r+') as friends_file:
            friends_dict = json.load(friends_file)
            util.control_msg("Current state of friend list:\n%s" %
                             json.dumps(friends_dict, indent=2))

    def print_fingerprint(self):
        identity_key = self.get_identity_pubkey()
        hexed_key = crypto.get_hexed_key(identity_key)
        util.control_msg("Our identity key is:")
        util.control_msg("\t%s" % otrlib.colorize(hexed_key, "green"))
        util.control_msg(
            "A friend can trust it using the '/flute trust-key' command.")

    def init_crypto(self):
        """Initialize per-account crypto: Create an identity keypair."""
        try:
            self.identity_privkey = crypto.load_identity_privkey_from_disk(
                self.identity_privkey_fname)
        except IOError:  # file not found (?)
            self.identity_privkey = crypto.gen_signing_privkey()
            util.debug("Generated identity keypair. Storing in '%s'." %
                       self.identity_privkey_fname)
            crypto.store_identity_privkey_to_disk(self.identity_privkey,
                                                  self.identity_privkey_fname)
        except ValueError:
            util.debug("Could not parse privkey.")

        util.debug("Using identity public key: %s" %
                   crypto.get_hexed_key(self.identity_privkey.verify_key))

    def register_flute_room(self, channel, server, buf, i_am_captain=False):
        """
        Register flute room on this account. If a room under this channel name
        already exists, be careful about overwriting it.
        """
        old_room = None

        # If this room already exists and is active, don't register it again.
        if channel in self.active_flute_rooms:
            old_room = self.active_flute_rooms[channel]
            if old_room.is_active():
                util.flute_channel_msg(buf,
                                       "Channel %s is already a flute room" %
                                       channel)  # XXX fix!
                raise FluteCommandError

        # If there used to be a room with this name but we are not overwriting
        # it, remove the old one.
        if old_room:
            self.active_flute_rooms.pop(channel)  # XXX more cleanup?

        # Create the room
        flute_room = room.FluteRoom(channel, server, buf, i_am_captain)
        # and register it.
        self.active_flute_rooms[channel] = flute_room

        return flute_room

    def trust_key(self, nickname, hexed_key):
        """Register trusted key 'hexed_key' under 'nickname'."""
        util.debug("Trusting key by %s" % nickname)

        try:
            crypto.store_friends_pubkey_to_disk(nickname, hexed_key,
                                                self.friends_db_fname)
        except crypto.NickAlreadyRegistered:
            util.control_msg(
                otrlib.colorize("Nick %s is already registered." % nickname,
                                "red"))
            return
        except crypto.KeyAlreadyRegistered:
            util.control_msg(
                otrlib.colorize("Key %s is already registered." % hexed_key,
                                "red"))
            return

        # Print current version of the friend list
        self.print_friend_list()

    def get_flute_room(self, channel, server):
        """Get flute room based on channel/server"""
        if channel not in self.active_flute_rooms:
            raise NoSuchRoom

        return self.active_flute_rooms[channel]

    def sign_msg_with_identity_key(self, msg):
        return self.identity_privkey.sign(msg)

    def get_identity_pubkey(self):
        return bytes(self.identity_privkey.verify_key)

    def get_friend_from_identity_key(self, identity_pubkey):
        """
        If this key is in our friend list, return the friend's nickname. if this key
        is unknown, raise IdentityKeyNotTrusted.
        """
        if not os.path.exists(self.friends_db_fname):
            raise IdentityKeyNotTrusted  # XXX initialize file

        with open(self.friends_db_fname, 'r+') as friends_file:
            try:
                friends_dict = json.load(friends_file)
            except ValueError, msg:
                util.debug("Could not load friends db: %s" % msg)
                friends_dict = {}

        # Check if nick or key are already registered.
        hexed_identity_pubkey = crypto.get_hexed_key(identity_pubkey)
        if hexed_identity_pubkey in friends_dict:
            return friends_dict[hexed_identity_pubkey]

        raise IdentityKeyNotTrusted
コード例 #11
0
ファイル: room.py プロジェクト: piratas-ar/viola
 def generate_room_key(self):
     self.participant_priv_key = crypto.gen_privkey()
     util.debug("Generated room participant key %s" % crypto.get_hexed_key(bytes(self.participant_priv_key.public_key)))
コード例 #12
0
 def generate_room_key(self):
     self.participant_priv_key = crypto.gen_privkey()
     util.debug("Generated room participant key %s" % crypto.get_hexed_key(bytes(self.participant_priv_key.public_key)))
コード例 #13
0
ファイル: accounts.py プロジェクト: Mandragorian/flute
 def print_fingerprint(self):
     identity_key = self.get_identity_pubkey()
     hexed_key = crypto.get_hexed_key(identity_key)
     util.control_msg("Our identity key is:")
     util.control_msg("\t%s" % otrlib.colorize(hexed_key, "green"))
     util.control_msg("A friend can trust it using the '/flute trust-key' command.")
コード例 #14
0
ファイル: flute.py プロジェクト: h1r00/flute
def handle_key_transport_packet(packet_payload, parsed, server):
    sender = parsed['from_nick']
    channel = parsed['to_channel']
    account = accounts.get_my_account()

    if not channel: # XXX functionify
        util.debug("Received KEY_TRANSPORT not in channel from %s. Ignoring." % sender)
        return ""

    # Is this channel a flute room for us?
    # XXX Code duplication
    try:
        flute_room = account.get_flute_room(channel, server)
    except accounts.NoSuchRoom:
        util.debug("Received KEY_TRANSPORT in a regular channel (%s). Ignoring." % channel)
        return ""

    payload = base64.b64decode(packet_payload)

    if len(payload) < MINIMUM_KEY_TRANSPORT_PAYLOAD_LEN:
        raise IncompletePacket

    if flute_room.i_am_captain:
        util.debug("Received KEY_TRANSPORT in %s by %s but I am captain! Ignoring." % (channel, sender))
        return ""

    util.debug("Received KEY_TRANSPORT in %s! Handling it." % channel)

    # Start parsing the packet
    signature = payload[:SIG_LEN]

    captain_identity_pubkey = payload[SIG_LEN:SIG_LEN+PUBKEY_LEN]
    captain_identity_pubkey = crypto.parse_signing_pubkey(captain_identity_pubkey)

    captain_transport_pubkey = payload[SIG_LEN+PUBKEY_LEN : SIG_LEN+PUBKEY_LEN+PUBKEY_LEN]
    captain_transport_pubkey = crypto.parse_pub_key(captain_transport_pubkey)

    new_key_counter, = struct.unpack('>I',payload[SIG_LEN + PUBKEY_LEN + PUBKEY_LEN: SIG_LEN + PUBKEY_LEN + PUBKEY_LEN + KEY_ID_LEN])

    encrypted_message_key_array = payload[SIG_LEN+PUBKEY_LEN+PUBKEY_LEN + KEY_ID_LEN:]

    # Check if we trust the captain.
    try:
        captain_friend_name = account.get_friend_from_identity_key(captain_identity_pubkey)
    except accounts.IdentityKeyNotTrusted:
        hexed_captain_key = crypto.get_hexed_key(captain_identity_pubkey)
        buf = flute_room.buf
        util.flute_channel_msg(buf, "Untrusted nickname %s is the captain of this channel with key: %s" % (sender, hexed_captain_key),
                               color="red")
        util.flute_channel_msg(buf, "Ignoring KEY_TRANSPORT by untrusted captain. If you trust that key and "
                               "you want to join the channel, please issue the following command and rejoin:\n"
                               "\t /flute trust-key <name> %s\n"
                               "where <name> is the nickname you want to assign to the key."  % hexed_captain_key,
                               color="red")
        util.flute_channel_msg(buf, "Example: /flute trust-key alice %s" % hexed_captain_key,
                               color="red")
        return ""

    # Verify captain signature
    captain_identity_pubkey.verify(payload)    # XXX catch exception

    # Check for replays using KEY_TRANSPORT counter
    if new_key_counter < 1:
        util.debug("Corrupted key counter %d" % new_key_counter)
        return ""
    if flute_room.key_transport_is_replay(new_key_counter):
        return ""

    # Try to decrypt the message key array
    try:
        room_message_key = crypto.decrypt_room_message_key(encrypted_message_key_array,
                                                           captain_transport_pubkey,
                                                           flute_room.get_room_participant_privkey())
    except crypto.NoKeyFound:
        util.debug("Received KEY_TRANSPORT but did not find my key. F**k.")
        return ""

    # We got the room message key!
    flute_room.set_room_message_key(room_message_key, new_key_counter)
    flute_room.status = "done"

    # We found our captain. Add them to the room!
    # XXX careful not to double-add the captain in case of rekey
    flute_room.add_member(sender, captain_identity_pubkey, captain_transport_pubkey)

    # Print some messages to the user
    buf = util.flute_channel_msg(flute_room.buf,
                                 "Got room key for %s from captain %s (friend name: %s)!" % (channel, sender, captain_friend_name))
    util.debug("Got a new room message key from captain %s: %s" % \
               (sender, crypto.get_hexed_key(room_message_key)))

    return ""
コード例 #15
0
ファイル: flute.py プロジェクト: h1r00/flute
def handle_room_join_packet(packet_payload, parsed, server):
    sender_nick = parsed['from_nick']
    channel = parsed['to_channel']
    account = accounts.get_my_account()

    if not parsed['to_channel']: # XXX functionify
        util.debug("Received ROOM_JOIN not in channel from %s. Ignoring." % sender_nick)
        return ""

    # Is this channel a flute room for us?
    try:
        flute_room = account.get_flute_room(channel, server)
    except accounts.NoSuchRoom:
        util.debug("Received ROOM_JOIN in a regular channel (%s). Ignoring." % channel)
        return ""

    payload = base64.b64decode(packet_payload)

    if len(payload) < ROOM_JOIN_PAYLOAD_LEN:
        raise IncompletePacket

    signature = payload[:SIG_LEN]
    identity_pubkey = crypto.parse_signing_pubkey(payload[SIG_LEN:SIG_LEN+PUBKEY_LEN])
    room_pubkey = crypto.parse_pub_key(payload[SIG_LEN+PUBKEY_LEN:SIG_LEN+PUBKEY_LEN+PUBKEY_LEN])

    # Verify signature
    # XXX is this the right logic for this particular packet?
    # XXX catch exception
    identity_pubkey.verify(payload)

    # XXX should we add all members even if we don't know them?
    flute_room.add_member(sender_nick, identity_pubkey, room_pubkey)

    # No need to do anything more if we are not captain in this channel.
    if not flute_room.i_am_captain:
        util.debug("Received ROOM_JOIN in %s but not captain. Ignoring." % channel)
        return ""

    util.debug("Received ROOM_JOIN in %s. Sending KEY_TRANSPORT (%d members)!" % (channel, len(flute_room.members)))

    # We are the captain. Check if we trust this key. Reject member otherwise.
    try:
        joining_friend_name = account.get_friend_from_identity_key(identity_pubkey)
    except accounts.IdentityKeyNotTrusted:
        buf = flute_room.buf
        util.flute_channel_msg(buf, "%s nickname %s is trying to join the channel with key %s." %
                               (otrlib.colorize("Untrusted", "red"), sender_nick,
                                crypto.get_hexed_key(identity_pubkey)), "red")
        util.flute_channel_msg(buf, "If you trust that key and you want them to join the channel, "
                               "please issue the following command and ask them to rejoin the channel:\n"
                               "\t /flute trust-key <name> %s\n"
                               "where <name> is the nickname you want to assign to the key."  %
                               crypto.get_hexed_key(identity_pubkey), "red")

        util.flute_channel_msg(buf, "Example: /flute trust-key alice %s" % crypto.get_hexed_key(identity_pubkey), "red")
        return ""

    # XXX Security: Maybe we should ask the captain before autoadding them!
    buf = flute_room.buf
    util.flute_channel_msg(buf, "User '%s' (friend name: %s) was added to the flute room!" % \
                           (sender_nick, joining_friend_name),
                           color="green")

    # We are captains in the channel. Act like it!
    # There is a new room member! Refresh and send new key!
    send_key_transport_packet(flute_room)
    return ""