summaryrefslogtreecommitdiff
path: root/plugins/otr.py
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/otr.py')
-rw-r--r--plugins/otr.py133
1 files changed, 106 insertions, 27 deletions
diff --git a/plugins/otr.py b/plugins/otr.py
index 90b4a855..25e7f896 100644
--- a/plugins/otr.py
+++ b/plugins/otr.py
@@ -4,20 +4,24 @@ This plugin implements `Off The Record messaging`_.
This is a plugin used to encrypt one-to-one conversation using the OTR
encryption method. You can use it if you want good privacy, deniability,
-authentication, and strong secrecy. Without this
-encryption, your messages are encrypted **at least** from your client (poezio) to
-your server. The message is decrypted by your server and you cannot control the
-encryption method of your messages from your server to your contact’s server
-(unless you are your own server’s administrator), nor from your contact’s
-server to your contact’s client.
+authentication, and strong secrecy. Without this encryption, your messages
+are encrypted **at least** from your client (poezio) to your server. The
+message is decrypted by your server and you cannot control the encryption
+method of your messages from your server to your contact’s server (unless
+you are your own server’s administrator), nor from your contact’s server
+to your contact’s client.
This plugin does end-to-end encryption. This means that **only** your contact can
decrypt your messages, and it is fully encrypted during **all** its travel
through the internet.
Note that if you are having an encrypted conversation with a contact, you can
-**not** send XHTML-IM messages to him. They will be removed and be replaced by
-plain text messages.
+**not** send XHTML-IM messages to him (or correct messages, or anything more than
+raw text). They will be removed and be replaced by plain text messages.
+
+This is a limitation of the OTR protocol, and it will never be fixed. Some clients
+like Pidgin-OTR try do do magic stuff with html unescaping inside the OTR body, and
+it is not pretty.
Installation
------------
@@ -48,17 +52,22 @@ Command added to Conversation Tabs and Private Tabs:
.. glossary::
/otr
- **Usage:** ``/otr [start|refresh|end|fpr|ourfpr]``
+ **Usage:** ``/otr [start|refresh|end|fpr|ourfpr|trust|untrust]``
- This command is used to start (or refresh), or end an OTR private session.
+ This command is used to manage an OTR private session.
- The ``fpr`` command gives you the fingerprint of the key of the remove entity, and
- the ``ourfpr`` command gives you the fingerprint of your own key.
+ - The ``start`` (or ``refresh``) command starts or refreshs a private OTR session
+ - The ``end`` command ends a private OTR session
+ - The ``fpr`` command gives you the fingerprint of the key of the remove entity
+ - The ``ourfpr`` command gives you the fingerprint of your own key
+ - The ``trust`` command marks the current remote key as trusted for the current remote JID
+ - The ``untrust`` command removes that trust
+ - Finally, the ``drop`` command is used if you want to delete your private key (not recoverable)
To use OTR, make sure the plugin is loaded (if not, then do ``/load otr``).
-Once you are in a private conversation, you have to do a:
+A simple workflow looks like this:
.. code-block:: none
@@ -67,18 +76,70 @@ Once you are in a private conversation, you have to do a:
The status of the OTR encryption should appear in the bar between the chat and
the input as ``OTR: encrypted``.
+Then you use ``fpr``/``ourfpr`` to check the fingerprints, and confirm your respective
+identities out-of-band.
+
+You can then use
+
+.. code-block:: none
+
+ /otr trust
+
+To set the key as trusted, which will be shown when you start or refresh a conversation
+(the trust status will be in a bold font and if the key is untrusted, the remote fingerprint
+will be shown).
+
Once you’re done, end the OTR session with
.. code-block:: none
/otr end
+Files
+-----
+
+This plugin creates trust files complatible with libotr and the files produced by gajim.
+
+
+The files are located in :file:`$XDG_DATA_HOME/poezio/otr/` by default (so
+:file:`~/.local/share/poezio/otr` in most cases).
+
+Two files are created:
+
+- An account_jid.key3 (:file:`example@example.com.key3`) file, which contains the private key
+- An account_jid.fpr (:file:`example@example.com.fpr`) file, which contains the list of trusted
+ (or untrusted) JIDs and keys.
+
+Configuration
+-------------
+
+.. glossary::
+ :sorted:
+
+ keys_dir
+ **Default:** ``$XDG_DATA_HOME/poezio/otr``
+
+ The directory in which you want keys and fpr to be stored.
+
+ allow_v2
+ **Default:** ``true``
+
+ Allow OTRv2
+
+ allow_v1
+ **Default:** ``false``
+
+ Allow OTRv1
+
+The :term:`allow_v1`` and :term:`allow_v2` configuration parameters are tab-specific.
Important details
-----------------
-The OTR session is considered for a full jid.
-
+The OTR session is considered for a full jid, but the trust is considered
+with a bare JID. This is important to know in the case of Private Chats, since
+you cannot always get the real the JID of your contact (or check if the same
+nick is used by different people).
.. _Off The Record messaging: http://wiki.xmpp.org/web/OTR
@@ -91,7 +152,8 @@ log = logging.getLogger(__name__)
import os
import curses
-from potr.context import NotEncryptedError, UnencryptedMessage, ErrorReceived, NotOTRMessage, STATE_ENCRYPTED, STATE_PLAINTEXT, STATE_FINISHED, Context, Account
+from potr.context import NotEncryptedError, UnencryptedMessage, ErrorReceived, NotOTRMessage,\
+ STATE_ENCRYPTED, STATE_PLAINTEXT, STATE_FINISHED, Context, Account, crypt
from plugin import BasePlugin
from tabs import ConversationTab, DynamicConversationTab, PrivateTab
@@ -244,8 +306,6 @@ class Plugin(BasePlugin):
def init(self):
# set the default values from the config
- policy = self.config.get('encryption_policy', 'ondemand').lower()
- POLICY_FLAGS['REQUIRE_ENCRYPTION'] = (policy == 'always')
allow_v2 = self.config.get('allow_v2', 'true').lower()
POLICY_FLAGS['ALLOW_V2'] = (allow_v2 != 'false')
allow_v1 = self.config.get('allow_v1', 'false').lower()
@@ -305,7 +365,6 @@ class Plugin(BasePlugin):
if not jid in self.contexts:
flags = POLICY_FLAGS.copy()
policy = self.config.get_by_tabname('encryption_policy', 'ondemand', jid).lower()
- flags['REQUIRE_ENCRYPTION'] = (policy == 'always')
allow_v2 = self.config.get_by_tabname('allow_v2', 'true', jid).lower()
flags['ALLOW_V2'] = (allow_v2 != 'false')
allow_v1 = self.config.get_by_tabname('allow_v1', 'false', jid).lower()
@@ -324,13 +383,15 @@ class Plugin(BasePlugin):
jid=msg['from'], nick_color=theming.get_theme().COLOR_REMOTE_USER,
typ=0)
del msg['body']
+ del msg['html']
hl(tab)
self.core.refresh_window()
return
except ErrorReceived as err:
# Received an OTR error
- tab.add_message('Received the following error from %s: %s' % (msg['from'], err.args[0]))
+ tab.add_message('Received the following error from %s: %s' % (msg['from'], err.args[0]), typ=0)
del msg['body']
+ del msg['html']
hl(tab)
self.core.refresh_window()
return
@@ -338,14 +399,26 @@ class Plugin(BasePlugin):
# ignore non-otr messages
# if we expected an OTR message, we would have
# got an UnencryptedMesssage
+ log.error('coucou %s', ctx.getPolicy('REQUIRE_ENCRYPTION'))
return
except NotEncryptedError as err:
- tab.add_message('An encrypted message from %s was received but is unreadable, as you are not'
- ' currently communicating privately.' % msg['from'],
- jid=msg['from'], nick_color=theming.get_theme().COLOR_REMOTE_USER,
+ tab.add_message('An encrypted message from %s was received but is '
+ 'unreadable, as you are not currently communicating'
+ ' privately.' % msg['from'], jid=msg['from'],
+ nick_color=theming.get_theme().COLOR_REMOTE_USER,
typ=0)
hl(tab)
del msg['body']
+ del msg['html']
+ self.core.refresh_window()
+ return
+ except crypt.InvalidParameterError:
+ tab.add_message('The message from %s could not be decrypted' %
+ msg['from'], jid=msg['from'], typ=0,
+ nick_color=theming.get_theme().COLOR_REMOTE_USER)
+ hl(tab)
+ del msg['body']
+ del msg['html']
self.core.refresh_window()
return
except:
@@ -424,6 +497,7 @@ class Plugin(BasePlugin):
if context.state not in (STATE_FINISHED, STATE_PLAINTEXT):
context.disconnect()
self.account.drop_privkey()
+ tab.add_message('Private key dropped.', typ=0)
elif arg == 'trust':
ctx = self.get_context(name)
key = ctx.getCurrentKey()
@@ -431,8 +505,10 @@ class Plugin(BasePlugin):
fpr = key.cfingerprint()
else:
return
- ctx.setTrust(fpr, 'verified')
- self.account.saveTrusts()
+ if not ctx.getCurrentTrust():
+ ctx.setTrust(fpr, 'verified')
+ self.account.saveTrusts()
+ tab.add_message('You added \x19b%s\x19o with key %s to your trusted list.' % (ctx.trustName, key), typ=0)
elif arg == 'untrust':
ctx = self.get_context(name)
key = ctx.getCurrentKey()
@@ -440,8 +516,11 @@ class Plugin(BasePlugin):
fpr = key.cfingerprint()
else:
return
- ctx.setTrust(fpr, '')
- self.account.saveTrusts()
+ if ctx.getCurrentTrust():
+ ctx.setTrust(fpr, '')
+ self.account.saveTrusts()
+ tab.add_message('You removed \x19b%s\x19o with key %s from your trusted list.' % (ctx.trustName, key), typ=0)
+ self.core.refresh_window()
def completion_otr(self, the_input):
return the_input.new_completion(['start', 'fpr', 'ourfpr', 'refresh', 'end', 'trust', 'untrust'], 1, quotify=False)