diff --git a/src/chat_control.py b/src/chat_control.py index acf53fe4f..bfc4beb4c 100644 --- a/src/chat_control.py +++ b/src/chat_control.py @@ -1057,10 +1057,7 @@ class ChatControl(ChatControlBase): if self.contact.jid in gajim.encrypted_chats[self.account]: self.xml.get_widget('gpg_togglebutton').set_active(True) - self.session = session - - # does this window have an existing, active esession? - self.esessioned = False + self.set_session(session) self.status_tooltip = gtk.Tooltips() self.update_ui() @@ -1467,6 +1464,23 @@ class ChatControl(ChatControlBase): self.mouse_over_in_last_30_secs = False self.kbd_activity_in_last_30_secs = False + + # print esession settings to textview + def print_esession_details(self): + if self.session and self.session.enable_encryption: + msg = _('E2E encryption enabled') + ChatControlBase.print_conversation_line(self, msg, 'status', '', None) + + if self.session.loggable: + msg = _('Session WILL be logged') + else: + msg = _('Session WILL NOT be logged') + + ChatControlBase.print_conversation_line(self, msg, 'status', '', None) + else: + msg = _('E2E encryption disabled') + ChatControlBase.print_conversation_line(self, msg, 'status', '', None) + def print_conversation(self, text, frm = '', tim = None, encrypted = False, subject = None, xhtml = None): '''Print a line in the conversation: @@ -1486,41 +1500,21 @@ class ChatControl(ChatControlBase): kind = 'info' name = '' else: - # ESessions if self.session and self.session.enable_encryption: - if not self.esessioned: - msg = _('Encryption enabled') - ChatControlBase.print_conversation_line(self, msg, - 'status', '', tim) - - if self.session.loggable: - msg = _('Session WILL be logged') - else: - msg = _('Session WILL NOT be logged') - - ChatControlBase.print_conversation_line(self, msg, - 'status', '', tim) - - self.esessioned = True - elif not encrypted: + if not encrypted: msg = _('The following message was NOT encrypted') ChatControlBase.print_conversation_line(self, msg, 'status', '', tim) - elif self.esessioned: - msg = _('Encryption disabled') - ChatControlBase.print_conversation_line(self, msg, - 'status', '', tim) - self.esessioned = False else: # GPG encryption ec = gajim.encrypted_chats[self.account] if encrypted and jid not in ec: - msg = _('Encryption enabled') + msg = _('OpenPGP Encryption enabled') ChatControlBase.print_conversation_line(self, msg, 'status', '', tim) ec.append(jid) elif not encrypted and jid in ec: - msg = _('Encryption disabled') + msg = _('OpenPGP Encryption disabled') ChatControlBase.print_conversation_line(self, msg, 'status', '', tim) ec.remove(jid) @@ -2003,12 +1997,16 @@ class ChatControl(ChatControlBase): def read_queue(self): '''read queue and print messages containted in it''' + jid = self.contact.jid jid_with_resource = jid if self.resource: jid_with_resource += '/' + self.resource events = gajim.events.get_events(self.account, jid_with_resource) + if hasattr(self, 'session') and self.session and self.session.enable_encryption: + self.print_esession_details() + # Is it a pm ? is_pm = False room_jid, nick = gajim.get_room_and_nick_from_fjid(jid) @@ -2169,18 +2167,18 @@ class ChatControl(ChatControlBase): msg = _('Encryption disabled') ChatControlBase.print_conversation_line(self, msg, 'status', '', None) - self.esessioned = False jid = str(self.session.jid) gajim.connections[self.account].delete_session(jid, self.session.thread_id) - self.session = gajim.connections[self.account].make_new_session(jid) + self.set_session(gajim.connections[self.account].make_new_session(jid)) else: if not self.session: - self.session = gajim.connections[self.account].make_new_session( - self.contact.jid) + fjid = self.contact.get_full_jid() + new_sess = gajim.connections[self.account].make_new_session(fjid) + self.set_session(new_sess) # XXX decide whether to use 4 or 3 message negotiation self.session.negotiate_e2e(False) diff --git a/src/common/stanza_session.py b/src/common/stanza_session.py index 9b5b5ea63..23b994fa4 100644 --- a/src/common/stanza_session.py +++ b/src/common/stanza_session.py @@ -71,8 +71,10 @@ class StanzaSession(object): def cancelled_negotiation(self): '''A negotiation has been cancelled, so reset this session to its default state.''' - # XXX notify the user - + if hasattr(self, 'control'): + msg = _('Session negotiation cancelled') + self.control.print_conversation_line(self, msg, 'status', '', None) + self.status = None self.negotiated = {} @@ -92,7 +94,7 @@ class StanzaSession(object): self.status = None def acknowledge_termination(self): - # we could send an acknowledgement message here, but we won't. + # we could send an acknowledgement message to the remote client here self.status = None if gajim.HAVE_PYCRYPTO: @@ -105,7 +107,7 @@ if gajim.HAVE_PYCRYPTO: import secrets # an encrypted stanza negotiation has several states. i've represented them -# as the following values in the 'status' +# as the following values in the 'status' # attribute of the session object: # 1. None: @@ -143,7 +145,7 @@ class EncryptedStanzaSession(StanzaSession): # _s denotes 'self' (ie. this client) self._kc_s = None - + # _o denotes 'other' (ie. the client at the other end of the session) self._kc_o = None @@ -161,17 +163,17 @@ class EncryptedStanzaSession(StanzaSession): self._kc_o = value self.decrypter = self.cipher.new(self._kc_o, self.cipher.MODE_CTR, counter=self.decryptcounter) - + def get_kc_o(self): return self._kc_o kc_s = property(get_kc_s, set_kc_s) kc_o = property(get_kc_o, set_kc_o) - + def encryptcounter(self): self.c_s = (self.c_s + 1) % (2 ** self.n) return crypto.encode_mpi_with_padding(self.c_s) - + def decryptcounter(self): self.c_o = (self.c_o + 1) % (2 ** self.n) return crypto.encode_mpi_with_padding(self.c_o) @@ -231,7 +233,7 @@ class EncryptedStanzaSession(StanzaSession): def decompress(self, compressed): if self.compression == None: - return compressed + return compressed def encrypt(self, encryptable): padded = crypto.pad_to_multiple(encryptable, 16, ' ', False) @@ -343,7 +345,7 @@ class EncryptedStanzaSession(StanzaSession): content += self.form_o + form_o2 mac_o_calculated = self.hmac(self.ks_o, content) - + if self.negotiated['recv_pubkey']: hash = crypto.sha256(mac_o_calculated) @@ -380,7 +382,7 @@ class EncryptedStanzaSession(StanzaSession): if self.negotiated['send_pubkey'] == 'hash': b64ed = base64.b64encode(self.hash(pubkey_s)) pubkey_s = '%s' % b64ed - + id_s = self.encrypt(pubkey_s + sign_s) else: id_s = self.encrypt(mac_s) @@ -395,7 +397,7 @@ class EncryptedStanzaSession(StanzaSession): if self.sigmai: # XXX save retained secret? self.check_identity(lambda : ()) - + return (xmpp.DataField(name='identity', value=base64.b64encode(id_s)), \ xmpp.DataField(name='mac', value=base64.b64encode(m_s))) @@ -437,7 +439,7 @@ class EncryptedStanzaSession(StanzaSession): x.addChild(node=xmpp.DataField(name='sign_algs', value='http://www.w3.org/2000/09/xmldsig#rsa-sha256', typ='hidden')) self.n_s = crypto.generate_nonce() - + x.addChild(node=xmpp.DataField(name='my_nonce', value=base64.b64encode(self.n_s), typ='hidden')) modp_options = [ 5, 14, 2, 1 ] @@ -454,7 +456,7 @@ class EncryptedStanzaSession(StanzaSession): self.status = 'requested-e2e' self.send(request) - + # 4.3 esession response (bob) def verify_options_bob(self, form): negotiated = {'recv_pubkey': None, 'send_pubkey': None} @@ -570,8 +572,8 @@ class EncryptedStanzaSession(StanzaSession): self.d = crypto.powmod(g, self.y, p) to_add = { 'my_nonce': self.n_s, - 'dhkeys': crypto.encode_mpi(self.d), - 'counter': crypto.encode_mpi(self.c_o), + 'dhkeys': crypto.encode_mpi(self.d), + 'counter': crypto.encode_mpi(self.c_o), 'nonce': self.n_o } for name in to_add: @@ -666,7 +668,7 @@ class EncryptedStanzaSession(StanzaSession): self.kc_o, self.km_o, self.ks_o = self.generate_responder_keys(self.k) self.verify_identity(form, self.d, True, 'b') else: - srses = secrets.secrets().retained_secrets(self.conn.name, self.jid.getStripped()) + srses = secrets.secrets().retained_secrets(self.conn.name, self.jid.getStripped()) rshashes = [self.hmac(self.n_s, rs) for (rs,v) in srses] if not rshashes: @@ -677,7 +679,7 @@ class EncryptedStanzaSession(StanzaSession): rshashes = [base64.b64encode(rshash) for rshash in rshashes] result.addChild(node=xmpp.DataField(name='rshashes', value=rshashes)) result.addChild(node=xmpp.DataField(name='dhkeys', value=base64.b64encode(crypto.encode_mpi(e)))) - + self.form_o = ''.join(map(lambda el: xmpp.c14n.c14n(el), form.getChildren())) # MUST securely destroy K unless it will be used later to generate the final shared secret @@ -687,13 +689,13 @@ class EncryptedStanzaSession(StanzaSession): feature.addChild(node=result) self.send(accept) - + if self.sigmai: self.status = 'active' self.enable_encryption = True else: self.status = 'identified-alice' - + # 4.5 esession accept (bob) def accept_e2e_bob(self, form): response = xmpp.Message() @@ -724,7 +726,7 @@ class EncryptedStanzaSession(StanzaSession): # 4.5.4 generating bob's final session keys srs = '' - + srses = secrets.secrets().retained_secrets(self.conn.name, self.jid.getStripped()) rshashes = [base64.b64decode(rshash) for rshash in form.getField('rshashes').getValues()] @@ -767,6 +769,9 @@ class EncryptedStanzaSession(StanzaSession): self.status = 'active' self.enable_encryption = True + if hasattr(self, 'control'): + self.control.print_esession_details() + def final_steps_alice(self, form): srs = '' srses = secrets.secrets().retained_secrets(self.conn.name, self.jid.getStripped()) @@ -793,13 +798,16 @@ class EncryptedStanzaSession(StanzaSession): self.verify_identity(form, self.d, False, 'b') # Note: If Alice discovers an error then she SHOULD ignore any encrypted content she received in the stanza. - + if self.negotiated['logging'] == 'mustnot': self.loggable = False self.status = 'active' self.enable_encryption = True + if hasattr(self, 'control'): + self.control.print_esession_details() + # calculate and store the new retained secret # prompt the user to check the remote party's identity (if necessary) def do_retained_secret(self, k, srs): diff --git a/src/gajim.py b/src/gajim.py index 0b5cc9d2e..51659d9cf 100755 --- a/src/gajim.py +++ b/src/gajim.py @@ -2031,7 +2031,8 @@ class Interface: ctrl = gajim.interface.msg_win_mgr.get_control(str(jid), account) if ctrl: - ctrl.session = gajim.connections[account].make_new_session(str(jid)) + new_sess = gajim.connections[account].make_new_session(str(jid)) + ctrl.set_session(new_sess) return @@ -2044,7 +2045,8 @@ class Interface: contact = gajim.contacts.get_contact(account, str(jid), resource) if not contact: connection = gajim.connections[account] - contact = gajim.contacts.create_contact(jid = jid.getStripped(), resource = resource, show = connection.get_status()) + contact = gajim.contacts.create_contact(jid = jid.getStripped(), + resource = resource, show = connection.get_status()) self.roster.new_chat(contact, account, resource = resource) ctrl = gajim.interface.msg_win_mgr.get_control(str(jid), account) diff --git a/src/message_control.py b/src/message_control.py index 6df9b0d91..b6b584eb2 100644 --- a/src/message_control.py +++ b/src/message_control.py @@ -117,15 +117,26 @@ class MessageControl: return len(gajim.events.get_events(self.account, self.contact.jid)) def set_session(self, session): - if session == self.session: + if hasattr(self, 'session') and session == self.session: return - if self.session: + was_encrypted = False + + if hasattr(self, 'session') and self.session: + if self.session.enable_encryption: + was_encrypted = True + print "starting a new session, dropping the old one!" gajim.connections[self.account].delete_session(self.session.jid, self.session.thread_id) self.session = session + if session: + session.control = self + + if was_encrypted: + self.print_esession_details() + def send_message(self, message, keyID = '', type = 'chat', chatstate = None, msg_id = None, composing_xep = None, resource = None, user_nick = None): @@ -134,7 +145,10 @@ class MessageControl: jid = self.contact.jid if not self.session: - self.session = gajim.connections[self.account].make_new_session(self.contact.get_full_jid()) + fjid = self.contact.get_full_jid() + new_session = gajim.connections[self.account].make_new_session(fjid) + + self.set_session(new_session) # Send and update history return gajim.connections[self.account].send_message(jid, message, keyID,