comparison sat/plugins/plugin_xep_0384.py @ 3972:5fbdf986670c

plugin pte: Pubsub Target Encryption implementation: This plugin lets encrypt a few items for a specific set of entities. rel 382
author Goffi <goffi@goffi.org>
date Mon, 31 Oct 2022 13:46:51 +0100
parents 8e7d5796fb23
children db45d49518f6
comparison
equal deleted inserted replaced
3971:9b1d74a6b48c 3972:5fbdf986670c
20 from datetime import datetime 20 from datetime import datetime
21 import enum 21 import enum
22 import logging 22 import logging
23 import time 23 import time
24 from typing import \ 24 from typing import \
25 Any, Dict, FrozenSet, List, Literal, NamedTuple, Optional, Set, Type, cast 25 Any, Dict, FrozenSet, List, Literal, NamedTuple, Optional, Set, Type, Union, cast
26 import uuid 26 import uuid
27 import xml.etree.ElementTree as ET 27 import xml.etree.ElementTree as ET
28 from xml.sax.saxutils import quoteattr 28 from xml.sax.saxutils import quoteattr
29 29
30 from typing_extensions import Final, Never, assert_never 30 from typing_extensions import Final, Never, assert_never
1579 # These triggers are used by oldmemo, which doesn't do SCE and only applies to 1579 # These triggers are used by oldmemo, which doesn't do SCE and only applies to
1580 # messages. Temporarily, until a more fitting trigger for SCE-based encryption is 1580 # messages. Temporarily, until a more fitting trigger for SCE-based encryption is
1581 # added, the messageReceived trigger is also used for twomemo. 1581 # added, the messageReceived trigger is also used for twomemo.
1582 sat.trigger.add( 1582 sat.trigger.add(
1583 "messageReceived", 1583 "messageReceived",
1584 self.__message_received_trigger, 1584 self._message_received_trigger,
1585 priority=100050 1585 priority=100050
1586 ) 1586 )
1587 sat.trigger.add( 1587 sat.trigger.add(
1588 "sendMessageData", 1588 "sendMessageData",
1589 self.__send_message_data_trigger, 1589 self.__send_message_data_trigger,
2096 client, 2096 client,
2097 session_manager, 2097 session_manager,
2098 frozenset(applied_trust_updates) 2098 frozenset(applied_trust_updates)
2099 ) 2099 )
2100 2100
2101 async def __message_received_trigger( 2101 async def _message_received_trigger(
2102 self, 2102 self,
2103 client: SatXMPPClient, 2103 client: SatXMPPClient,
2104 message_elt: domish.Element, 2104 message_elt: domish.Element,
2105 post_treat: defer.Deferred 2105 post_treat: defer.Deferred
2106 ) -> bool: 2106 ) -> bool:
2111 message has fully progressed through the message receiving flow. Can be used 2111 message has fully progressed through the message receiving flow. Can be used
2112 to apply treatments to the fully processed message, like marking it as 2112 to apply treatments to the fully processed message, like marking it as
2113 encrypted. 2113 encrypted.
2114 @return: Whether to continue the message received flow. 2114 @return: Whether to continue the message received flow.
2115 """ 2115 """
2116
2117 muc_plaintext_cache_key: Optional[MUCPlaintextCacheKey] = None 2116 muc_plaintext_cache_key: Optional[MUCPlaintextCacheKey] = None
2118 2117
2119 sender_jid = jid.JID(message_elt["from"]) 2118 sender_jid = jid.JID(message_elt["from"])
2120 feedback_jid: jid.JID 2119 feedback_jid: jid.JID
2121 2120
2122 message_type = message_elt.getAttribute("type", "unknown") 2121 message_type = message_elt.getAttribute("type", C.MESS_TYPE_NORMAL)
2123 is_muc_message = message_type == C.MESS_TYPE_GROUPCHAT 2122 is_muc_message = message_type == C.MESS_TYPE_GROUPCHAT
2124 if is_muc_message: 2123 if is_muc_message:
2125 if self.__xep_0045 is None: 2124 if self.__xep_0045 is None:
2126 log.warning( 2125 log.warning(
2127 "Ignoring MUC message since plugin XEP-0045 is not available." 2126 "Ignoring MUC message since plugin XEP-0045 is not available."
2418 if encryption["plugin"].namespace != twomemo.twomemo.NAMESPACE: 2417 if encryption["plugin"].namespace != twomemo.twomemo.NAMESPACE:
2419 # Encryption is requested for this recipient, but not with twomemo 2418 # Encryption is requested for this recipient, but not with twomemo
2420 return True 2419 return True
2421 2420
2422 # All pre-checks done, we can start encrypting! 2421 # All pre-checks done, we can start encrypting!
2423 await self.__encrypt( 2422 await self.encrypt(
2424 client, 2423 client,
2425 twomemo.twomemo.NAMESPACE, 2424 twomemo.twomemo.NAMESPACE,
2426 stanza, 2425 stanza,
2427 recipient_bare_jid, 2426 recipient_bare_jid,
2428 stanza.getAttribute("type", "unkown") == C.MESS_TYPE_GROUPCHAT, 2427 stanza.getAttribute("type", C.MESS_TYPE_NORMAL) == C.MESS_TYPE_GROUPCHAT,
2429 stanza.getAttribute("id", None) 2428 stanza.getAttribute("id", None)
2430 ) 2429 )
2431 2430
2432 # Add a store hint if this is a message stanza 2431 # Add a store hint if this is a message stanza
2433 if stanza.name == "message": 2432 if stanza.name == "message":
2460 stanza = mess_data["xml"] 2459 stanza = mess_data["xml"]
2461 recipient_jid = mess_data["to"] 2460 recipient_jid = mess_data["to"]
2462 is_muc_message = mess_data["type"] == C.MESS_TYPE_GROUPCHAT 2461 is_muc_message = mess_data["type"] == C.MESS_TYPE_GROUPCHAT
2463 stanza_id = mess_data["uid"] 2462 stanza_id = mess_data["uid"]
2464 2463
2465 await self.__encrypt( 2464 await self.encrypt(
2466 client, 2465 client,
2467 oldmemo.oldmemo.NAMESPACE, 2466 oldmemo.oldmemo.NAMESPACE,
2468 stanza, 2467 stanza,
2469 recipient_jid, 2468 recipient_jid,
2470 is_muc_message, 2469 is_muc_message,
2472 ) 2471 )
2473 2472
2474 # Add a store hint 2473 # Add a store hint
2475 self.__xep_0334.addHintElements(stanza, [ "store" ]) 2474 self.__xep_0334.addHintElements(stanza, [ "store" ])
2476 2475
2477 async def __encrypt( 2476 async def encrypt(
2478 self, 2477 self,
2479 client: SatXMPPClient, 2478 client: SatXMPPClient,
2480 namespace: Literal["urn:xmpp:omemo:2", "eu.siacs.conversations.axolotl"], 2479 namespace: Literal["urn:xmpp:omemo:2", "eu.siacs.conversations.axolotl"],
2481 stanza: domish.Element, 2480 stanza: domish.Element,
2482 recipient_jid: jid.JID, 2481 recipient_jids: Union[jid.JID, Set[jid.JID]],
2483 is_muc_message: bool, 2482 is_muc_message: bool,
2484 stanza_id: Optional[str] 2483 stanza_id: Optional[str]
2485 ) -> None: 2484 ) -> None:
2486 """ 2485 """
2487 @param client: The client. 2486 @param client: The client.
2488 @param namespace: The namespace of the OMEMO version to use. 2487 @param namespace: The namespace of the OMEMO version to use.
2489 @param stanza: The stanza. Twomemo will encrypt the whole stanza using SCE, 2488 @param stanza: The stanza. Twomemo will encrypt the whole stanza using SCE,
2490 oldmemo will encrypt only the body. The stanza is modified by this call. 2489 oldmemo will encrypt only the body. The stanza is modified by this call.
2491 @param recipient_jid: The JID of the recipient. Can be a bare (aka "userhost") JID 2490 @param recipient_jid: The JID of the recipients.
2492 but doesn't have to. 2491 Can be a bare (aka "userhost") JIDs but doesn't have to.
2492 A single JID can be used.
2493 @param is_muc_message: Whether the stanza is a message stanza to a MUC room. 2493 @param is_muc_message: Whether the stanza is a message stanza to a MUC room.
2494 @param stanza_id: The id of this stanza. Especially relevant for message stanzas 2494 @param stanza_id: The id of this stanza. Especially relevant for message stanzas
2495 to MUC rooms such that the outgoing plaintext can be cached for MUC message 2495 to MUC rooms such that the outgoing plaintext can be cached for MUC message
2496 reflection handling. 2496 reflection handling.
2497 2497
2498 @warning: The calling code MUST take care of adding the store message processing 2498 @warning: The calling code MUST take care of adding the store message processing
2499 hint to the stanza if applicable! This can be done before or after this call, 2499 hint to the stanza if applicable! This can be done before or after this call,
2500 the order doesn't matter. 2500 the order doesn't matter.
2501 """ 2501 """
2502 if isinstance(recipient_jids, jid.JID):
2503 recipient_jids = {recipient_jids}
2504 if not recipient_jids:
2505 raise exceptions.InternalError("At least one JID must be specified")
2506 recipient_jid = next(iter(recipient_jids))
2502 2507
2503 muc_plaintext_cache_key: Optional[MUCPlaintextCacheKey] = None 2508 muc_plaintext_cache_key: Optional[MUCPlaintextCacheKey] = None
2504 2509
2505 recipient_bare_jids: Set[str] 2510 recipient_bare_jids: Set[str]
2506 feedback_jid: jid.JID 2511 feedback_jid: jid.JID
2507 2512
2508 if is_muc_message: 2513 if is_muc_message:
2514 if len(recipient_jids) != 1:
2515 raise exceptions.InternalError(
2516 'Only one JID can be set when "is_muc_message" is set'
2517 )
2509 if self.__xep_0045 is None: 2518 if self.__xep_0045 is None:
2510 raise exceptions.InternalError( 2519 raise exceptions.InternalError(
2511 "Encryption of MUC message requested, but plugin XEP-0045 is not" 2520 "Encryption of MUC message requested, but plugin XEP-0045 is not"
2512 " available." 2521 " available."
2513 ) 2522 )
2529 client=client, 2538 client=client,
2530 room_jid=room_jid, 2539 room_jid=room_jid,
2531 message_uid=stanza_id 2540 message_uid=stanza_id
2532 ) 2541 )
2533 else: 2542 else:
2534 recipient_bare_jids = { recipient_jid.userhost() } 2543 recipient_bare_jids = {r.userhost() for r in recipient_jids}
2535 feedback_jid = recipient_jid.userhostJID() 2544 feedback_jid = recipient_jid.userhostJID()
2536 2545
2537 log.debug( 2546 log.debug(
2538 f"Intercepting message that is to be encrypted by {namespace} for" 2547 f"Intercepting message that is to be encrypted by {namespace} for"
2539 f" {recipient_bare_jids}" 2548 f" {recipient_bare_jids}"