Welcome to mirror list, hosted at ThFree Co, Russian Federation.

dev.gajim.org/gajim/gajim.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/glade/esession_info_window.glade94
-rw-r--r--data/glade/message_window.glade20
-rw-r--r--src/chat_control.py47
-rw-r--r--src/common/stanza_session.py38
-rw-r--r--src/dialogs.py41
-rw-r--r--src/negotiation.py21
-rw-r--r--src/secrets.py2
-rw-r--r--src/session.py3
8 files changed, 206 insertions, 60 deletions
diff --git a/data/glade/esession_info_window.glade b/data/glade/esession_info_window.glade
new file mode 100644
index 000000000..2951b4913
--- /dev/null
+++ b/data/glade/esession_info_window.glade
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
+<!--Generated with glade3 3.4.0 on Sat Jun 28 20:51:00 2008 -->
+<glade-interface>
+ <widget class="GtkDialog" id="esession_info_window">
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="border_width">5</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="has_separator">False</property>
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="spacing">2</property>
+ <child>
+ <widget class="GtkLabel" id="info_display">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">(ESession info)</property>
+ <property name="wrap">True</property>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHBox" id="verification_info">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <child>
+ <widget class="GtkImage" id="warning">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="stock">gtk-dialog-warning</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">This contact's identity has not been verified.</property>
+ <property name="wrap">True</property>
+ <property name="width_chars">0</property>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkButton" id="verify_now_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">Verify now</property>
+ <property name="response_id">0</property>
+ <signal name="clicked" handler="on_verify_now_button_clicked"/>
+ </widget>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <child>
+ <widget class="GtkButton" id="close_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">Close</property>
+ <property name="response_id">0</property>
+ <signal name="clicked" handler="on_close_button_clicked"/>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+</glade-interface>
diff --git a/data/glade/message_window.glade b/data/glade/message_window.glade
index 4cb02eeac..be0da608d 100644
--- a/data/glade/message_window.glade
+++ b/data/glade/message_window.glade
@@ -111,12 +111,20 @@
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
- <widget class="GtkImage" id="lock_image">
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="no_show_all">True</property>
- <property name="stock">gtk-dialog-authentication</property>
- <property name="icon_size">1</property>
- </widget>
+ <widget class="GtkButton" id="authentication_button">
+ <property name="no_show_all">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="relief">GTK_RELIEF_NONE</property>
+ <property name="focus_on_click">False</property>
+ <property name="response_id">0</property>
+ <child>
+ <widget class="GtkImage" id="lock_image">
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="stock">gtk-dialog-authentication</property>
+ <property name="icon_size">1</property>
+ </widget>
+ </child>
+ </widget>
<packing>
<property name="expand">False</property>
<property name="padding">2</property>
diff --git a/src/chat_control.py b/src/chat_control.py
index 1eba60c08..2ec9cd477 100644
--- a/src/chat_control.py
+++ b/src/chat_control.py
@@ -1056,6 +1056,10 @@ class ChatControl(ChatControlBase):
self.widget_set_visible(self.xml.get_widget('banner_eventbox'),
gajim.config.get('hide_chat_banner'))
+ self.authentication_button = self.xml.get_widget('authentication_button')
+ id = self.authentication_button.connect('clicked', self._on_authentication_button_clicked)
+ self.handlers[id] = self.authentication_button
+
# Add lock image to show chat encryption
self.lock_image = self.xml.get_widget('lock_image')
self.lock_tooltip = gtk.Tooltips()
@@ -1115,11 +1119,18 @@ class ChatControl(ChatControlBase):
self.on_avatar_eventbox_button_press_event)
self.handlers[id] = widget
- self.session = session
+ if not session:
+ session = gajim.connections[self.account].find_controlless_session(self.contact.jid)
+ self.session = session
+
if session:
session.control = self
+ self.session = session
- # Enable ecryption if needed
+ if session.enable_encryption:
+ self.print_esession_details()
+
+ # Enable encryption if needed
e2e_is_active = hasattr(self, 'session') and self.session and self.session.enable_encryption
self.gpg_is_active = False
gpg_pref = gajim.config.get_per('contacts', contact.jid, 'gpg_enabled')
@@ -1134,7 +1145,7 @@ class ChatControl(ChatControlBase):
if self.session:
self.session.loggable = gajim.config.get('log_encrypted_sessions')
self._show_lock_image(self.gpg_is_active, 'GPG', self.gpg_is_active, self.session and \
- self.session.is_loggable())
+ self.session.is_loggable(), self.session and self.session.verified_identity)
self.status_tooltip = gtk.Tooltips()
@@ -1365,7 +1376,7 @@ class ChatControl(ChatControlBase):
self.gpg_is_active)
self._show_lock_image(self.gpg_is_active, 'GPG', self.gpg_is_active, self.session and \
- self.session.is_loggable())
+ self.session.is_loggable(), self.session and self.session.verified_identity)
def _show_lock_image(self, visible, enc_type = '', enc_enabled = False, chat_logged = False, authenticated = False):
'''Set lock icon visibility and create tooltip'''
@@ -1373,22 +1384,26 @@ class ChatControl(ChatControlBase):
status_string = enc_enabled and 'is' or 'is NOT'
logged_string = chat_logged and 'will' or 'will NOT'
- if enc_type == 'OTR':
- authenticated_string = authenticated \
- and ' and authenticated' \
- or ' and NOT authenticated'
+ if authenticated:
+ authenticated_string = ' and authenticated'
+ self.lock_image.set_from_stock('gtk-dialog-authentication', 1)
else:
- authenticated_string = ''
+ authenticated_string = ' and NOT authenticated'
+ self.lock_image.set_from_stock('gtk-dialog-warning', 1)
- tooltip = '%s Encryption %s active%s.\n' \
+ tooltip = '%s encryption %s active%s.\n' \
'Your chat session %s be logged.' % \
- (enc_type, status_string, authenticated_string,
+ (enc_type, status_string, authenticated_string,
logged_string)
- self.lock_tooltip.set_tip(self.lock_image, tooltip)
- self.widget_set_visible(self.lock_image, not visible)
+ self.lock_tooltip.set_tip(self.authentication_button, tooltip)
+ self.widget_set_visible(self.authentication_button, not visible)
self.lock_image.set_sensitive(enc_enabled)
+ def _on_authentication_button_clicked(self, widget):
+ if self.session and self.session.enable_encryption:
+ dialogs.ESessionInfoWindow(self.session)
+
def _process_command(self, message):
if message[0] != '/':
return False
@@ -1588,11 +1603,15 @@ class ChatControl(ChatControlBase):
msg = _('Session WILL NOT be logged')
ChatControlBase.print_conversation_line(self, msg, 'status', '', None)
+
+ if not self.session.verified_identity:
+ ChatControlBase.print_conversation_line(self, 'SAS not verified', 'status', '', None)
else:
msg = _('E2E encryption disabled')
ChatControlBase.print_conversation_line(self, msg, 'status', '', None)
+
self._show_lock_image(e2e_is_active, 'E2E', e2e_is_active, self.session and \
- self.session.is_loggable())
+ self.session.is_loggable(), self.session and self.session.verified_identity)
def print_conversation(self, text, frm='', tim=None, encrypted=False,
subject=None, xhtml=None, simple=False):
diff --git a/src/common/stanza_session.py b/src/common/stanza_session.py
index d8ba17109..1f500d80c 100644
--- a/src/common/stanza_session.py
+++ b/src/common/stanza_session.py
@@ -183,6 +183,9 @@ class EncryptedStanzaSession(StanzaSession):
# _o denotes 'other' (ie. the client at the other end of the session)
self._kc_o = None
+ # has the remote contact's identity ever been verified?
+ self.verified_identity = False
+
# keep the encrypter updated with my latest cipher key
def set_kc_s(self, value):
self._kc_s = value
@@ -338,7 +341,8 @@ class EncryptedStanzaSession(StanzaSession):
raise exceptions.NegotiationError, 'calculated m_%s differs from received m_%s' % (i_o, i_o)
if i_o == 'a' and self.sas_algs == 'sas28x5':
- # XXX not necessary if there's a verified retained secret
+ # we don't need to calculate this if there's a verified retained secret
+ # (but we do anyways)
self.sas = crypto.sas_28x5(m_o, self.form_s)
if self.negotiated['recv_pubkey']:
@@ -844,26 +848,32 @@ class EncryptedStanzaSession(StanzaSession):
if 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):
+ def do_retained_secret(self, k, old_srs):
+ '''calculate the new retained secret. determine if the user needs to check the remote party's identity. set up callbacks for when the identity has been verified.'''
+
new_srs = self.hmac(k, 'New Retained Secret')
+ self.srs = new_srs
+
account = self.conn.name
bjid = self.jid.getStripped()
- if srs:
- if secrets.secrets().srs_verified(account, bjid, srs):
- secrets.secrets().replace_srs(account, bjid, srs, new_srs, True)
- else:
- def _cb(verified):
- secrets.secrets().replace_srs(account, bjid, srs, new_srs, verified)
+ self.verified_identity = False
- self.check_identity(_cb)
+ if old_srs:
+ if secrets.secrets().srs_verified(account, bjid, old_srs):
+ # already had a stored secret verified by the user.
+ secrets.secrets().replace_srs(account, bjid, old_srs, new_srs, True)
+ # continue without warning.
+ self.verified_identity = True
+ else:
+ # had a secret, but it wasn't verified.
+ secrets.secrets().replace_srs(account, bjid, old_srs, new_srs, False)
else:
- def _cb(verified):
- secrets.secrets().save_new_srs(account, bjid, new_srs, verified)
+ # we don't even have an SRS
+ secrets.secrets().save_new_srs(account, bjid, new_srs, False)
- self.check_identity(_cb)
+ def _verified_srs_cb(self):
+ secrets.secrets().replace_srs(self.conn.name, self.jid.getStripped(), self.srs, self.srs, True)
def make_dhfield(self, modp_options, sigmai):
dhs = []
diff --git a/src/dialogs.py b/src/dialogs.py
index 07e88e70e..3bff3c7ce 100644
--- a/src/dialogs.py
+++ b/src/dialogs.py
@@ -3780,7 +3780,7 @@ class DataFormWindow(Dialog):
self.dataform_widget.data_form = self.dataform
self.dataform_widget.show_all()
self.vbox.pack_start(self.dataform_widget)
-
+
def on_ok(self):
form = self.dataform_widget.data_form
if isinstance(self.df_response_ok, tuple):
@@ -3788,3 +3788,42 @@ class DataFormWindow(Dialog):
else:
self.df_response_ok(form)
self.destroy()
+
+class ESessionInfoWindow:
+ '''Class for displaying information about a XEP-0116 encrypted session'''
+ def __init__(self, session):
+ self.session = session
+
+ self.xml = gtkgui_helpers.get_glade('esession_info_window.glade')
+ self.xml.signal_autoconnect(self)
+
+ self.update_info()
+
+ self.window = self.xml.get_widget('esession_info_window')
+ self.window.show_all()
+
+ def update_info(self):
+ labeltext = _('''Your chat session with %s is encrypted.\n\nSAS is: %s''') % (self.session.jid, self.session.sas)
+
+ if self.session.verified_identity:
+ labeltext += '\n\n' + _('''You have already verified this contact's identity.''')
+ w = self.xml.get_widget('verification_info')
+ w.set_no_show_all(True)
+ w.hide()
+
+ self.xml.get_widget('info_display').set_text(labeltext)
+
+ def on_close_button_clicked(self, widget):
+ self.window.destroy()
+
+ def on_verify_now_button_clicked(self, widget):
+ pritext = _('''Have you verified the remote contact's identity?''')
+ sectext = _('''To prevent a man-in-the-middle attack, you should speak to this person directly (in person or on the phone) and verify that they see the same SAS as you.\n\nThis session's SAS: %s''') % self.session.sas
+ sectext += '\n\n' + _('Did you talk to the remote contact and verify the SAS?')
+
+ dialog = YesNoDialog(pritext, sectext)
+
+ if dialog.get_response() == gtk.RESPONSE_YES:
+ self.session._verified_srs_cb()
+ self.session.verified_identity = True
+ self.update_info()
diff --git a/src/negotiation.py b/src/negotiation.py
index 8cc4c9ad5..c2b1fcf56 100644
--- a/src/negotiation.py
+++ b/src/negotiation.py
@@ -14,23 +14,6 @@ def describe_features(features):
elif features['logging'] == 'mustnot':
return _('- messages will not be logged')
-def show_sas_dialog(session, jid, sas, on_success):
- def success_cb(checked):
- on_success(checked)
-
- def failure_cb():
- session.reject_negotiation()
-
- dialogs.ConfirmationDialogCheck(_('''OK to continue with negotiation?'''),
- _('''You've begun an encrypted session with %s, but it can't be guaranteed that you're talking directly to the person you think you are.
-
-You should speak with them directly (in person or on the phone) and confirm that their Short Authentication String is identical to this one: %s
-
-Would you like to continue with the encrypted session?''') % (jid, sas),
-
- _('Yes, I verified the Short Authentication String'),
- on_response_ok=success_cb, on_response_cancel=failure_cb, is_modal=False)
-
class FeatureNegotiationWindow:
'''FeatureNegotiotionWindow class'''
def __init__(self, account, jid, session, form):
@@ -67,8 +50,6 @@ class FeatureNegotiationWindow:
self.window.destroy()
def on_cancel_button_clicked(self, widget):
- # XXX determine whether to reveal presence
-
rejection = xmpp.Message(self.jid)
rejection.setThread(self.session.thread_id)
feature = rejection.NT.feature
@@ -80,8 +61,6 @@ class FeatureNegotiationWindow:
feature.addChild(node=x)
- # XXX optional <body/>
-
gajim.connections[self.account].send_stanza(rejection)
self.window.destroy()
diff --git a/src/secrets.py b/src/secrets.py
index a95f4cd0d..0caeda7bf 100644
--- a/src/secrets.py
+++ b/src/secrets.py
@@ -136,7 +136,7 @@ class Secrets:
# has the user verified this retained secret?
def srs_verified(self, account, jid, srs):
return self.find_srs(account, jid, srs)[1]
-
+
def replace_srs(self, account, jid, old_secret, new_secret, verified):
our_secrets = self.srs[account][jid]
diff --git a/src/session.py b/src/session.py
index 651ce2795..5016e3d5a 100644
--- a/src/session.py
+++ b/src/session.py
@@ -334,9 +334,6 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession):
# ---- ESessions stuff ---
- def check_identity(self, on_success):
- negotiation.show_sas_dialog(self, self.jid, self.sas, on_success)
-
def handle_negotiation(self, form):
if form.getField('accept') and not form['accept'] in ('1', 'true'):
self.cancelled_negotiation()