comparison sat/memory/encryption.py @ 2646:712cb4ff3e13

core: new EncryptionHandler class which manage encrypted session as a core feature: Plugin handling encryption can now register using host.registerEncryptionPlugin, and an encryption session can now be started using messageEncryptionStart bridge method. This will make encryption handling more easy, as we now know if a session is clear or e2e encrypted, and which plugin handle it.
author Goffi <goffi@goffi.org>
date Sun, 29 Jul 2018 19:22:56 +0200
parents
children ebcff5423465
comparison
equal deleted inserted replaced
2645:f2cf1daa42cb 2646:712cb4ff3e13
1 #!/usr/bin/env python2
2 # -*- coding: utf-8 -*-
3
4 # SAT: a jabber client
5 # Copyright (C) 2009-2018 Jérôme Poisson (goffi@goffi.org)
6
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU Affero General Public License for more details.
16
17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20 from sat.core.i18n import _
21 from sat.core.constants import Const as C
22 from sat.core import exceptions
23 from collections import namedtuple
24 from sat.core.log import getLogger
25 log = getLogger(__name__)
26
27
28 EncryptionPlugin = namedtuple("EncryptionPlugin", ("instance",
29 "name",
30 "namespace",
31 "priority"))
32
33
34 class EncryptionHandler(object):
35 """Class to handle encryption sessions for a client"""
36 plugins = [] # plugin able to encrypt messages
37
38 def __init__(self, host):
39 self._sessions = {} # bare_jid ==> encryption_data
40
41 @classmethod
42 def registerPlugin(cls, plg_instance, name, namespace, priority=0):
43 """Register a plugin handling an encryption algorithm
44
45 @param plg_instance(object): instance of the plugin
46 it must have the following methods:
47 - startEncryption(jid.JID): start an encryption session with a bare jid
48 - stopEncryption(jid.JID): stop an encryption session with a bare jid
49 @param name(unicode): human readable name of the encryption alrgorithm
50 @param namespace(unicode): namespace of the encryption algorithm
51 @param priority(int): priority of this plugin to encrypt an message when not
52 selected manually
53 """
54 existing_ns = [p.namespace for p in cls.plugins]
55 if namespace in existing_ns:
56 raise exceptions.ConflictError("A plugin with this namespace already exists!")
57 plg = EncryptionPlugin(
58 instance=plg_instance,
59 name=name,
60 namespace=namespace,
61 priority=priority)
62 cls.plugins.append(plg)
63 cls.plugins.sort(key=lambda p: p.priority)
64
65 def start(self, entity, namespace=None):
66 """Start an encrypted session with an entity
67
68 @param entity(jid.JID): entity to start an encrypted session with
69 must be bare jid is the algorithm encrypt for all devices
70 @param namespace(unicode, None): namespace of the encryption algorithm to use
71 None to select automatically an algorithm
72 """
73 if not self.plugins:
74 raise exceptions.NotFound(_(u"No encryption plugin is registered, "
75 u"an encryption session can't be started"))
76
77 bare_jid = entity.userhostJID()
78 if bare_jid in self._sessions:
79 plg = self._sessions[bare_jid]['plugin']
80
81 msg = (_(u"Session with {bare_jid} is already encrypted with {name}."
82 u"Please stop encryption session before changing algorithm.")
83 .format(bare_jid=bare_jid, name=plg.name))
84 log.warning(msg)
85 raise exceptions.ConflictError(msg)
86
87 if namespace is None:
88 plg = self.plugins[0]
89 else:
90 try:
91 plg = next(p for p in self.plugins if p.namespace == namespace)
92 except StopIteration:
93 raise exceptions.NotFound(_(
94 u"Can't find requested encryption plugin: {namespace}").format(
95 namespace=namespace))
96
97 data = {"plugin": plg}
98 if entity.resource:
99 # indicate that we encrypt only for some devices
100 data['directed_devices'] = [entity.resource]
101
102 self._sessions[entity.userhostJID()] = data
103 log.info(_(u"Encryption session as been set for {bare_jid} with "
104 u"{encryption_name}").format(
105 bare_jid=bare_jid.userhost(), encryption_name=plg.name))
106
107 ## Triggers ##
108
109 def setEncryptionFlag(self, mess_data):
110 """Set "encryption" key in mess_data if session with destinee is encrypted"""
111
112 if mess_data["type"] == "groupchat":
113 # FIXME: to change when group chat encryption will be handled
114 return
115
116 to_jid = mess_data['to']
117 encryption = self._sessions.get(to_jid.userhostJID())
118 if encryption is not None:
119 mess_data[C.MESS_KEY_ENCRYPTION] = encryption