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
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 ""
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 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 ""
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 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 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)
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 ""
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 ""
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
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)))
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 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 ""