Skip to main content

Decentralized Instant Messaging Protocol

Project description

Decentralized Instant Messaging Protocol (Python)

license Version PRs Welcome Platform

Talk is cheap, show you the codes!

Dependencies

pip3 install dimp

Common Extensions

facebook.py

class Facebook(Barrack):
    """ Access database to load/save user's private key, meta and profiles """

    def save_private_key(self, private_key: PrivateKey, identifier: ID) -> bool:
        # TODO: save private key into safety storage
        pass

    def save_meta(self, meta: Meta, identifier: ID) -> bool:
        if not meta.match_identifier(identifier):
            return False
        # TODO: save meta to local/persistent storage
        pass

    def save_profile(self, profile: Profile) -> bool:
        if not self.verify_profile(profile):
            return False
        # TODO: save to local storage
        pass

    def verify_profile(self, profile: Profile) -> bool:
        if profile is None:
            return False
        elif profile.valid:
            # already verified
            return True
        identifier = profile.identifier
        meta = None
        if identifier.type.is_user():
            # verify with user's meta.key
            meta = self.meta(identifier=identifier)
        elif identifier.type.is_group():
            # verify with group owner's meta.key
            group = self.group(identifier=identifier)
            if group is not None:
                meta = self.meta(identifier=group.owner)
        if meta is not None:
            return profile.verify(public_key=meta.key)

    #
    #   Barrack
    #
    def create_user(self, identifier: ID) -> User:
        assert identifier.type.is_user(), 'user ID error: %s' % identifier
        if identifier.is_broadcast:
            # create user 'anyone@anywhere'
            return User(identifier=identifier)
        assert self.meta(identifier) is not None, 'failed to get meta for user: %s' % identifier
        # TODO: check user type
        u_type = identifier.type
        if u_type.is_person():
            return User(identifier=identifier)
        if u_type.is_robot():
            return Robot(identifier=identifier)
        if u_type.is_station():
            return Station(identifier=identifier)
        raise TypeError('unsupported user type: %s' % u_type)

    def create_group(self, identifier: ID) -> Group:
        assert identifier.type.is_group(), 'group ID error: %s' % identifier
        if identifier.is_broadcast:
            # create group 'everyone@everywhere'
            return Group(identifier=identifier)
        assert self.meta(identifier) is not None, 'failed to get meta for group: %s' % identifier
        # TODO: check group type
        g_type = identifier.type
        if g_type == NetworkID.Polylogue:
            return Polylogue(identifier=identifier)
        if g_type == NetworkID.Chatroom:
            raise NotImplementedError('Chatroom not implemented')
        if g_type.is_provider():
            return ServiceProvider(identifier=identifier)
        raise TypeError('unsupported group type: %s' % g_type)


#
#  global
#
facebook = Facebook()

keystore.py

class KeyStore(KeyCache):
    """ For reuse symmetric key """

    def save_keys(self, key_map: dict) -> bool:
        # TODO: save to local cache
        pass

    def load_keys(self) -> dict:
        # TODO: load from local cache
        pass


#
#  global
#
keystore = KeyStore()

messenger.py

class Messenger(Transceiver, ConnectionDelegate):
    """ Transform and send/receive message """

    def __init__(self):
        super().__init__()
        # self.barrack = facebook
        # self.key_cache = keystore
        self.delegate: MessengerDelegate = None

    def encrypt_content(self, content: Content, key: dict, msg: InstantMessage) -> bytes:
        password = SymmetricKey(key=key)
        assert password == key, 'irregular symmetric key: %s' % key
        # check attachment for File/Image/Audio/Video message content before
        if isinstance(content, FileContent):
            data = password.encrypt(data=content.data)
            # upload (encrypted) file data onto CDN and save the URL in message content
            url = self.delegate.upload_data(data=data, msg=msg)
            if url is not None:
                content.url = url
                content.data = None
        return super().encrypt_content(content=content, key=password, msg=msg)

    def decrypt_content(self, data: bytes, key: dict, msg: SecureMessage) -> Optional[Content]:
        password = SymmetricKey(key=key)
        content = super().decrypt_content(data=data, key=password, msg=msg)
        if content is None:
            return None
        # check attachment for File/Image/Audio/Video message content after
        if isinstance(content, FileContent):
            i_msg = InstantMessage.new(content=content, envelope=msg.envelope)
            # download from CDN
            file_data = self.delegate.download_data(content.url, i_msg)
            if file_data is None:
                # save symmetric key for decrypted file data after download from CDN
                content.password = password
            else:
                # decrypt file data
                content.data = password.decrypt(data=file_data)
                assert content.data is not None, 'failed to decrypt file data with key: %s' % key
                content.url = None
        return content

    #
    #   Send message
    #
    def send_message(self, msg: InstantMessage, callback: Callback=None, split: bool=True) -> bool:
        """
        Send instant message (encrypt and sign) onto DIM network

        :param msg:      instant message
        :param callback: callback function
        :param split:    if it's a group message, split it before sending out
        :return:         False on data/delegate error
        """
        # Send message (secured + certified) to target station
        s_msg = self.encrypt_message(msg=msg)
        r_msg = self.sign_message(msg=s_msg)
        receiver = self.facebook.identifier(msg.envelope.receiver)
        ok = True
        if split and receiver.type.is_group():
            # split for each members
            members = self.facebook.members(identifier=receiver)
            if members is None or len(members) == 0:
                # FIXME: query group members from sender
                messages = None
            else:
                messages = r_msg.split(members=members)
            if messages is None:
                # failed to split msg, send it to group
                ok = self.__send_message(msg=r_msg, callback=callback)
            else:
                # sending group message one by one
                for r_msg in messages:
                    if not self.__send_message(msg=r_msg, callback=callback):
                        ok = False
        else:
            ok = self.__send_message(msg=r_msg, callback=callback)
        # TODO: if OK, set iMsg.state = sending; else set iMsg.state = waiting
        return ok

    def __send_message(self, msg: ReliableMessage, callback: Callback) -> bool:
        data = self.serialize_message(msg=msg)
        handler = MessageCallback(msg=msg, cb=callback)
        return self.delegate.send_package(data=data, handler=handler)

    #
    #   ConnectionDelegate
    #
    def received_package(self, data: bytes) -> Optional[bytes]:
        """
        Processing received message package

        :param data: message data
        :return: response message data
        """
        # 1. deserialize message
        r_msg = self.deserialize_message(data=data)
        # 2. process message
        response = self.process_message(msg=r_msg)
        if response is None:
            # nothing to response
            return None
        # 3. pack response
        user = self.facebook.current_user
        assert user is not None, 'failed to get current user'
        sender = self.facebook.identifier(r_msg.envelope.sender)
        i_msg = InstantMessage.new(content=response, sender=user.identifier, receiver=sender)
        s_msg = self.encrypt_message(msg=i_msg)
        msg_r = self.sign_message(msg=s_msg)
        assert msg_r is not None, 'failed to response: %s' % i_msg
        # serialize message
        return self.serialize_message(msg=msg_r)

    def process_message(self, msg: ReliableMessage) -> Optional[Content]:
        # TODO: try to verify/decrypt message and process it
        pass


#
#  global
#
messenger = Messenger()
messenger.barrack = facebook
messenger.key_cache = keystore

User Account

register.py

def register(username: str) -> User:
    # 1. generate private key
    sk = PrivateKey({'algorithm': 'RSA'})

    # 2. generate meta with username(as seed) and private key
    meta = Meta.generate(private_key=sk, seed=username)

    # 3. generate ID with network type by meta
    identifier = meta.generate_identifier(network=network)

    # 4. save private key and meta info
    facebook.save_private_key(private_key=sk, identifier=identifier)
    facebook.save_meta(meta=meta, identifier=identifier)

    # 5. create user with ID
    return facebook.user(identifier)

Messaging

send.py

def pack(content: Content, sender: ID, receiver: ID) -> ReliableMessage:
    # 1. create InstantMessage
    i_msg = InstantMessage.new(content=content, sender=sender, receiver=receiver)
    # 2. encrypt 'content' to 'data' for receiver
    s_msg = messenger.encrypt_message(msg=i_msg)
    # 3. sign 'data' by sender
    r_msg = messenger.sign_message(msg= s_msg)
    # OK
    return r_msg


def send(content: Content, sender: ID, receiver: ID) -> bool:
    # 1. pack message
    r_msg = pack(content=content, sender=sender, receiver=receiver)
    # 2. callback handler
    callback = None
    # 3. encode and send out
    return messenger.send_message(msg=r_msg, callback=callback)


if __name__ == '__main__':
    moki = facebook.identifier('moki@4WDfe3zZ4T7opFSi3iDAKiuTnUHjxmXekk')
    hulk = facebook.identifier('hulk@4YeVEN3aUnvC1DNUufCq1bs9zoBSJTzVEj')
    # Say Hi
    text='Hello world!'
    content = TextContent.new(text=text)
    send(content=content, sender=moki, receiver=hulk)

receive.py

def unpack(msg: ReliableMessage) -> Content:
    # 1. verify 'data' with 'signature'
    s_msg = messenger.verify_message(msg=msg)
    # 2. check group message
    receiver = facebook.identifier(msg.envelope.receiver)
    if receiver.type.is_group():
        # TODO: split it
        pass
    # 3. decrypt 'data' to 'content'
    i_msg = messenger.decrypt_message(msg=s_msg)
    # OK
    return i_msg.content


#
#   StationDelegate
#
def receive_package(data: bytes, station: Station):
    # 1. decode messsage package
    r_msg = messenger.deserialize_message(data=data)
    # 2. verify and decrypt message
    content = unpack(msg=r_msg)
    # TODO: process message content

Copyright © 2019 Albert Moky

Project details


Release history Release notifications | RSS feed

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dimp-0.8.3.tar.gz (21.1 kB view details)

Uploaded Source

Built Distribution

dimp-0.8.3-py3-none-any.whl (33.7 kB view details)

Uploaded Python 3

File details

Details for the file dimp-0.8.3.tar.gz.

File metadata

  • Download URL: dimp-0.8.3.tar.gz
  • Upload date:
  • Size: 21.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/41.0.1 requests-toolbelt/0.9.1 tqdm/4.32.2 CPython/3.7.0b3

File hashes

Hashes for dimp-0.8.3.tar.gz
Algorithm Hash digest
SHA256 6752531ceab028245d5eb32f17b8bc5bbfaf7e6d4b05757808262069d6cabbef
MD5 2c2d9842ec4b01e72e443de05c7b2e6f
BLAKE2b-256 14bd58cb2d3ce51472a1c1e9a1be8959eb582f3ecbd2e02b44c4acb444764c57

See more details on using hashes here.

File details

Details for the file dimp-0.8.3-py3-none-any.whl.

File metadata

  • Download URL: dimp-0.8.3-py3-none-any.whl
  • Upload date:
  • Size: 33.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/41.0.1 requests-toolbelt/0.9.1 tqdm/4.32.2 CPython/3.7.0b3

File hashes

Hashes for dimp-0.8.3-py3-none-any.whl
Algorithm Hash digest
SHA256 c059b701aad6e5c6b0179af65a64b33606778d096d3678d3de8d14dd6732d725
MD5 7cec8019b757ee7c0831538545a8b28a
BLAKE2b-256 c09324f86c3ad3b4921085a00f7b1b6db228f9da5bc4f19892ece7f02fb6daaa

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page