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 introduction_cmd(parsed_args, buf): account = accounts.get_my_account() # Prepare the metadata server = util.get_local_server(buf) # If a nick is provided, introduce to that nick. Otherwise, check if we are # currently having a private conversation with another nick, and if so # introduce to that nick. if len(parsed_args) >= 2: target_nick = parsed_args[1] else: if otrlib.buffer_is_private(buf): target_nick = util.get_local_channel(buf) else: util.control_msg( "Bad introduction command! Can't introduce yourself to a channel!" ) raise flute.FluteCommandError introduction.send_introduction(account, target_nick, server, buf) util.flute_channel_msg(buf, "[Introduced ourselves to %s.]" % target_nick, color="green")
def decrypt_room_message(self, message_ciphertext): """ Figure out the right "room message key" and try to decrypt the room message ciphertext. """ plaintext = None # Dont even try decrypting if we don't know the current room message key... if self.key_cache.is_empty(): util.debug("Received ROOM_MESSAGE in %s but no message key. Ignoring." % self.name) util.flute_channel_msg(self.buf, "[You hear a flute screeching... Please do '/flute join-room' to join the session.]", "grey") return "" # Loop over all keys and try to decrypt packet. for potential_room_message_key in self.key_cache.message_key_iterator(): try: plaintext = crypto.decrypt_room_message(potential_room_message_key, message_ciphertext) break except crypto.DecryptFail: util.debug("Hm, that key did not work. Trying next one...") continue # Did we get a plaintext? If yes, return it!! if plaintext: return plaintext # If we are here, all keys failed to decrypt and we can't do anything else... util.debug("All keys failed to decrypt ROOM_MESSAGE packet...") raise MessageDecryptFail
def key_transport_is_replay(self, new_key_counter): if new_key_counter <= self.key_transport_counter: util.flute_channel_msg(self.buf, "Received replayed KEY_TRANSPORT packet (%d / %d)" % \ (new_key_counter, self.key_transport_counter), color="red") return True return False
def handle_room_message_packet(packet_payload, parsed, server): sender_host = parsed['from'] sender_nick = parsed['from_nick'] channel = parsed['to_channel'] account = accounts.get_my_account() if not channel: # XXX functionify util.debug("Received ROOM_MESSAGE not in channel from %s. Ignoring." % sender_host) 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 ROOM_MESSAGE in a regular channel (%s). Ignoring." % channel) buf = util.get_current_buffer() # XXX weechat API shouldn't polute flute.py util.flute_channel_msg(buf, "[Tootie-too! What an annoying sound! Do '/flute join-room' to join the flute session.]", color="lightcyan") return "" payload = base64.b64decode(packet_payload) if len(payload) <= 64: raise IncompletePacket util.debug("Attempting to decrypt ROOM_MESSAGE in %s" % channel) # Get packet fields signature = payload[:SIG_LEN] message_ciphertext = payload[SIG_LEN:] # Decrypt ciphertext try: plaintext = flute_room.decrypt_room_message(message_ciphertext) except room.MessageDecryptFail: util.flute_channel_msg(flute_room.buf, "Could not decrypt message sent in room. Maybe old key. Try rejoining the channel.", color="red") return "" if not plaintext: return "" msg = "[ENCRYPTED] %s" % plaintext msg_in = otrlib.build_privmsg_in(sender_host, channel, msg) return msg_in
def send_room_join(channel, server, buf): """Send ROOM_JOIN message.""" account = accounts.get_my_account() # Don't send ROOM_JOIN to empty channel. No one to handle it. if util.irc_channel_is_empty(channel, server): util.flute_channel_msg(buf, "Can't 'join-room' in an empty channel!", "red") util.flute_channel_msg(buf, "Do '/flute start-room' if you want to start a new flute room instead.", "red") return # First of all, register this new room. flute_room = account.register_flute_room(channel, server, buf) # Get the keys to be placed in the packet my_pub_key = account.get_identity_pubkey() my_room_key = flute_room.get_room_participant_pubkey() # Sign them packet_fields = my_pub_key + my_room_key packet_signed = account.sign_msg_with_identity_key(packet_fields) payload_b64 = base64.b64encode(packet_signed) msg = ROOM_JOIN_OPCODE + payload_b64 transport.send_flute_privmsg(server, channel, msg) util.flute_channel_msg(buf, "Requested to join room %s..." % channel)
def introduction_cmd(parsed_args, buf): account = accounts.get_my_account() # Prepare the metadata server = util.get_local_server(buf) # If a nick is provided, introduce to that nick. Otherwise, check if we are # currently having a private conversation with another nick, and if so # introduce to that nick. if len(parsed_args) >= 2: target_nick = parsed_args[1] else: if otrlib.buffer_is_private(buf): target_nick = util.get_local_channel(buf) else: util.control_msg("Bad introduction command! Can't introduce yourself to a channel!") raise flute.FluteCommandError introduction.send_introduction(account, target_nick, server, buf) util.flute_channel_msg(buf, "[Introduced ourselves to %s.]" % target_nick, color="green")
def start_flute_room(channel, server, buf): """Start a Flute room in 'channel'@'server' on weechat buffer 'buf'.""" account = accounts.get_my_account() util.debug("Starting a flute session in %s." % channel) # Make sure we are the only nick in the channel otherwise someone else # might already be captaining. if not util.irc_channel_is_empty(channel, server): util.flute_channel_msg(buf, "Can only start flute session in empty channel!", "red") util.flute_channel_msg(buf, "Try '/flute join-room' in this channel instead.", "red") return account.register_flute_room(channel, server, buf, i_am_captain=True) util.flute_channel_msg(buf, "We are now the captain in room %s!" % channel)
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 ""
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 ""