plugin sec OTR: fixed OTR:
- fixed bridge calls
- use deferred translation
- unicode string fixes
- various other improvments
This commit is contained in:
parent
dc4e3c9d8c
commit
d5fbafd979
1 changed files with 87 additions and 53 deletions
|
|
@ -37,9 +37,10 @@ import uuid
|
|||
|
||||
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 gives you is the same as below. If there is a mismatch, there can be a spy between you!")
|
||||
DROP_TXT = D_("You private key is used to encrypt messages for your correspondent, nobody except you must know it, if you are in doubt, you should drop it!\n\nAre you sure you want to drop your private key?")
|
||||
OTR_MENU = D_(u'OTR')
|
||||
AUTH_TXT = D_(u"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 gives you is the same as below. If there is a mismatch, there can be a spy between you!")
|
||||
DROP_TXT = D_(u"You private key is used to encrypt messages for your correspondent, nobody except you must know it, if you are in doubt, you should drop it!\n\nAre you sure you want to drop your private key?")
|
||||
NO_LOG = D_(u"/!\\Your history is not logged anymore, and most of advanced features are disabled !") # FIXME: not used at the moment
|
||||
|
||||
DEFAULT_POLICY_FLAGS = {
|
||||
'ALLOW_V1':False,
|
||||
|
|
@ -47,6 +48,7 @@ DEFAULT_POLICY_FLAGS = {
|
|||
'REQUIRE_ENCRYPTION':True,
|
||||
}
|
||||
|
||||
|
||||
PLUGIN_INFO = {
|
||||
"name": "OTR",
|
||||
"import_name": "OTR",
|
||||
|
|
@ -55,7 +57,7 @@ PLUGIN_INFO = {
|
|||
"dependencies": [],
|
||||
"main": "OTR",
|
||||
"handler": "no",
|
||||
"description": _("""Implementation of OTR""")
|
||||
"description": _(u"""Implementation of OTR""")
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -103,20 +105,28 @@ class Context(potr.context.Context):
|
|||
trusted_str = _(u"trusted") if trusted else _(u"untrusted")
|
||||
|
||||
if old_state == potr.context.STATE_ENCRYPTED:
|
||||
feedback = _(u"%(trusted)s OTR conversation with %(other_jid)s REFRESHED") % {'trusted': trusted_str, 'other_jid': self.peer.full()}
|
||||
feedback = D_(u"{trusted} OTR conversation with {other_jid} REFRESHED").format(
|
||||
trusted = trusted_str,
|
||||
other_jid = self.peer.full())
|
||||
else:
|
||||
feedback = _(u"%(trusted)s Encrypted OTR conversation started with %(other_jid)s\n/!\\ Your history is not logged anymore, and most of advanced features are disabled !") % {'trusted': trusted_str, 'other_jid': self.peer.full()}
|
||||
feedback = D_(u"{trusted} Encrypted OTR conversation started with {other_jid}\n{no_log}").format(
|
||||
trusted = trusted_str,
|
||||
other_jid = self.peer.full(),
|
||||
no_log = NO_LOG)
|
||||
elif state == potr.context.STATE_FINISHED:
|
||||
feedback = _(u"OTR conversation with %(other_jid)s is FINISHED") % {'other_jid': self.peer.full()}
|
||||
feedback = D_(u"OTR conversation with {other_jid} is FINISHED").format(other_jid = self.peer.full())
|
||||
else:
|
||||
log.error(_(u"Unknown OTR state"))
|
||||
log.error(D_(u"Unknown OTR state"))
|
||||
return
|
||||
|
||||
client = self.user.client
|
||||
self.host.bridge.messageNew(client.jid.full(),
|
||||
feedback,
|
||||
mess_type=C.MESS_TYPE_INFO,
|
||||
self.host.bridge.messageNew(uid=unicode(uuid.uuid4()),
|
||||
timestamp=time.time(),
|
||||
from_jid=client.jid.full(),
|
||||
to_jid=self.peer.full(),
|
||||
message={u'': feedback},
|
||||
subject={},
|
||||
mess_type=C.MESS_TYPE_INFO,
|
||||
extra={},
|
||||
profile=client.profile)
|
||||
# TODO: send signal to frontends
|
||||
|
|
@ -150,7 +160,7 @@ class Account(potr.context.Account):
|
|||
def savePrivkey(self):
|
||||
log.debug(u"savePrivkey")
|
||||
if self.privkey is None:
|
||||
raise exceptions.InternalError(_("Save is called but privkey is None !"))
|
||||
raise exceptions.InternalError(_(u"Save is called but privkey is None !"))
|
||||
priv_key = self.privkey.serializePrivateKey().encode('hex')
|
||||
d = self.host.memory.encryptValue(priv_key, self.client.profile)
|
||||
def save_encrypted_key(encrypted_priv_key):
|
||||
|
|
@ -210,10 +220,10 @@ class OTR(object):
|
|||
host.trigger.add("MessageReceived", self.MessageReceivedTrigger, priority=100000)
|
||||
host.trigger.add("messageSend", self.messageSendTrigger, priority=100000)
|
||||
host.bridge.addMethod("skipOTR", ".plugin", in_sign='s', out_sign='', method=self._skipOTR) # FIXME: must be removed, must be done on per-message basis
|
||||
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)
|
||||
host.importMenu((MAIN_MENU, D_("Drop private key")), self._dropPrivKey, security_limit=0, type_=C.MENU_SINGLE)
|
||||
host.importMenu((OTR_MENU, D_(u"Start/Refresh")), self._otrStartRefresh, security_limit=0, help_string=D_(u"Start or refresh an OTR session"), type_=C.MENU_SINGLE)
|
||||
host.importMenu((OTR_MENU, D_(u"End session")), self._otrSessionEnd, security_limit=0, help_string=D_(u"Finish an OTR session"), type_=C.MENU_SINGLE)
|
||||
host.importMenu((OTR_MENU, D_(u"Authenticate")), self._otrAuthenticate, security_limit=0, help_string=D_(u"Authenticate user/see your fingerprint"), type_=C.MENU_SINGLE)
|
||||
host.importMenu((OTR_MENU, D_(u"Drop private key")), self._dropPrivKey, security_limit=0, type_=C.MENU_SINGLE)
|
||||
host.trigger.add("presenceReceived", self._presenceReceivedTrigger)
|
||||
|
||||
def _fixPotr(self):
|
||||
|
|
@ -270,7 +280,7 @@ class OTR(object):
|
|||
except KeyError:
|
||||
pass
|
||||
|
||||
def _startRefresh(self, menu_data, profile):
|
||||
def _otrStartRefresh(self, menu_data, profile):
|
||||
"""Start or refresh an OTR session
|
||||
|
||||
@param menu_data: %(menu_data)s
|
||||
|
|
@ -279,17 +289,24 @@ class OTR(object):
|
|||
client = self.host.getClient(profile)
|
||||
try:
|
||||
to_jid = jid.JID(menu_data['jid'])
|
||||
if not to_jid.resource:
|
||||
to_jid.resource = self.host.memory.getMainResource(client, to_jid) # FIXME: temporary and unsecure, must be changed when frontends are refactored
|
||||
except KeyError:
|
||||
log.error(_("jid key is not present !"))
|
||||
log.error(_(u"jid key is not present !"))
|
||||
return defer.fail(exceptions.DataError)
|
||||
otrctx = self.context_managers[profile].getContextForUser(to_jid)
|
||||
query = otrctx.messageSend(0, '?OTRv?')
|
||||
otrctx.inject(query)
|
||||
self.startRefresh(client, to_jid)
|
||||
return {}
|
||||
|
||||
def _endSession(self, menu_data, profile):
|
||||
def startRefresh(self, client, to_jid):
|
||||
"""Start or refresh an OTR session
|
||||
|
||||
@param to_jid(jid.JID): jid to start encrypted session with
|
||||
"""
|
||||
if not to_jid.resource:
|
||||
to_jid.resource = self.host.memory.getMainResource(client, to_jid) # FIXME: temporary and unsecure, must be changed when frontends are refactored
|
||||
otrctx = self.context_managers[client.profile].getContextForUser(to_jid)
|
||||
query = otrctx.sendMessage(0, '?OTRv?')
|
||||
otrctx.inject(query)
|
||||
|
||||
def _otrSessionEnd(self, menu_data, profile):
|
||||
"""End an OTR session
|
||||
|
||||
@param menu_data: %(menu_data)s
|
||||
|
|
@ -298,17 +315,22 @@ class OTR(object):
|
|||
client = self.host.getClient(profile)
|
||||
try:
|
||||
to_jid = jid.JID(menu_data['jid'])
|
||||
if not to_jid.resource:
|
||||
to_jid.resource = self.host.memory.getMainResource(client, to_jid) # FIXME: temporary and unsecure, must be changed when frontends are refactored
|
||||
except KeyError:
|
||||
log.error(_("jid key is not present !"))
|
||||
log.error(_(u"jid key is not present !"))
|
||||
return defer.fail(exceptions.DataError)
|
||||
otrctx = self.context_managers[profile].getContextForUser(to_jid)
|
||||
self.endSession(client, to_jid)
|
||||
return {}
|
||||
|
||||
def endSession(self, client, to_jid):
|
||||
"""End an OTR session"""
|
||||
if not to_jid.resource:
|
||||
to_jid.resource = self.host.memory.getMainResource(client, to_jid) # FIXME: temporary and unsecure, must be changed when frontends are refactored
|
||||
otrctx = self.context_managers[client.profile].getContextForUser(to_jid)
|
||||
otrctx.disconnect()
|
||||
return {}
|
||||
|
||||
def _authenticate(self, menu_data, profile):
|
||||
"""Authenticate other user and see our own fingerprint
|
||||
def _otrAuthenticate(self, menu_data, profile):
|
||||
"""End an OTR session
|
||||
|
||||
@param menu_data: %(menu_data)s
|
||||
@param profile: %(doc_profile)s
|
||||
|
|
@ -316,12 +338,16 @@ class OTR(object):
|
|||
client = self.host.getClient(profile)
|
||||
try:
|
||||
to_jid = jid.JID(menu_data['jid'])
|
||||
if not to_jid.resource:
|
||||
to_jid.resource = self.host.memory.getMainResource(client, to_jid) # FIXME: temporary and unsecure, must be changed when frontends are refactored
|
||||
except KeyError:
|
||||
log.error(_("jid key is not present !"))
|
||||
log.error(_(u"jid key is not present !"))
|
||||
return defer.fail(exceptions.DataError)
|
||||
ctxMng = self.context_managers[profile]
|
||||
return self.authenticate(client, to_jid)
|
||||
|
||||
def authenticate(self, client, to_jid):
|
||||
"""Authenticate other user and see our own fingerprint"""
|
||||
if not to_jid.resource:
|
||||
to_jid.resource = self.host.memory.getMainResource(client, to_jid) # FIXME: temporary and unsecure, must be changed when frontends are refactored
|
||||
ctxMng = self.context_managers[client.profile]
|
||||
otrctx = ctxMng.getContextForUser(to_jid)
|
||||
priv_key = ctxMng.account.privkey
|
||||
|
||||
|
|
@ -329,10 +355,10 @@ class OTR(object):
|
|||
# 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_MESS: _(u"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"),
|
||||
title = _(u"No private key"),
|
||||
)
|
||||
return {'xmlui': dialog.toXml()}
|
||||
|
||||
|
|
@ -342,10 +368,10 @@ class OTR(object):
|
|||
# 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_MESS: _(u"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"),
|
||||
title = _(u"Fingerprint"),
|
||||
)
|
||||
return {'xmlui': dialog.toXml()}
|
||||
|
||||
|
|
@ -354,10 +380,10 @@ class OTR(object):
|
|||
data = xml_tools.XMLUIResult2DataFormResult(raw_data)
|
||||
if data['match'] == 'yes':
|
||||
otrctx.setCurrentTrust('verified')
|
||||
note_msg = _("Your correspondent {correspondent} is now TRUSTED")
|
||||
note_msg = _(u"Your correspondent {correspondent} is now TRUSTED")
|
||||
else:
|
||||
otrctx.setCurrentTrust('')
|
||||
note_msg = _("Your correspondent {correspondent} is now UNTRUSTED")
|
||||
note_msg = _(u"Your correspondent {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.peer)}
|
||||
|
|
@ -370,11 +396,11 @@ class OTR(object):
|
|||
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.addText(D_(u"Your own fingerprint is:\n{fingerprint}").format(fingerprint=priv_key))
|
||||
xmlui.addText(D_(u"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.addLabel(D_(u'Is your correspondent fingerprint the same as here ?'))
|
||||
xmlui.addList("match", [('yes', _('yes')),('no', _('no'))], ['yes' if trusted else 'no'])
|
||||
return {'xmlui': xmlui.toXml()}
|
||||
|
||||
|
|
@ -390,12 +416,12 @@ class OTR(object):
|
|||
if not to_jid.resource:
|
||||
to_jid.resource = self.host.memory.getMainResource(client, to_jid) # FIXME: temporary and unsecure, must be changed when frontends are refactored
|
||||
except KeyError:
|
||||
log.error(_("jid key is not present !"))
|
||||
log.error(_(u"jid key is not present !"))
|
||||
return defer.fail(exceptions.DataError)
|
||||
|
||||
ctxMng = self.context_managers[profile]
|
||||
if ctxMng.account.privkey is None:
|
||||
return {'xmlui': xml_tools.note(_("You don't have a private key yet !")).toXml()}
|
||||
return {'xmlui': xml_tools.note(_(u"You don't have a private key yet !")).toXml()}
|
||||
|
||||
def dropKey(data, profile):
|
||||
if C.bool(data['answer']):
|
||||
|
|
@ -404,7 +430,7 @@ class OTR(object):
|
|||
context.disconnect()
|
||||
ctxMng.account.privkey = None
|
||||
ctxMng.account.getPrivkey() # as account.privkey is None, getPrivkey will generate a new key, and save it
|
||||
return {'xmlui': xml_tools.note(_("Your private key has been dropped")).toXml()}
|
||||
return {'xmlui': xml_tools.note(_(u"Your private key has been dropped")).toXml()}
|
||||
return {}
|
||||
|
||||
submit_id = self.host.registerCallback(dropKey, with_data=True, one_shot=True)
|
||||
|
|
@ -425,10 +451,15 @@ class OTR(object):
|
|||
if otrctx.state == potr.context.STATE_ENCRYPTED:
|
||||
log.warning(u"Received unencrypted message in an encrypted context (from %(jid)s)" % {'jid': from_jid.full()})
|
||||
client = self.host.getClient(profile)
|
||||
self.host.bridge.messageNew(from_jid.full(),
|
||||
_(u"WARNING: received unencrypted data in a supposedly encrypted context"),
|
||||
mess_type=C.MESS_TYPE_INFO,
|
||||
|
||||
feedback=D_(u"WARNING: received unencrypted data in a supposedly encrypted context"),
|
||||
self.host.bridge.messageNew(uid=unicode(uuid.uuid4()),
|
||||
timestamp=time.time(),
|
||||
from_jid=from_jid.full(),
|
||||
to_jid=client.jid.full(),
|
||||
message={u'': feedback},
|
||||
subject={},
|
||||
mess_type=C.MESS_TYPE_INFO,
|
||||
extra={},
|
||||
profile=client.profile)
|
||||
encrypted = False
|
||||
|
|
@ -487,11 +518,14 @@ class OTR(object):
|
|||
otrctx.sendMessage(0, msg.encode('utf-8'))
|
||||
self.host.messageSendToBridge(mess_data, client)
|
||||
else:
|
||||
feedback = D_("Your message was not sent because your correspondent closed the encrypted conversation on his/her side. Either close your own side, or refresh the session.")
|
||||
self.host.bridge.messageNew(to_jid.full(),
|
||||
feedback,
|
||||
mess_type=C.MESS_TYPE_INFO,
|
||||
feedback = D_(u"Your message was not sent because your correspondent closed the encrypted conversation on his/her side. Either close your own side, or refresh the session.")
|
||||
self.host.bridge.messageNew(uid=unicode(uuid.uuid4()),
|
||||
timestamp=time.time(),
|
||||
from_jid=to_jid.full(),
|
||||
to_jid=client.jid.full(),
|
||||
message={u'': feedback},
|
||||
subject={},
|
||||
mess_type=C.MESS_TYPE_INFO,
|
||||
extra={},
|
||||
profile=client.profile)
|
||||
return False
|
||||
|
|
|
|||
Loading…
Reference in a new issue