plugin OTR: authentication management + fixed a bug in setState (due to a wrong docstring in potr.context.getCurrentTrust)
This commit is contained in:
parent
0443149ef8
commit
df69c77ea3
2 changed files with 101 additions and 6 deletions
|
|
@ -25,6 +25,7 @@ from sat.core.constants import Const as C
|
|||
from sat.core.log import getLogger
|
||||
from sat.core import exceptions
|
||||
log = getLogger(__name__)
|
||||
from sat.tools import xml_tools
|
||||
from twisted.words.protocols.jabber import jid
|
||||
from twisted.python import failure
|
||||
from twisted.internet import defer
|
||||
|
|
@ -34,6 +35,7 @@ from sat.memory import persistent
|
|||
NS_OTR = "otr_plugin"
|
||||
PRIVATE_KEY = "PRIVATE KEY"
|
||||
MAIN_MENU = D_('OTR')
|
||||
AUTH_TXT = D_("To authenticate your correspondent, you need to give your below fingerprint *BY AN EXTERNAL CANAL* (i.e. not in this chat), and check that the one he give you is the same as below. If there is a mismatch, there can be a spy between you !")
|
||||
|
||||
DEFAULT_POLICY_FLAGS = {
|
||||
'ALLOW_V1':False,
|
||||
|
|
@ -54,7 +56,6 @@ PLUGIN_INFO = {
|
|||
|
||||
|
||||
class Context(potr.context.Context):
|
||||
|
||||
def __init__(self, host, account, other_jid):
|
||||
super(Context, self).__init__(account, other_jid)
|
||||
self.host = host
|
||||
|
|
@ -88,7 +89,7 @@ class Context(potr.context.Context):
|
|||
feedback = _(u"/!\\ conversation with %(other_jid)s is now UNENCRYPTED") % {'other_jid': self.peer.full()}
|
||||
elif state == potr.context.STATE_ENCRYPTED:
|
||||
try:
|
||||
fingerprint, trusted = self.getCurrentTrust()
|
||||
trusted = self.getCurrentTrust()
|
||||
except TypeError:
|
||||
trusted = False
|
||||
trusted_str = _(u"trusted") if trusted else _(u"untrusted")
|
||||
|
|
@ -138,6 +139,28 @@ class Account(potr.context.Account):
|
|||
self.client.otr_data[PRIVATE_KEY] = encrypted_priv_key
|
||||
d.addCallback(save_encrypted_key)
|
||||
|
||||
def loadTrusts(self):
|
||||
trust_data = self.client.otr_data.get('trust', {})
|
||||
for jid_, jid_data in trust_data.iteritems():
|
||||
for fingerprint, trust_level in jid_data.iteritems():
|
||||
log.debug('setting trust for {jid}: [{fingerprint}] = "{trust_level}"'.format(jid=jid_, fingerprint=fingerprint, trust_level=trust_level))
|
||||
self.trusts.setdefault(jid.JID(jid_), {})[fingerprint] = trust_level
|
||||
|
||||
def saveTrusts(self):
|
||||
log.debug("saving trusts for {profile}".format(profile=self.client.profile))
|
||||
log.debug("trusts = {}".format(self.client.otr_data['trust']))
|
||||
self.client.otr_data.force('trust')
|
||||
|
||||
def setTrust(self, other_jid, fingerprint, trustLevel):
|
||||
try:
|
||||
trust_data = self.client.otr_data['trust']
|
||||
except KeyError:
|
||||
trust_data = {}
|
||||
self.client.otr_data['trust'] = trust_data
|
||||
jid_data = trust_data.setdefault(other_jid.full(), {})
|
||||
jid_data[fingerprint] = trustLevel
|
||||
super(Account, self).setTrust(other_jid, fingerprint, trustLevel)
|
||||
|
||||
|
||||
class ContextManager(object):
|
||||
|
||||
|
|
@ -169,6 +192,7 @@ class OTR(object):
|
|||
host.trigger.add("sendMessage", self.sendMessageTrigger, priority=100000)
|
||||
host.importMenu((MAIN_MENU, D_("Start/Refresh")), self._startRefresh, security_limit=0, help_string=D_("Start or refresh an OTR session"), type_=C.MENU_SINGLE)
|
||||
host.importMenu((MAIN_MENU, D_("End session")), self._endSession, security_limit=0, help_string=D_("Finish an OTR session"), type_=C.MENU_SINGLE)
|
||||
host.importMenu((MAIN_MENU, D_("Authenticate")), self._authenticate, security_limit=0, help_string=D_("Authenticate user/see your fingerprint"), type_=C.MENU_SINGLE)
|
||||
|
||||
def _fixPotr(self):
|
||||
# FIXME: potr fix for bad unicode handling
|
||||
|
|
@ -189,7 +213,7 @@ class OTR(object):
|
|||
@defer.inlineCallbacks
|
||||
def profileConnected(self, profile):
|
||||
client = self.host.getClient(profile)
|
||||
self.context_managers[profile] = ContextManager(self.host, client)
|
||||
ctxMng = self.context_managers[profile] = ContextManager(self.host, client)
|
||||
client.otr_data = persistent.PersistentBinaryDict(NS_OTR, profile)
|
||||
yield client.otr_data.load()
|
||||
encrypted_priv_key = client.otr_data.get(PRIVATE_KEY, None)
|
||||
|
|
@ -198,6 +222,7 @@ class OTR(object):
|
|||
client.otr_priv_key = potr.crypt.PK.parsePrivateKey(priv_key.decode('hex'))[0]
|
||||
else:
|
||||
client.otr_priv_key = None
|
||||
ctxMng.account.loadTrusts()
|
||||
|
||||
def _startRefresh(self, menu_data, profile):
|
||||
"""Start or refresh an OTR session
|
||||
|
|
@ -234,6 +259,76 @@ class OTR(object):
|
|||
otrctx.disconnect()
|
||||
return {}
|
||||
|
||||
def _authenticate(self, menu_data, profile):
|
||||
"""Authenticate other user and see our own fingerprint
|
||||
|
||||
@param menu_data: %(menu_data)s
|
||||
@param profile: %(doc_profile)s
|
||||
"""
|
||||
try:
|
||||
to_jid = jid.JID(menu_data['jid'])
|
||||
if not to_jid.resource:
|
||||
to_jid.resource = self.host.memory.getLastResource(to_jid, profile) # FIXME: temporary and unsecure, must be changed when frontends are refactored
|
||||
except KeyError:
|
||||
log.error(_("jid key is not present !"))
|
||||
return defer.fail(exceptions.DataError)
|
||||
otrctx = self.context_managers[profile].getContextForUser(to_jid)
|
||||
|
||||
priv_key = otrctx.user.client.otr_priv_key
|
||||
|
||||
if priv_key is None:
|
||||
# we have no private key yet
|
||||
dialog = xml_tools.XMLUI(C.XMLUI_DIALOG,
|
||||
dialog_opt = {C.XMLUI_DATA_TYPE: C.XMLUI_DIALOG_MESSAGE,
|
||||
C.XMLUI_DATA_MESS: _("You have no private key yet, start an OTR conversation to have one"),
|
||||
C.XMLUI_DATA_LVL: C.XMLUI_DATA_LVL_WARNING
|
||||
},
|
||||
title = _("No private key"),
|
||||
)
|
||||
return {'xmlui': dialog.toXml()}
|
||||
|
||||
other_fingerprint = otrctx.getCurrentKey()
|
||||
|
||||
if other_fingerprint is None:
|
||||
# we have a private key, but not the fingerprint of our correspondent
|
||||
dialog = xml_tools.XMLUI(C.XMLUI_DIALOG,
|
||||
dialog_opt = {C.XMLUI_DATA_TYPE: C.XMLUI_DIALOG_MESSAGE,
|
||||
C.XMLUI_DATA_MESS: _("Your fingerprint is\n{fingerprint}\n\nStart an OTR conversation to have your correspondent one.").format(fingerprint=priv_key),
|
||||
C.XMLUI_DATA_LVL: C.XMLUI_DATA_LVL_INFO
|
||||
},
|
||||
title = _("Fingerprint"),
|
||||
)
|
||||
return {'xmlui': dialog.toXml()}
|
||||
|
||||
def setTrust(raw_data, profile):
|
||||
# This method is called when authentication form is submited
|
||||
data = xml_tools.XMLUIResult2DataFormResult(raw_data)
|
||||
if data['match'] == 'yes':
|
||||
otrctx.setCurrentTrust('verified')
|
||||
note_msg = _("Your correspondant {correspondent} is now TRUSTED")
|
||||
else:
|
||||
otrctx.setCurrentTrust('')
|
||||
note_msg = _("Your correspondant {correspondent} is now UNTRUSTED")
|
||||
note = xml_tools.XMLUI(C.XMLUI_DIALOG, dialog_opt = {
|
||||
C.XMLUI_DATA_TYPE: C.XMLUI_DIALOG_NOTE,
|
||||
C.XMLUI_DATA_MESS: note_msg.format(correspondent=otrctx.user.name)}
|
||||
)
|
||||
return {'xmlui': note.toXml()}
|
||||
|
||||
submit_id = self.host.registerCallback(setTrust, with_data=True, one_shot=True)
|
||||
trusted = bool(otrctx.getCurrentTrust())
|
||||
|
||||
xmlui = xml_tools.XMLUI(C.XMLUI_FORM, title=_('Authentication (%s)') % to_jid.full(), submit_id=submit_id)
|
||||
xmlui.addText(_(AUTH_TXT))
|
||||
xmlui.addDivider()
|
||||
xmlui.addText(_("Your own fingerprint is:\n{fingerprint}").format(fingerprint=priv_key))
|
||||
xmlui.addText(_("Your correspondent fingerprint should be:\n{fingerprint}").format(fingerprint=other_fingerprint))
|
||||
xmlui.addDivider('blank')
|
||||
xmlui.changeContainer('pairs')
|
||||
xmlui.addLabel(_('Is your correspondent fingerprint the same as here ?'))
|
||||
xmlui.addList("match", [('yes', _('yes')),('no', _('no'))], ['yes' if trusted else 'no'])
|
||||
return {'xmlui': xmlui.toXml()}
|
||||
|
||||
def _receivedTreatment(self, data, profile):
|
||||
from_jid = jid.JID(data['from'])
|
||||
log.debug(u"_receivedTreatment [from_jid = %s]" % from_jid)
|
||||
|
|
|
|||
|
|
@ -740,9 +740,9 @@ class ListWidget(InputWidget):
|
|||
"""
|
||||
|
||||
@param xmlui
|
||||
@param options (list[string, tuple]): each option can be given as:
|
||||
- a string if the label and the value are the same
|
||||
- a couple if the label and the value differ
|
||||
@param options (list[option]): each option can be given as:
|
||||
- a single string if the label and the value are the same
|
||||
- a tuple with a couple of string (value,label) if the label and the value differ
|
||||
@param selected (list[string]): list of the selected values
|
||||
@param style (string)
|
||||
@param name (string)
|
||||
|
|
|
|||
Loading…
Reference in a new issue