diff --git a/data/glade/esession_info_window.glade b/data/glade/esession_info_window.glade
new file mode 100644
index 0000000000000000000000000000000000000000..2951b4913d5573e220d593aa5784f8a510a04539
--- /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 4cb02eeac77c613d20499dd459b1cf2eac8f0afa..be0da608d029df6f7a10631d428c006730f31a53 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 1eba60c082e7f54fa338a78cdda2bca8244d8c6d..2ec9cd477d2647c23764e9096ff1e71a2883376d 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 d8ba17109dc3393070e091f2340d5c842f74d447..1f500d80cd641c77aa6d21a1b24cb99bd03d70ee 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 07e88e70ec42216cfb488129715558c4f11430ea..3bff3c7ce6158cb6056343203563a61d382e660a 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 8cc4c9ad5f1c52da6d17a42197824ac746b66c23..c2b1fcf56f1972f6e2ff814190c6ae61ac3efef6 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 a95f4cd0d609f82c54ca7e2446b39380fa81b508..0caeda7bfebcf08c5429858e8e78d4214acb4b05 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 651ce2795dcf5a9f2513a4def51e0b1640a7676c..5016e3d5abcbf5d3d6f6726d6eeab6723c5db0c2 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()