comparison libervia/backend/plugins/plugin_xep_0391.py @ 4270:0d7bb4df2343

Reformatted code base using black.
author Goffi <goffi@goffi.org>
date Wed, 19 Jun 2024 18:44:57 +0200
parents e11b13418ba6
children
comparison
equal deleted inserted replaced
4269:64a85ce8be70 4270:0d7bb4df2343
75 log.info(_("XEP-0391 (Pubsub Attachments) plugin initialization")) 75 log.info(_("XEP-0391 (Pubsub Attachments) plugin initialization"))
76 host.register_namespace("jet", NS_JET) 76 host.register_namespace("jet", NS_JET)
77 self.host = host 77 self.host = host
78 self._o = host.plugins["XEP-0384"] 78 self._o = host.plugins["XEP-0384"]
79 self._j = host.plugins["XEP-0166"] 79 self._j = host.plugins["XEP-0166"]
80 host.trigger.add("XEP-0166_initiate_elt_built", self._on_initiate_elt_build)
81 host.trigger.add("XEP-0166_on_session_initiate", self._on_session_initiate)
82 host.trigger.add("XEP-0234_jingle_handler", self._add_encryption_filter)
80 host.trigger.add( 83 host.trigger.add(
81 "XEP-0166_initiate_elt_built", 84 "XEP-0234_file_receiving_request_conf", self._add_encryption_filter
82 self._on_initiate_elt_build
83 )
84 host.trigger.add(
85 "XEP-0166_on_session_initiate",
86 self._on_session_initiate
87 )
88 host.trigger.add(
89 "XEP-0234_jingle_handler",
90 self._add_encryption_filter
91 )
92 host.trigger.add(
93 "XEP-0234_file_receiving_request_conf",
94 self._add_encryption_filter
95 ) 85 )
96 86
97 def get_handler(self, client): 87 def get_handler(self, client):
98 return JET_Handler() 88 return JET_Handler()
99 89
100 async def _on_initiate_elt_build( 90 async def _on_initiate_elt_build(
101 self, 91 self,
102 client: SatXMPPEntity, 92 client: SatXMPPEntity,
103 session: Dict[str, Any], 93 session: Dict[str, Any],
104 iq_elt: domish.Element, 94 iq_elt: domish.Element,
105 jingle_elt: domish.Element 95 jingle_elt: domish.Element,
106 ) -> bool: 96 ) -> bool:
107 if client.encryption.get_namespace( 97 if (
108 session["peer_jid"].userhostJID() 98 client.encryption.get_namespace(session["peer_jid"].userhostJID())
109 ) != self._o.NS_OLDMEMO: 99 != self._o.NS_OLDMEMO
100 ):
110 return True 101 return True
111 for content_elt in jingle_elt.elements(self._j.namespace, "content"): 102 for content_elt in jingle_elt.elements(self._j.namespace, "content"):
112 content_data = session["contents"][content_elt["name"]] 103 content_data = session["contents"][content_elt["name"]]
113 transport_data = content_data["transport_data"] 104 transport_data = content_data["transport_data"]
114 if transport_data.get("webrtc"): 105 if transport_data.get("webrtc"):
124 enc_type = "eu.siacs.conversations.axolotl" 115 enc_type = "eu.siacs.conversations.axolotl"
125 security_elt["cipher"] = cipher 116 security_elt["cipher"] = cipher
126 security_elt["type"] = enc_type 117 security_elt["type"] = enc_type
127 encryption_data = content_data["encryption"] = { 118 encryption_data = content_data["encryption"] = {
128 "cipher": cipher, 119 "cipher": cipher,
129 "type": enc_type 120 "type": enc_type,
130 } 121 }
131 session_manager = await self._o.get_session_manager(client.profile) 122 session_manager = await self._o.get_session_manager(client.profile)
132 await self._o.download_missing_device_lists( 123 await self._o.download_missing_device_lists(
133 client, self._o.NS_OLDMEMO, {session["peer_jid"]}, session_manager 124 client, self._o.NS_OLDMEMO, {session["peer_jid"]}, session_manager
134 ) 125 )
135 try: 126 try:
136 messages, encryption_errors = await session_manager.encrypt( 127 messages, encryption_errors = await session_manager.encrypt(
137 frozenset({session["peer_jid"].userhost()}), 128 frozenset({session["peer_jid"].userhost()}),
138 # the value seems to be the commonly used value 129 # the value seems to be the commonly used value
139 { self._o.NS_OLDMEMO: b" " }, 130 {self._o.NS_OLDMEMO: b" "},
140 backend_priority_order=[ self._o.NS_OLDMEMO ], 131 backend_priority_order=[self._o.NS_OLDMEMO],
141 identifier = client.jid.userhost() 132 identifier=client.jid.userhost(),
142 ) 133 )
143 except Exception as e: 134 except Exception as e:
144 log.exception("Can't generate IV and keys") 135 log.exception("Can't generate IV and keys")
145 raise e 136 raise e
146 message, plain_key_material = next(iter(messages.items())) 137 message, plain_key_material = next(iter(messages.items()))
147 iv, key = message.content.initialization_vector, plain_key_material.key 138 iv, key = message.content.initialization_vector, plain_key_material.key
148 content_data["encryption"].update({ 139 content_data["encryption"].update({"iv": iv, "key": key})
149 "iv": iv,
150 "key": key
151 })
152 encrypted_elt = xml_tools.et_elt_2_domish_elt( 140 encrypted_elt = xml_tools.et_elt_2_domish_elt(
153 oldmemo.etree.serialize_message(message) 141 oldmemo.etree.serialize_message(message)
154 ) 142 )
155 security_elt.addChild(encrypted_elt) 143 security_elt.addChild(encrypted_elt)
156 return True 144 return True
158 async def _on_session_initiate( 146 async def _on_session_initiate(
159 self, 147 self,
160 client: SatXMPPEntity, 148 client: SatXMPPEntity,
161 session: Dict[str, Any], 149 session: Dict[str, Any],
162 iq_elt: domish.Element, 150 iq_elt: domish.Element,
163 jingle_elt: domish.Element 151 jingle_elt: domish.Element,
164 ) -> bool: 152 ) -> bool:
165 for content_elt in jingle_elt.elements(self._j.namespace, "content"): 153 for content_elt in jingle_elt.elements(self._j.namespace, "content"):
166 content_data = session["contents"][content_elt["name"]] 154 content_data = session["contents"][content_elt["name"]]
167 security_elt = next(content_elt.elements(NS_JET, "security"), None) 155 security_elt = next(content_elt.elements(NS_JET, "security"), None)
168 if security_elt is None: 156 if security_elt is None:
179 try: 167 try:
180 message = await oldmemo.etree.parse_message( 168 message = await oldmemo.etree.parse_message(
181 xml_tools.domish_elt_2_et_elt(encrypted_elt, False), 169 xml_tools.domish_elt_2_et_elt(encrypted_elt, False),
182 session["peer_jid"].userhost(), 170 session["peer_jid"].userhost(),
183 client.jid.userhost(), 171 client.jid.userhost(),
184 session_manager 172 session_manager,
185 ) 173 )
186 __, __, plain_key_material = await session_manager.decrypt(message) 174 __, __, plain_key_material = await session_manager.decrypt(message)
187 except Exception as e: 175 except Exception as e:
188 log.warning(f"Can't get IV and key: {e}\n{security_elt.toXml()}") 176 log.warning(f"Can't get IV and key: {e}\n{security_elt.toXml()}")
189 continue 177 continue
190 try: 178 try:
191 content_data["encryption"] = { 179 content_data["encryption"] = {
192 "cipher": security_elt["cipher"], 180 "cipher": security_elt["cipher"],
193 "type": security_elt["type"], 181 "type": security_elt["type"],
194 "iv": message.content.initialization_vector, 182 "iv": message.content.initialization_vector,
195 "key": plain_key_material.key 183 "key": plain_key_material.key,
196 } 184 }
197 except KeyError as e: 185 except KeyError as e:
198 log.warning(f"missing data, can't decrypt: {e}") 186 log.warning(f"missing data, can't decrypt: {e}")
199 continue 187 continue
200 188
201 return True 189 return True
202 190
203 def __encrypt( 191 def __encrypt(
204 self, 192 self, data: bytes, encryptor: CipherContext, data_cb: Callable
205 data: bytes,
206 encryptor: CipherContext,
207 data_cb: Callable
208 ) -> bytes: 193 ) -> bytes:
209 data_cb(data) 194 data_cb(data)
210 if data: 195 if data:
211 return encryptor.update(data) 196 return encryptor.update(data)
212 else: 197 else:
213 try: 198 try:
214 return encryptor.finalize() + encryptor.tag 199 return encryptor.finalize() + encryptor.tag
215 except AlreadyFinalized: 200 except AlreadyFinalized:
216 return b'' 201 return b""
217 202
218 def __decrypt( 203 def __decrypt(
219 self, 204 self,
220 data: bytes, 205 data: bytes,
221 buffer: list[bytes], 206 buffer: list[bytes],
222 decryptor: CipherContext, 207 decryptor: CipherContext,
223 data_cb: Callable 208 data_cb: Callable,
224 ) -> bytes: 209 ) -> bytes:
225 buffer.append(data) 210 buffer.append(data)
226 data = b''.join(buffer) 211 data = b"".join(buffer)
227 buffer.clear() 212 buffer.clear()
228 if len(data) > 16: 213 if len(data) > 16:
229 decrypted = decryptor.update(data[:-16]) 214 decrypted = decryptor.update(data[:-16])
230 data_cb(decrypted) 215 data_cb(decrypted)
231 else: 216 else:
232 decrypted = b'' 217 decrypted = b""
233 buffer.append(data[-16:]) 218 buffer.append(data[-16:])
234 return decrypted 219 return decrypted
235 220
236 def __decrypt_finalize( 221 def __decrypt_finalize(
237 self, 222 self,
238 file_obj: io.BytesIO, 223 file_obj: io.BytesIO,
239 buffer: list[bytes], 224 buffer: list[bytes],
240 decryptor: CipherContext, 225 decryptor: CipherContext,
241 ) -> None: 226 ) -> None:
242 tag = b''.join(buffer) 227 tag = b"".join(buffer)
243 file_obj.write(decryptor.finalize_with_tag(tag)) 228 file_obj.write(decryptor.finalize_with_tag(tag))
244 229
245 async def _add_encryption_filter( 230 async def _add_encryption_filter(
246 self, 231 self,
247 client: SatXMPPEntity, 232 client: SatXMPPEntity,
248 session: Dict[str, Any], 233 session: Dict[str, Any],
249 content_data: Dict[str, Any], 234 content_data: Dict[str, Any],
250 elt: domish.Element 235 elt: domish.Element,
251 ) -> bool: 236 ) -> bool:
252 try: 237 try:
253 file_obj = content_data["stream_object"].file_obj 238 file_obj = content_data["stream_object"].file_obj
254 except KeyError: 239 except KeyError:
255 transport_data = content_data["transport_data"] 240 transport_data = content_data["transport_data"]
256 if transport_data.get("webrtc"): 241 if transport_data.get("webrtc"):
257 # we skip JET to avoid double encryption 242 # we skip JET to avoid double encryption
258 log.debug("JET skipped due to webrtc transport.") 243 log.debug("JET skipped due to webrtc transport.")
259 return True 244 return True
260 try: 245 try:
261 encryption_data=content_data["encryption"] 246 encryption_data = content_data["encryption"]
262 except KeyError: 247 except KeyError:
263 return True 248 return True
264 cipher = ciphers.Cipher( 249 cipher = ciphers.Cipher(
265 ciphers.algorithms.AES(encryption_data["key"]), 250 ciphers.algorithms.AES(encryption_data["key"]),
266 modes.GCM(encryption_data["iv"]), 251 modes.GCM(encryption_data["iv"]),
272 decryptor = cipher.decryptor() 257 decryptor = cipher.decryptor()
273 file_obj.pre_close_cb = partial( 258 file_obj.pre_close_cb = partial(
274 self.__decrypt_finalize, 259 self.__decrypt_finalize,
275 file_obj=file_obj, 260 file_obj=file_obj,
276 buffer=buffer, 261 buffer=buffer,
277 decryptor=decryptor 262 decryptor=decryptor,
278 ) 263 )
279 file_obj.data_cb = partial( 264 file_obj.data_cb = partial(
280 self.__decrypt, 265 self.__decrypt,
281 buffer=buffer, 266 buffer=buffer,
282 decryptor=decryptor, 267 decryptor=decryptor,
283 data_cb=file_obj.data_cb 268 data_cb=file_obj.data_cb,
284 ) 269 )
285 else: 270 else:
286 # we are sending a file 271 # we are sending a file
287 file_obj.data_cb = partial( 272 file_obj.data_cb = partial(
288 self.__encrypt, 273 self.__encrypt, encryptor=cipher.encryptor(), data_cb=file_obj.data_cb
289 encryptor=cipher.encryptor(),
290 data_cb=file_obj.data_cb
291 ) 274 )
292 275
293 return True 276 return True
294 277
295 278