Пример #1
0
    def from_pickle(cls, pickle, passphrase=""):
        # types: (bytes, str) -> PkDecryption
        if not pickle:
            raise ValueError("Pickle can't be empty")

        byte_key = to_bytes(passphrase)
        key_buffer = ffi.new("char[]", byte_key)
        pickle_buffer = ffi.new("char[]", pickle)

        pubkey_length = lib.olm_pk_key_length()
        pubkey_buffer = ffi.new("char[]", pubkey_length)

        obj = cls.__new__(cls)

        ret = lib.olm_unpickle_pk_decryption(
            obj._pk_decryption,
            key_buffer, len(byte_key),
            pickle_buffer, len(pickle),
            pubkey_buffer, pubkey_length)

        obj._check_error(ret)

        obj.public_key = bytes_to_native_str(ffi.unpack(
            pubkey_buffer,
            pubkey_length
        ))

        return obj
Пример #2
0
    def pickle(self, passphrase=""):
        # type: (Optional[str]) -> bytes
        """Store an inbound group session.

        Stores a group session as a base64 string. Encrypts the session using
        the supplied passphrase. Returns a byte object containing the base64
        encoded string of the pickled session.

        Args:
            passphrase(str, optional): The passphrase to be used to encrypt
                the session.
        """
        byte_passphrase = bytes(passphrase, "utf-8") if passphrase else b""

        passphrase_buffer = ffi.new("char[]", byte_passphrase)
        pickle_length = lib.olm_pickle_inbound_group_session_length(
            self._session)
        pickle_buffer = ffi.new("char[]", pickle_length)

        ret = lib.olm_pickle_inbound_group_session(self._session,
                                                   passphrase_buffer,
                                                   len(byte_passphrase),
                                                   pickle_buffer,
                                                   pickle_length)

        self._check_error(ret)

        return ffi.unpack(pickle_buffer, pickle_length)
Пример #3
0
    def decrypt(self, message):
        # type: (_OlmMessage) -> str
        """Decrypts a message using the session. Returns the plaintext string
        on success. Raises OlmSessionError on failure. If the base64 couldn't
        be decoded then the error message will be "INVALID_BASE64". If the
        message is for an unsupported version of the protocol the error message
        will be "BAD_MESSAGE_VERSION". If the message couldn't be decoded then
        the error message will be "BAD_MESSAGE_FORMAT". If the MAC on the
        message was invalid then the error message will be "BAD_MESSAGE_MAC".

        Args:
            message(OlmMessage): The Olm message that will be decrypted. It can
            be either an OlmPreKeyMessage or an OlmMessage.
        """
        if not message.ciphertext:
            raise ValueError("Ciphertext can't be empty")

        byte_ciphertext = to_bytes(message.ciphertext)
        ciphertext_buffer = ffi.new("char[]", byte_ciphertext)

        max_plaintext_length = lib.olm_decrypt_max_plaintext_length(
            self._session, message.message_type, ciphertext_buffer,
            len(byte_ciphertext))
        plaintext_buffer = ffi.new("char[]", max_plaintext_length)
        ciphertext_buffer = ffi.new("char[]", byte_ciphertext)
        plaintext_length = lib.olm_decrypt(self._session, message.message_type,
                                           ciphertext_buffer,
                                           len(byte_ciphertext),
                                           plaintext_buffer,
                                           max_plaintext_length)
        self._check_error(plaintext_length)
        return bytes_to_native_str(
            ffi.unpack(plaintext_buffer, plaintext_length))
Пример #4
0
    def from_pickle(cls, pickle, passphrase=""):
        # type: (bytes, Optional[str]) -> OutboundGroupSession
        """Load a previously stored outbound group session.

        Loads an outbound group session from a pickled base64 string and
        returns an OutboundGroupSession object. Decrypts the session using the
        supplied passphrase. Raises OlmSessionError on failure. If the
        passphrase doesn't match the one used to encrypt the session then the
        error message for the exception will be "BAD_ACCOUNT_KEY". If the
        base64 couldn't be decoded then the error message will be
        "INVALID_BASE64".

        Args:
            pickle(bytes): Base64 encoded byte string containing the pickled
                session
            passphrase(str, optional): The passphrase used to encrypt the
        """
        if not pickle:
            raise ValueError("Pickle can't be empty")

        byte_passphrase = bytes(passphrase, "utf-8") if passphrase else b""
        passphrase_buffer = ffi.new("char[]", byte_passphrase)
        pickle_buffer = ffi.new("char[]", pickle)

        obj = cls.__new__(cls)

        ret = lib.olm_unpickle_outbound_group_session(obj._session,
                                                      passphrase_buffer,
                                                      len(byte_passphrase),
                                                      pickle_buffer,
                                                      len(pickle))
        obj._check_error(ret)

        return obj
Пример #5
0
    def encrypt(self, plaintext):
        # type: (AnyStr) -> str
        """Encrypt a message.

        Returns the encrypted ciphertext.

        Args:
            plaintext(str): A string that will be encrypted using the group
                session.
        """
        byte_plaintext = to_bytes(plaintext)
        message_length = lib.olm_group_encrypt_message_length(
            self._session, len(byte_plaintext))

        message_buffer = ffi.new("char[]", message_length)

        plaintext_buffer = ffi.new("char[]", byte_plaintext)

        ret = lib.olm_group_encrypt(
            self._session,
            plaintext_buffer,
            len(byte_plaintext),
            message_buffer,
            message_length,
        )
        self._check_error(ret)
        return bytes_to_native_str(ffi.unpack(message_buffer, message_length))
Пример #6
0
    def from_pickle(cls, pickle, passphrase=""):
        # type: (bytes, Optional[str]) -> Account
        """Load a previously stored olm account.

        Loads an account from a pickled base64-encoded string and returns an
        Account object. Decrypts the account using the supplied passphrase.
        Raises OlmAccountError on failure. If the passphrase doesn't match the
        one used to encrypt the account then the error message for the
        exception will be "BAD_ACCOUNT_KEY". If the base64 couldn't be decoded
        then the error message will be "INVALID_BASE64".

        Args:
            pickle(bytes): Base64 encoded byte string containing the pickled
                account
            passphrase(str, optional): The passphrase used to encrypt the
                account.
        """
        if not pickle:
            raise ValueError("Pickle can't be empty")

        byte_key = bytes(passphrase, "utf-8") if passphrase else b""
        key_buffer = ffi.new("char[]", byte_key)
        pickle_buffer = ffi.new("char[]", pickle)

        obj = cls.__new__(cls)

        ret = lib.olm_unpickle_account(obj._account, key_buffer, len(byte_key),
                                       pickle_buffer, len(pickle))
        obj._check_error(ret)

        return obj
Пример #7
0
    def __init__(self, seed):
        # type: (bytes) -> None
        """Create a new signing object.

        Args:
            seed(bytes): the seed to use as the private key for signing.  The
                seed must have the same length as the seeds generated by
                PkSigning.generate_seed().
        """
        if not seed:
            raise ValueError("seed can't be empty")

        self._buf = ffi.new("char[]", lib.olm_pk_signing_size())
        self._pk_signing = lib.olm_pk_signing(self._buf)
        track_for_finalization(self, self._pk_signing, _clear_pk_signing)

        seed_buffer = ffi.new("char[]", seed)

        pubkey_length = lib.olm_pk_signing_public_key_length()
        pubkey_buffer = ffi.new("char[]", pubkey_length)

        ret = lib.olm_pk_signing_key_from_seed(self._pk_signing, pubkey_buffer,
                                               pubkey_length, seed_buffer,
                                               len(seed))

        # zero out copies of the seed
        lib.memset(seed_buffer, 0, len(seed))

        self._check_error(ret)

        self.public_key = bytes_to_native_str(
            ffi.unpack(pubkey_buffer, pubkey_length))
Пример #8
0
    def decrypt(self, ciphertext):
        # type: (AnyStr) -> Tuple[str, int]
        """Decrypt a message

        Returns a tuple of the decrypted plain-text and the message index of
        the decrypted message or raises OlmGroupSessionError on failure.
        On failure the error message of the exception  will be:

        * OLM_INVALID_BASE64         if the message is not valid base64
        * OLM_BAD_MESSAGE_VERSION    if the message was encrypted with an
            unsupported version of the protocol
        * OLM_BAD_MESSAGE_FORMAT     if the message headers could not be
            decoded
        * OLM_BAD_MESSAGE_MAC        if the message could not be verified
        * OLM_UNKNOWN_MESSAGE_INDEX  if we do not have a session key
            corresponding to the message's index (i.e., it was sent before
            the session key was shared with us)

        Args:
            ciphertext(str): Base64 encoded ciphertext containing the encrypted
                message
        """
        if not ciphertext:
            raise ValueError("Ciphertext can't be empty.")

        byte_ciphertext = to_bytes(ciphertext)

        # copy because max_plaintext_length will destroy the buffer
        ciphertext_buffer = ffi.new("char[]", byte_ciphertext)

        max_plaintext_length = lib.olm_group_decrypt_max_plaintext_length(
            self._session, ciphertext_buffer, len(byte_ciphertext))
        self._check_error(max_plaintext_length)
        plaintext_buffer = ffi.new("char[]", max_plaintext_length)
        # copy because max_plaintext_length will destroy the buffer
        ciphertext_buffer = ffi.new("char[]", byte_ciphertext)

        message_index = ffi.new("uint32_t*")
        plaintext_length = lib.olm_group_decrypt(self._session,
                                                 ciphertext_buffer,
                                                 len(byte_ciphertext),
                                                 plaintext_buffer,
                                                 max_plaintext_length,
                                                 message_index)

        self._check_error(plaintext_length)

        plaintext = bytes_to_native_str(
            ffi.unpack(plaintext_buffer, plaintext_length))

        # clear out copies of the plaintext
        lib.memset(plaintext_buffer, 0, max_plaintext_length)

        return plaintext, message_index[0]
Пример #9
0
    def decrypt(self, message, unicode_errors="replace"):
        # type: (_OlmMessage, str) -> str
        """Decrypts a message using the session. Returns the plaintext string
        on success. Raises OlmSessionError on failure. If the base64 couldn't
        be decoded then the error message will be "INVALID_BASE64". If the
        message is for an unsupported version of the protocol the error message
        will be "BAD_MESSAGE_VERSION". If the message couldn't be decoded then
        the error message will be "BAD_MESSAGE_FORMAT". If the MAC on the
        message was invalid then the error message will be "BAD_MESSAGE_MAC".

        Args:
            message(OlmMessage): The Olm message that will be decrypted. It can
                be either an OlmPreKeyMessage or an OlmMessage.
            unicode_errors(str, optional): The error handling scheme to use for
                unicode decoding errors. The default is "replace" meaning that
                the character that was unable to decode will be replaced with
                the unicode replacement character (U+FFFD). Other possible
                values are "strict", "ignore" and "xmlcharrefreplace" as well
                as any other name registered with codecs.register_error that
                can handle UnicodeEncodeErrors.
        """
        if not message.ciphertext:
            raise ValueError("Ciphertext can't be empty")

        byte_ciphertext = to_bytes(message.ciphertext)
        # make a copy the ciphertext buffer, because
        # olm_decrypt_max_plaintext_length wants to destroy something
        ciphertext_buffer = ffi.new("char[]", byte_ciphertext)

        max_plaintext_length = lib.olm_decrypt_max_plaintext_length(
            self._session, message.message_type, ciphertext_buffer,
            len(byte_ciphertext))
        self._check_error(max_plaintext_length)
        plaintext_buffer = ffi.new("char[]", max_plaintext_length)

        # make a copy the ciphertext buffer, because
        # olm_decrypt_max_plaintext_length wants to destroy something
        ciphertext_buffer = ffi.new("char[]", byte_ciphertext)
        plaintext_length = lib.olm_decrypt(self._session, message.message_type,
                                           ciphertext_buffer,
                                           len(byte_ciphertext),
                                           plaintext_buffer,
                                           max_plaintext_length)
        self._check_error(plaintext_length)
        plaintext = to_unicode_str(ffi.unpack(plaintext_buffer,
                                              plaintext_length),
                                   errors=unicode_errors)

        # clear out copies of the plaintext
        lib.memset(plaintext_buffer, 0, max_plaintext_length)

        return plaintext
Пример #10
0
    def matches(self, message, identity_key=None):
        # type: (OlmPreKeyMessage, Optional[AnyStr]) -> bool
        """Checks if the PRE_KEY message is for this in-bound session.
        This can happen if multiple messages are sent to this session before
        this session sends a message in reply. Returns True if the session
        matches. Returns False if the session does not match. Raises
        OlmSessionError on failure. If the base64 couldn't be decoded then the
        error message will be "INVALID_BASE64". If the message was for an
        unsupported protocol version then the error message will be
        "BAD_MESSAGE_VERSION". If the message couldn't be decoded then then the
        error message will be * "BAD_MESSAGE_FORMAT".

        Args:
            message(OlmPreKeyMessage): The Olm prekey message that will checked
                if it is intended for this session.
            identity_key(str, optional): The identity key of the sender. To
                check if the message was also sent using this identity key.
        """
        if not isinstance(message, OlmPreKeyMessage):
            raise TypeError("Matches can only be called with prekey messages.")

        if not message.ciphertext:
            raise ValueError("Ciphertext can't be empty")

        ret = None

        byte_ciphertext = to_bytes(message.ciphertext)

        message_buffer = ffi.new("char[]", byte_ciphertext)

        if identity_key:
            byte_id_key = to_bytes(identity_key)
            identity_key_buffer = ffi.new("char[]", byte_id_key)

            ret = lib.olm_matches_inbound_session_from(self._session,
                                                       identity_key_buffer,
                                                       len(byte_id_key),
                                                       message_buffer,
                                                       len(byte_ciphertext))

        else:
            ret = lib.olm_matches_inbound_session(self._session,
                                                  message_buffer,
                                                  len(byte_ciphertext))

        self._check_error(ret)

        return bool(ret)
Пример #11
0
    def encrypt(self, plaintext):
        # type: (AnyStr) -> _OlmMessage
        """Encrypts a message using the session. Returns the ciphertext as an
        base64 encoded strin on success. Raises OlmSessionError on failure. If
        there weren't enough random bytes to encrypt the message the error
        message for the exception will be NOT_ENOUGH_RANDOM.

        Args:
            plaintext(str): The plaintext message that will be encrypted.
        """
        byte_plaintext = to_bytes(plaintext)

        r_length = lib.olm_encrypt_random_length(self._session)
        random = URANDOM(r_length)
        random_buffer = ffi.new("char[]", random)

        message_type = lib.olm_encrypt_message_type(self._session)

        self._check_error(message_type)

        ciphertext_length = lib.olm_encrypt_message_length(
            self._session, len(plaintext)
        )
        ciphertext_buffer = ffi.new("char[]", ciphertext_length)

        plaintext_buffer = ffi.new("char[]", byte_plaintext)

        self._check_error(lib.olm_encrypt(
            self._session,
            plaintext_buffer, len(byte_plaintext),
            random_buffer, r_length,
            ciphertext_buffer, ciphertext_length,
        ))

        if message_type == lib.OLM_MESSAGE_TYPE_PRE_KEY:
            return OlmPreKeyMessage(
                bytes_to_native_str(ffi.unpack(
                    ciphertext_buffer,
                    ciphertext_length
                )))
        elif message_type == lib.OLM_MESSAGE_TYPE_MESSAGE:
            return OlmMessage(
                bytes_to_native_str(ffi.unpack(
                    ciphertext_buffer,
                    ciphertext_length
                )))
        else:  # pragma: no cover
            raise ValueError("Unknown message type")
Пример #12
0
    def generate_bytes(self, extra_info, length):
        # type: (str, int) -> bytes
        """Generate bytes to use for the short authentication string.

        Args:
            extra_info (str): Extra information to mix in when generating the
                bytes.
            length (int): The number of bytes to generate.

        Raises OlmSasError if the other users persons public key isn't set or
        an internal Olm error happens.

        """
        if length < 1:
            raise ValueError("The length needs to be a positive integer value")

        byte_info = to_bytearray(extra_info)
        out_buffer = ffi.new("char[]", length)

        self._check_error(
            lib.olm_sas_generate_bytes(
                self._sas,
                ffi.from_buffer(byte_info),
                len(byte_info),
                out_buffer,
                length
            )
        )

        return ffi.unpack(out_buffer, length)
Пример #13
0
    def pickle(self, passphrase=""):
        # type: (str) -> bytes
        """Store a PkDecryption object.

        Stores a PkDecryption object as a base64 string. Encrypts the object
        using the supplied passphrase. Returns a byte object containing the
        base64 encoded string of the pickled session.

        Args:
            passphrase(str, optional): The passphrase to be used to encrypt
                the object.
        """
        byte_key = to_bytearray(passphrase)

        pickle_length = lib.olm_pickle_pk_decryption_length(
            self._pk_decryption)
        pickle_buffer = ffi.new("char[]", pickle_length)

        ret = lib.olm_pickle_pk_decryption(self._pk_decryption,
                                           ffi.from_buffer(byte_key),
                                           len(byte_key), pickle_buffer,
                                           pickle_length)
        try:
            self._check_error(ret)
        finally:
            # zero out copies of the passphrase
            for i in range(0, len(byte_key)):
                byte_key[i] = 0

        return ffi.unpack(pickle_buffer, pickle_length)
Пример #14
0
 def __new__(cls, session_key=None):
     # type: (Type[InboundGroupSession], Optional[str]) -> InboundGroupSession
     obj = super().__new__(cls)
     obj._buf = ffi.new("char[]", lib.olm_inbound_group_session_size())
     obj._session = lib.olm_inbound_group_session(obj._buf)
     track_for_finalization(obj, obj._session, _clear_inbound_group_session)
     return obj
Пример #15
0
    def __init__(self, other_users_pubkey=None):
        # type: (Optional[str]) -> None
        """Create a new SAS object.

        Args:
            other_users_pubkey(str, optional): The other users public key, this
                key is necesary to generate bytes for the authentication string
                as well as to calculate the MAC.

        Attributes:
            other_key_set (bool): A boolean flag that tracks if we set the
                other users public key for this SAS object.

        Raises OlmSasError on failure.

        """
        self._buf = ffi.new("char[]", lib.olm_sas_size())
        self._sas = lib.olm_sas(self._buf)
        self.other_key_set = False
        track_for_finalization(self, self._sas, _clear_sas)

        random_length = lib.olm_create_sas_random_length(self._sas)
        random = URANDOM(random_length)

        self._create_sas(random, random_length)

        if other_users_pubkey:
            self.set_their_pubkey(other_users_pubkey)
Пример #16
0
    def sign(self, message):
        # type: (AnyStr) -> str
        """Signs a message with this account.

        Signs a message with the private ed25519 identity key of this account.
        Returns the signature.
        Raises OlmAccountError on failure.

        Args:
            message(str): The message to sign.
        """
        bytes_message = to_bytearray(message)
        out_length = lib.olm_account_signature_length(self._account)
        out_buffer = ffi.new("char[]", out_length)

        try:
            self._check_error(
                lib.olm_account_sign(self._account,
                                     ffi.from_buffer(bytes_message),
                                     len(bytes_message), out_buffer,
                                     out_length))
        finally:
            # clear out copies of the message, which may be plaintext
            if bytes_message is not message:
                for i in range(0, len(bytes_message)):
                    bytes_message[i] = 0

        return bytes_to_native_str(ffi.unpack(out_buffer, out_length))
Пример #17
0
    def pickle(self, passphrase=""):
        # type: (Optional[str]) -> bytes
        """Store an Olm account.

        Stores an account as a base64 string. Encrypts the account using the
        supplied passphrase. Returns a byte object containing the base64
        encoded string of the pickled account. Raises OlmAccountError on
        failure.

        Args:
            passphrase(str, optional): The passphrase to be used to encrypt
                the account.
        """
        byte_key = bytearray(passphrase, "utf-8") if passphrase else b""

        pickle_length = lib.olm_pickle_account_length(self._account)
        pickle_buffer = ffi.new("char[]", pickle_length)

        try:
            self._check_error(
                lib.olm_pickle_account(self._account,
                                       ffi.from_buffer(byte_key),
                                       len(byte_key),
                                       pickle_buffer,
                                       pickle_length))
        finally:
            # zero out copies of the passphrase
            for i in range(0, len(byte_key)):
                byte_key[i] = 0

        return ffi.unpack(pickle_buffer, pickle_length)
Пример #18
0
    def calculate_mac(self, message, extra_info):
        # type: (str, str) -> str
        """Generate a message authentication code based on the shared secret.

        Args:
            message (str): The message to produce the authentication code for.
            extra_info (str): Extra information to mix in when generating the
                MAC

        Raises OlmSasError on failure.

        """
        byte_message = to_bytes(message)
        byte_info = to_bytes(extra_info)

        mac_length = lib.olm_sas_mac_length(self._sas)
        mac_buffer = ffi.new("char[]", mac_length)

        self._check_error(
            lib.olm_sas_calculate_mac(
                self._sas,
                ffi.from_buffer(byte_message),
                len(byte_message),
                ffi.from_buffer(byte_info),
                len(byte_info),
                mac_buffer,
                mac_length
            )
        )
        return bytes_to_native_str(ffi.unpack(mac_buffer, mac_length))
Пример #19
0
    def calculate_mac_long_kdf(self, message, extra_info):
        # type: (str, str) -> str
        """Generate a message authentication code based on the shared secret.

        This function should not be used unless compatibility with an older
        non-tagged Olm version is required.

        Args:
            message (str): The message to produce the authentication code for.
            extra_info (str): Extra information to mix in when generating the
                MAC

        Raises OlmSasError on failure.

        """
        byte_message = to_bytes(message)
        byte_info = to_bytes(extra_info)

        mac_length = lib.olm_sas_mac_length(self._sas)
        mac_buffer = ffi.new("char[]", mac_length)

        self._check_error(
            lib.olm_sas_calculate_mac_long_kdf(
                self._sas,
                ffi.from_buffer(byte_message),
                len(byte_message),
                ffi.from_buffer(byte_info),
                len(byte_info),
                mac_buffer,
                mac_length
            )
        )
        return bytes_to_native_str(ffi.unpack(mac_buffer, mac_length))
Пример #20
0
 def __new__(cls):
     # type: (Type[Account]) -> Account
     obj = super().__new__(cls)
     obj._buf = ffi.new("char[]", lib.olm_account_size())
     obj._account = lib.olm_account(obj._buf)
     track_for_finalization(obj, obj._account, _clear_account)
     return obj
Пример #21
0
    def decrypt(self, message):
        # type (PkMessage) -> str
        ephermal_key = to_bytes(message.ephermal_key)
        ephermal_key_size = len(ephermal_key)

        mac = to_bytes(message.mac)
        mac_length = len(mac)

        ciphertext = to_bytes(message.ciphertext)
        ciphertext_length = len(ciphertext)

        max_plaintext_length = lib.olm_pk_max_plaintext_length(
            self._pk_decryption,
            ciphertext_length
        )
        plaintext = ffi.new("char[]", max_plaintext_length)

        ret = lib.olm_pk_decrypt(
            self._pk_decryption,
            ephermal_key, ephermal_key_size,
            mac, mac_length,
            ciphertext, ciphertext_length,
            plaintext, max_plaintext_length)
        self._check_error(ret)

        unpacked_plaintext = (ffi.unpack(
            plaintext,
            ret
        ))

        return bytes_to_native_str(unpacked_plaintext)
Пример #22
0
    def import_session(cls, session_key):
        # type: (AnyStr) -> InboundGroupSession
        """Create an InboundGroupSession from an exported session key.

        Creates an InboundGroupSession with an previously exported session key,
        raises OlmGroupSessionError on failure. The error message for the
        exception will be:

        * OLM_INVALID_BASE64  if the session_key is not valid base64
        * OLM_BAD_SESSION_KEY if the session_key is invalid

        Args:
            session_key(str): The exported session key with which the inbound
                group session will be created
        """
        obj = cls.__new__(cls)

        byte_session_key = to_bytes(session_key)

        key_buffer = ffi.new("char[]", byte_session_key)
        ret = lib.olm_import_inbound_group_session(obj._session, key_buffer,
                                                   len(byte_session_key))
        obj._check_error(ret)

        return obj
Пример #23
0
    def export_session(self, message_index):
        # type: (int) -> str
        """Export an inbound group session

        Export the base64-encoded ratchet key for this session, at the given
        index, in a format which can be used by import_session().

        Raises OlmGroupSessionError on failure. The error message for the
        exception will be:

        * OLM_UNKNOWN_MESSAGE_INDEX if we do not have a session key
            corresponding to the given index (ie, it was sent before the
            session key was shared with us)

        Args:
            message_index(int): The message index at which the session should
                be exported.
        """

        export_length = lib.olm_export_inbound_group_session_length(
            self._session)

        export_buffer = ffi.new("char[]", export_length)
        ret = lib.olm_export_inbound_group_session(self._session,
                                                   export_buffer,
                                                   export_length,
                                                   message_index)
        self._check_error(ret)
        return bytes_to_native_str(ffi.unpack(export_buffer, export_length))
Пример #24
0
    def encrypt(self, plaintext):
        # type: (AnyStr) -> str
        """Encrypt a message.

        Returns the encrypted ciphertext.

        Args:
            plaintext(str): A string that will be encrypted using the group
                session.
        """
        byte_plaintext = to_bytearray(plaintext)
        message_length = lib.olm_group_encrypt_message_length(
            self._session, len(byte_plaintext))

        message_buffer = ffi.new("char[]", message_length)

        try:
            ret = lib.olm_group_encrypt(
                self._session,
                ffi.from_buffer(byte_plaintext),
                len(byte_plaintext),
                message_buffer,
                message_length,
            )
            self._check_error(ret)
        finally:
            # clear out copies of plaintext
            if byte_plaintext is not plaintext:
                for i in range(0, len(byte_plaintext)):
                    byte_plaintext[i] = 0

        return bytes_to_native_str(ffi.unpack(message_buffer, message_length))
Пример #25
0
    def pickle(self, passphrase=""):
        # type: (Optional[str]) -> bytes
        """Store an outbound group session.

        Stores a group session as a base64 string. Encrypts the session using
        the supplied passphrase. Returns a byte object containing the base64
        encoded string of the pickled session.

        Args:
            passphrase(str, optional): The passphrase to be used to encrypt
                the session.
        """
        byte_passphrase = bytearray(passphrase, "utf-8") if passphrase else b""
        pickle_length = lib.olm_pickle_outbound_group_session_length(
            self._session)
        pickle_buffer = ffi.new("char[]", pickle_length)

        try:
            ret = lib.olm_pickle_outbound_group_session(
                self._session, ffi.from_buffer(byte_passphrase),
                len(byte_passphrase), pickle_buffer, pickle_length)
            self._check_error(ret)
        finally:
            # clear out copies of the passphrase
            for i in range(0, len(byte_passphrase)):
                byte_passphrase[i] = 0

        return ffi.unpack(pickle_buffer, pickle_length)
Пример #26
0
    def __init__(self):
        if False:  # pragma: no cover
            self._pk_decryption = self._pk_decryption  # type: ffi.cdata

        random_length = lib.olm_pk_private_key_length()
        random = URANDOM(random_length)
        random_buffer = ffi.new("char[]", random)

        key_length = lib.olm_pk_key_length()
        key_buffer = ffi.new("char[]", key_length)

        ret = lib.olm_pk_key_from_private(self._pk_decryption, key_buffer,
                                          key_length, random_buffer,
                                          random_length)
        self._check_error(ret)
        self.public_key = bytes_to_native_str(
            ffi.unpack(key_buffer, key_length))
Пример #27
0
    def __init__(self, account, identity_key, one_time_key):
        # type: (Account, AnyStr, AnyStr) -> None
        """Create a new outbound Olm session.

        Creates a new outbound session for sending messages to a given
        identity key and one-time key.

        Raises OlmSessionError on failure. If the keys couldn't be decoded as
        base64 then the error message will be "INVALID_BASE64". If there
        weren't enough random bytes for the session creation the error message
        for the exception will be NOT_ENOUGH_RANDOM.

        Args:
            account(Account): The Olm Account that will be used to create this
                session.
            identity_key(str): The identity key of the person with whom we want
                to start the session.
            one_time_key(str): A one-time key from the person with whom we want
                to start the session.
        """
        if not identity_key:
            raise ValueError("Identity key can't be empty")

        if not one_time_key:
            raise ValueError("One-time key can't be empty")

        super().__init__()

        byte_id_key = to_bytes(identity_key)
        byte_one_time = to_bytes(one_time_key)

        session_random_length = lib.olm_create_outbound_session_random_length(
            self._session)

        random = URANDOM(session_random_length)
        random_buffer = ffi.new("char[]", random)
        identity_key_buffer = ffi.new("char[]", byte_id_key)
        one_time_key_buffer = ffi.new("char[]", byte_one_time)

        self._check_error(
            lib.olm_create_outbound_session(self._session, account._account,
                                            identity_key_buffer,
                                            len(byte_id_key),
                                            one_time_key_buffer,
                                            len(byte_one_time), random_buffer,
                                            session_random_length))
Пример #28
0
 def __new__(cls):
     # type: (Type[PkDecryption]) -> PkDecryption
     obj = super().__new__(cls)
     obj._buf = ffi.new("char[]", lib.olm_pk_decryption_size())
     obj._pk_decryption = lib.olm_pk_decryption(obj._buf)
     obj.public_key = None
     track_for_finalization(obj, obj._pk_decryption, _clear_pk_decryption)
     return obj
Пример #29
0
 def __new__(cls):
     # type: (Type[OutboundGroupSession]) -> OutboundGroupSession
     obj = super().__new__(cls)
     obj._buf = ffi.new("char[]", lib.olm_outbound_group_session_size())
     obj._session = lib.olm_outbound_group_session(obj._buf)
     track_for_finalization(obj, obj._session,
                            _clear_outbound_group_session)
     return obj
Пример #30
0
    def encrypt(self, plaintext):
        # type: (AnyStr) -> PkMessage
        """Encrypt a message.

        Returns the encrypted PkMessage.

        Args:
            plaintext(str): A string that will be encrypted using the
            PkEncryption object.
        """
        byte_plaintext = to_bytearray(plaintext)

        r_length = lib.olm_pk_encrypt_random_length(self._pk_encryption)
        random = URANDOM(r_length)
        random_buffer = ffi.new("char[]", random)

        ciphertext_length = lib.olm_pk_ciphertext_length(
            self._pk_encryption, len(byte_plaintext))
        ciphertext = ffi.new("char[]", ciphertext_length)

        mac_length = lib.olm_pk_mac_length(self._pk_encryption)
        mac = ffi.new("char[]", mac_length)

        ephemeral_key_size = lib.olm_pk_key_length()
        ephemeral_key = ffi.new("char[]", ephemeral_key_size)

        ret = lib.olm_pk_encrypt(self._pk_encryption,
                                 ffi.from_buffer(byte_plaintext),
                                 len(byte_plaintext), ciphertext,
                                 ciphertext_length, mac, mac_length,
                                 ephemeral_key, ephemeral_key_size,
                                 random_buffer, r_length)

        try:
            self._check_error(ret)
        finally:  # pragma: no cover
            # clear out copies of plaintext
            if byte_plaintext is not plaintext:
                for i in range(0, len(byte_plaintext)):
                    byte_plaintext[i] = 0

        message = PkMessage(
            bytes_to_native_str(ffi.unpack(ephemeral_key, ephemeral_key_size)),
            bytes_to_native_str(ffi.unpack(mac, mac_length)),
            bytes_to_native_str(ffi.unpack(ciphertext, ciphertext_length)))
        return message