make esession authentication warning less obtrusive

This commit is contained in:
Brendan Taylor 2008-06-29 04:39:29 +00:00
parent 408d3b4ff6
commit b490904454
8 changed files with 206 additions and 60 deletions

View File

@ -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>

View File

@ -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>

View File

@ -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):

View File

@ -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)
self.verified_identity = False
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:
def _cb(verified):
secrets.secrets().replace_srs(account, bjid, srs, new_srs, verified)
self.check_identity(_cb)
# 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 = []

View File

@ -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()

View File

@ -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()

View File

@ -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]

View File

@ -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()