Author: bct
Date: 2007-08-20 10:16:48 +0200 (Mon, 20 Aug 2007)
New Revision: 8511
Modified:
branches/e2e_encryption/src/chat_control.py
branches/e2e_encryption/src/common/connection_handlers.py
branches/e2e_encryption/src/common/stanza_session.py
branches/e2e_encryption/src/gajim.py
Log:
esession bugfixes
Modified: branches/e2e_encryption/src/chat_control.py
===================================================================
--- branches/e2e_encryption/src/chat_control.py 2007-08-20 08:16:26 UTC (rev 8510)
+++ branches/e2e_encryption/src/chat_control.py 2007-08-20 08:16:48 UTC (rev 8511)
@@ -971,6 +971,9 @@
self.session = session
+ # does this window have an existing, active esession?
+ self.esessioned = False
+
self.status_tooltip = gtk.Tooltips()
self.update_ui()
# restore previous conversation
@@ -1236,13 +1239,13 @@
contact = self.contact
+ encrypted = bool(self.session) and self.session.enable_encryption
+
keyID = ''
- encrypted = False
if self.xml.get_widget('gpg_togglebutton').get_active():
keyID = contact.keyID
encrypted = True
-
chatstates_on = gajim.config.get('outgoing_chat_state_notifications') != \
'disabled'
composing_jep = contact.composing_jep
@@ -1352,18 +1355,46 @@
kind = 'info'
name = ''
else:
- ec = gajim.encrypted_chats[self.account]
- if encrypted and jid not in ec:
- msg = _('Encryption enabled')
+ # ESessions
+ if 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:
+ 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)
- ec.append(jid)
- elif not encrypted and jid in ec:
- msg = _('Encryption disabled')
- ChatControlBase.print_conversation_line(self, msg,
- 'status', '', tim)
- ec.remove(jid)
- self.xml.get_widget('gpg_togglebutton').set_active(encrypted)
+ self.esessioned = False
+ else:
+ # GPG encryption
+ ec = gajim.encrypted_chats[self.account]
+ if encrypted and jid not in ec:
+ msg = _('Encryption enabled')
+ ChatControlBase.print_conversation_line(self, msg,
+ 'status', '', tim)
+ ec.append(jid)
+ elif not encrypted and jid in ec:
+ msg = _('Encryption disabled')
+ ChatControlBase.print_conversation_line(self, msg,
+ 'status', '', tim)
+ ec.remove(jid)
+ self.xml.get_widget('gpg_togglebutton').set_active(encrypted)
+
if not frm:
kind = 'incoming'
name = contact.get_shown_name()
@@ -1949,9 +1980,15 @@
if self.session and self.session.enable_encryption:
self.session.terminate_e2e()
+ 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)
else:
if not self.session:
Modified: branches/e2e_encryption/src/common/connection_handlers.py
===================================================================
--- branches/e2e_encryption/src/common/connection_handlers.py 2007-08-20 08:16:26 UTC (rev 8510)
+++ branches/e2e_encryption/src/common/connection_handlers.py 2007-08-20 08:16:48 UTC (rev 8511)
@@ -1451,18 +1451,22 @@
self._InitE2ECB(con, msg, session)
encrypted = False
+ tim = msg.getTimestamp()
+ tim = time.strptime(tim, '%Y%m%dT%H:%M:%S')
+ tim = time.localtime(timegm(tim))
- e2eTag = msg.getTag('c', namespace = common.xmpp.NS_STANZA_CRYPTO)
- if e2eTag:
+ e2e_tag = msg.getTag('c', namespace = common.xmpp.NS_STANZA_CRYPTO)
+ if e2e_tag:
encrypted = True
- msg = session.decrypt_stanza(msg)
+ try:
+ msg = session.decrypt_stanza(msg)
+ except:
+ self.dispatch('FAILED_DECRYPT', (frm, tim))
+
msgtxt = msg.getBody()
msghtml = msg.getXHTML()
subject = msg.getSubject() # if not there, it's None
- tim = msg.getTimestamp()
- tim = time.strptime(tim, '%Y%m%dT%H:%M:%S')
- tim = time.localtime(timegm(tim))
jid = helpers.get_jid_from_iq(msg)
chatstate = None
encTag = msg.getTag('x', namespace = common.xmpp.NS_ENCRYPTED)
@@ -1573,6 +1577,8 @@
def get_session(self, jid, thread_id, type):
'''returns an existing session between this connection and 'jid', returns a new one if none exist.'''
+ print repr(self.sessions)
+
session = self.find_session(jid, thread_id, type)
if session:
Modified: branches/e2e_encryption/src/common/stanza_session.py
===================================================================
--- branches/e2e_encryption/src/common/stanza_session.py 2007-08-20 08:16:26 UTC (rev 8510)
+++ branches/e2e_encryption/src/common/stanza_session.py 2007-08-20 08:16:48 UTC (rev 8511)
@@ -289,7 +289,7 @@
calculated_mac = self.hmac(self.km_o, macable + self.encode_mpi_with_padding(self.c_o))
if not calculated_mac == received_mac:
- raise 'bad signature (%s != %s)' % (repr(received_mac), repr(calculated_mac))
+ raise exceptions.DecryptionError, 'bad signature'
m_final = base64.b64decode(c.getTagData('data'))
m_compressed = self.decrypt(m_final)
@@ -298,7 +298,7 @@
try:
parsed = xmpp.Node(node='<node>' + plaintext + '</node>')
except:
- raise exceptions.DecryptionError
+ raise exceptions.DecryptionError, 'decrypted <data/> not parseable as XML'
for child in parsed.getChildren():
stanza.addChild(node=child)
@@ -377,12 +377,12 @@
content += self.form_o + form_o2
mac_o_calculated = self.hmac(self.ks_o, content)
-
+
if self.negotiated['recv_pubkey']:
hash = self.sha256(mac_o_calculated)
if not eir_pubkey.verify(hash, signature):
- raise exceptions.NegotiationError, 'public key signature verification failed!'
+ raise exceptions.NegotiationError, 'public key signature verification failed!'
elif mac_o_calculated != mac_o:
raise exceptions.NegotiationError, 'calculated mac_%s differs from received mac_%s' % (i_o, i_o)
@@ -589,7 +589,7 @@
self.n_o = base64.b64decode(form['my_nonce'])
dhhashes = form.getField('dhhashes').getValues()
- self.He = base64.b64decode(dhhashes[group_order].encode("utf8"))
+ self.negotiated['He'] = base64.b64decode(dhhashes[group_order].encode("utf8"))
bytes = int(self.n / 8)
@@ -741,7 +741,8 @@
e = self.decode_mpi(base64.b64decode(form['dhkeys']))
p = dh.primes[self.modp]
- # XXX return <feature-not-implemented> if hash(e) != He
+ if self.sha256(self.encode_mpi(e)) != self.negotiated['He']:
+ raise exceptions.NegotiationError, 'SHA256(e) != He'
k = self.get_shared_secret(e, self.y, p)
@@ -903,11 +904,18 @@
def fail_bad_negotiation(self, reason):
'''they've tried to feed us a bogus value, send an error and cancel everything.'''
+
err = xmpp.Error(xmpp.Message(), xmpp.ERR_FEATURE_NOT_IMPLEMENTED)
err.T.error.T.text.setData(reason)
self.send(err)
+
self.status = None
+ self.enable_encryption = False
+ # this prevents the MAC check on decryption from succeeding,
+ # preventing falsified messages from going through.
+ self.km_o = ''
+
def is_loggable(self):
account = self.conn.name
no_log_for = gajim.config.get_per('accounts', account, 'no_log_for')
Modified: branches/e2e_encryption/src/gajim.py
===================================================================
--- branches/e2e_encryption/src/gajim.py 2007-08-20 08:16:26 UTC (rev 8510)
+++ branches/e2e_encryption/src/gajim.py 2007-08-20 08:16:48 UTC (rev 8511)
@@ -1701,6 +1701,15 @@
atom_entry, = data
AtomWindow.newAtomEntry(atom_entry)
+ def handle_event_failed_decrypt(self, account, data):
+ jid, tim = data
+
+ ctrl = self.msg_win_mgr.get_control(jid, account)
+ if ctrl:
+ ctrl.print_conversation_line('Unable to decrypt message from %s\nIt may have been tampered with.' % (jid), 'status', '', tim)
+ else:
+ print 'failed decrypt, unable to find a control to notify you in.'
+
def handle_session_negotiation(self, account, data):
jid, session, form = data
@@ -1713,12 +1722,25 @@
# encrypted session states. these are described in stanza_session.py
# bob responds
- if form.getType() == 'form' and u'e2e' in \
- map(lambda x: x[1], form.getField('security').getOptions()):
+ if form.getType() == 'form' and 'security' in form.asDict():
def continue_with_negotiation(*args):
if len(args):
self.dialog.destroy()
+ # we don't support 3-message negotiation as the responder
+ if 'dhkeys' in form.asDict():
+ err = xmpp.Error(xmpp.Message(), xmpp.ERR_FEATURE_NOT_IMPLEMENTED)
+
+ feature = xmpp.Node(xmpp.NS_FEATURE + ' feature')
+ field = xmpp.Node('field')
+ field['var'] = 'dhkeys'
+
+ feature.addChild(node=field)
+ err.addChild(node=feature)
+
+ session.send(err)
+ return
+
negotiated, not_acceptable, ask_user = session.verify_options_bob(form)
if ask_user:
@@ -1776,8 +1798,11 @@
dialog.destroy()
negotiated.update(ask_user)
-
- session.accept_e2e_alice(form, negotiated)
+
+ try:
+ session.accept_e2e_alice(form, negotiated)
+ except exceptions.NegotiationError, details:
+ session.fail_bad_negotiation(details)
def reject_nondefault_options(widget):
session.reject_negotiation()
@@ -1788,7 +1813,10 @@
on_response_yes = accept_nondefault_options,
on_response_no = reject_nondefault_options)
else:
- session.accept_e2e_alice(form, negotiated)
+ try:
+ session.accept_e2e_alice(form, negotiated)
+ except exceptions.NegotiationError, details:
+ session.fail_bad_negotiation(details)
return
elif session.status == 'responded-e2e' and form.getType() == 'result':
@@ -1802,7 +1830,7 @@
return
elif session.status == 'identified-alice' and form.getType() == 'result':
session.check_identity = lambda: negotiation.show_sas_dialog(jid, session.sas)
-
+
try:
session.final_steps_alice(form)
except exceptions.NegotiationError, details:
@@ -1819,7 +1847,7 @@
ctrl = gajim.interface.msg_win_mgr.get_control(str(jid), account)
if ctrl:
- ctrl.session = gajim.connections[self.account].make_new_session(str(jid))
+ ctrl.session = gajim.connections[account].make_new_session(str(jid))
return
@@ -2266,6 +2294,7 @@
'SIGNED_IN': self.handle_event_signed_in,
'METACONTACTS': self.handle_event_metacontacts,
'ATOM_ENTRY': self.handle_atom_entry,
+ 'FAILED_DECRYPT': self.handle_event_failed_decrypt,
'PRIVACY_LISTS_RECEIVED': self.handle_event_privacy_lists_received,
'PRIVACY_LIST_RECEIVED': self.handle_event_privacy_list_received,
'PRIVACY_LISTS_ACTIVE_DEFAULT': \