import Packets.Packet as p import Packets.HeaderPacket as h import lzma import msgpack import random import math import logging # DO NOT CHANGE DATA SIZE UNLESS YOU KNOW WHAT YOURE DOING logger = logging.getLogger("__main__." + __name__) def dict2bytes(cdict: dict): return lzma.compress(msgpack.dumps(cdict)) class Message: """ Full message which is composed of `Packets.Packet.Packet`s `🔗 Source `__ Attributes ---------- packets: list[Packets.Packet.Packet] List of packets making up the Message """ def __init__( self, bytesObject: bytes, sender: int, senderDisplayName: int, sourceNode, recipient: int, recipientNode: int, cryptographyInfo, packetsClass, pAction, dataSize: int = 128, wantFullResponse: bool = False, target=True, subMessage=False, primaryMessage=None, pskEncrypt=False ): """ Parameters ---------- bytesObject: bytes Bytes to split into packets sender: int 6 digit (maximum) node or peer ID senderDisplayName: int 3 digit (maximum) ID for mapping display names to a given user sourceNode: int Source of request recipient: int 6 digit (maximum) node or peer ID recipientNode: int 6 digit (maximum) node ID to route the packet to cryptographyInfo: Cryptography.WhaleSong.Transport Cryptography instance for encrypting message packetsClass: int Which protocol the packets are using pAction: int 3 digit (maximum) pAction ID for mapping precise actions within a protocol dataSize: int Size to cut the bytesObject into per packet wantFullResponse: bool Whether to send a response when the message has completed reception (TODO: Kill all retries for associated packets when received) target Whether the message is being sent to a target, if so, where subMessage: bool Whether this is a submessage primaryMessage Primary message this is a submessage to, if this is a submessage pskEncrypt: bool Whether to encrypt the message with the pre shared key """ self.recipientNode = recipientNode self.target = target self.subMessage = subMessage if subMessage: self.primaryMessage = primaryMessage if isinstance(bytesObject, list): packets = [h.Header(bytesObject[0])] for packet in bytesObject: packets.append( p.Packet( packet["data"], packetsID=packet["packetsID"], packetNumber=packet["packetNumber"], packetsClass=packetsClass, ) ) self.packets = packets else: # TODO: Data passed in by peers should already have been e2ee encrypted by SubtleCrypto if subMessage == False: bytesObject, nonce, tag = cryptographyInfo.encrypt( bytesObject, str(recipientNode).zfill(6), isDict=False, pskEncrypt=pskEncrypt ) # logger.debug(bytesObject) self.nonce = nonce self.tag = tag packets = [] self.packetsID = random.randrange(0, 999999) pnum = 1 dataSize = 80 blen = math.ceil(len(bytesObject) / dataSize) tb = b"" for it in range(blen): if it >= (blen - 1): b = bytesObject[it * dataSize:] else: b = bytesObject[it * dataSize: (it * dataSize + dataSize)] if subMessage: packets.append( p.Packet( b, self.packetsID, pnum, packetsClass=packetsClass, primaryMessage=primaryMessage, ) ) else: packets.append( p.Packet(b, self.packetsID, pnum, packetsClass=packetsClass) ) pnum += 1 tb += b if subMessage: pass else: packets.insert( 0, h.Header( self.packetsID, pnum, sender, senderDisplayName, sourceNode, recipient, recipientNode, wantFullResponse=wantFullResponse, packetsClass=packetsClass, pAction=pAction, target=target, ), ) self.fullPackets = [p for p in packets] if subMessage: pnum -= 1 for it in range(pnum): packet = msgpack.loads(packets[it].dump()) packet["packetCount"] = pnum packets[it] = msgpack.dumps(packet) self.packets = packets def get(self) -> list[p.Packet]: """ Get and return all packets """ return self.packets def reassemble(self, completedMessage: dict, cryptographyInfo, subMessage=False, yctx=None, packetCount=None): """ Reassemble packets from a completed message in `Sponge.base`, meant to be used without instantiation Arguments --------- completedMessage: dict All parts of the message and submessage cryptographyInfo: Cryptography.WhaleSong.Transport Cryptography instance for encrypting message subMessage: bool Whether this is a submessage yctx Message parsing context packetCount Number of packets """ data = b"" sourceNode = None if subMessage: sourceNode = yctx["sourceNode"]["val"] for it in range(1, packetCount+1): data += completedMessage["data"][completedMessage["dataOrder"].index(it)] data = msgpack.loads(lzma.decompress(data)) # logger.debug(data) # logger.debug(completedMessage["data"]) # logger.debug(completedMessage["dataOrder"]) else: packetCount = int(completedMessage.yctx["packetCount"]["val"]) sourceNode = completedMessage.yctx["sourceNode"]["val"] # logger.debug(completedMessage.data) for it in range(1, packetCount): if it in completedMessage.dataOrder: data += completedMessage.data[completedMessage.dataOrder.index(it)] # logger.debug("pre decrypt") # logger.debug(data) data = cryptographyInfo.decrypt( data, sourceNode, completedMessage.nonce, completedMessage.tag ) return data