From e3d66733712e161d9287ea3f0116e5b57477b0d8 Mon Sep 17 00:00:00 2001 From: Damir Jelić Date: Sun, 8 Jul 2018 12:19:16 +0200 Subject: python: Import improved python bindings. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit imports the python bindings from: https://github.com/poljar/python-olm The bindings are imported at commit c44b145818520d69eaaa350fb95afcb846125e0f Minor modifications were made while importing: - Removed travis config - Removed Arch Linux PKGBUILD - Removed the html docs, they can be rebuild by running make html in the docs folder - Slightly modified the README The new bindings feature some improvements over the old ones: - Python 2 and 3 support - Automatic memory management - Automatic memory clearing before it is freed - Type signatures via the python typing module - Full test coverage - Properties are utilized where it makes sense (e.g. account.id) Signed-off-by: Damir Jelić --- python/Makefile | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 python/Makefile (limited to 'python/Makefile') diff --git a/python/Makefile b/python/Makefile new file mode 100644 index 0000000..998d307 --- /dev/null +++ b/python/Makefile @@ -0,0 +1,35 @@ +PYTHON ?= python + +all: olm + +olm: + $(PYTHON) setup.py build + +install: olm + $(PYTHON) setup.py install --skip-build -O1 --root=$(DESTDIR) + +test: develop py2develop + python3 -m pytest + python2 -m pytest + python3 -m pytest --flake8 --benchmark-disable + python3 -m pytest --isort --benchmark-disable + python3 -m pytest --cov --cov-branch --benchmark-disable + +clean: + -rm -r python_olm.egg-info/ dist/ __pycache__/ + -rm *.so _libolm.o + -rm -r packages/ + -rm -r build/ + +develop: _libolm.o +py2develop: _libolm.so + +_libolm.so: include/olm/olm.h olm_build.py + python2 olm_build.py + -rm _libolm.c + +_libolm.o: include/olm/olm.h olm_build.py + python3 olm_build.py + -rm _libolm.c + +.PHONY: all olm install clean test develop -- cgit v1.2.3-70-g09d2 From ac071d9c0d69e4330a06f171e3bddc713ecd97d6 Mon Sep 17 00:00:00 2001 From: Damir Jelić Date: Sun, 8 Jul 2018 12:19:17 +0200 Subject: python: Enable build with the local build of the Olm C library. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds the ability to build the bindings without having a globally installed Olm C library. Provided that the C library is already built, the tests can be run now with make test. Signed-off-by: Damir Jelić --- Makefile | 1 + python/Makefile | 8 +++++--- python/olm_build.py | 14 +++++++++++++- 3 files changed, 19 insertions(+), 4 deletions(-) (limited to 'python/Makefile') diff --git a/Makefile b/Makefile index 154954c..be50a8f 100644 --- a/Makefile +++ b/Makefile @@ -125,6 +125,7 @@ $(RELEASE_TARGET): $(RELEASE_OBJECTS) -Wl,--version-script,version_script.ver \ $(OUTPUT_OPTION) $(RELEASE_OBJECTS) ln -sf libolm.so.$(VERSION) $(BUILD_DIR)/libolm.so.$(MAJOR) + ln -sf libolm.so.$(VERSION) $(BUILD_DIR)/libolm.so debug: $(DEBUG_TARGET) .PHONY: debug diff --git a/python/Makefile b/python/Makefile index 998d307..2ab839c 100644 --- a/python/Makefile +++ b/python/Makefile @@ -3,7 +3,7 @@ PYTHON ?= python all: olm olm: - $(PYTHON) setup.py build + DEVELOP=$(DEVELOP) $(PYTHON) setup.py build install: olm $(PYTHON) setup.py install --skip-build -O1 --root=$(DESTDIR) @@ -24,12 +24,14 @@ clean: develop: _libolm.o py2develop: _libolm.so +_libolm.so: DEVELOP ?= 1 _libolm.so: include/olm/olm.h olm_build.py - python2 olm_build.py + DEVELOP=$(DEVELOP) python2 olm_build.py -rm _libolm.c +_libolm.o: DEVELOP ?= 1 _libolm.o: include/olm/olm.h olm_build.py - python3 olm_build.py + DEVELOP=$(DEVELOP) python3 olm_build.py -rm _libolm.c .PHONY: all olm install clean test develop diff --git a/python/olm_build.py b/python/olm_build.py index 5ffefc2..281a571 100644 --- a/python/olm_build.py +++ b/python/olm_build.py @@ -24,6 +24,15 @@ from cffi import FFI ffibuilder = FFI() PATH = os.path.dirname(__file__) +DEVELOP = os.environ.get("DEVELOP") + +compile_args = [] +link_args = [] + +if DEVELOP and DEVELOP.lower() in ["yes", "true", "1"]: + compile_args = ["-I../include"] + link_args = ['-Wl,-L=../build,-rpath=../build'] + ffibuilder.set_source( "_libolm", @@ -31,7 +40,10 @@ ffibuilder.set_source( #include #include #include - """, libraries=["olm"]) + """, + libraries=["olm"], + extra_compile_args=compile_args, + extra_link_args=link_args) with open(os.path.join(PATH, "include/olm/olm.h")) as f: ffibuilder.cdef(f.read(), override=True) -- cgit v1.2.3-70-g09d2 From d4b2cce60345662512238f5e26bd4eff2d83ba4c Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Fri, 12 Oct 2018 18:53:18 -0400 Subject: generate python/include/olm/olm.h automatically --- python/.gitignore | 1 + python/Makefile | 6 +- python/include/olm/olm.h | 787 ----------------------------------------------- 3 files changed, 6 insertions(+), 788 deletions(-) delete mode 100644 python/include/olm/olm.h (limited to 'python/Makefile') diff --git a/python/.gitignore b/python/.gitignore index 3393fec..1ff9f49 100644 --- a/python/.gitignore +++ b/python/.gitignore @@ -9,3 +9,4 @@ __pycache__ *.pyc .hypothesis/ .tox/ +include/ diff --git a/python/Makefile b/python/Makefile index 2ab839c..ad2621c 100644 --- a/python/Makefile +++ b/python/Makefile @@ -2,7 +2,11 @@ PYTHON ?= python all: olm -olm: +include/olm/olm.h: ../include/olm/olm.h ../include/olm/inbound_group_session.h ../include/olm/outbound_group_session.h + mkdir -p include/olm + $(CPP) -I dummy -I ../include ../include/olm/olm.h -o include/olm/olm.h + +olm: include/olm/olm.h DEVELOP=$(DEVELOP) $(PYTHON) setup.py build install: olm diff --git a/python/include/olm/olm.h b/python/include/olm/olm.h deleted file mode 100644 index 1ea0971..0000000 --- a/python/include/olm/olm.h +++ /dev/null @@ -1,787 +0,0 @@ -/* Copyright 2015, 2016 OpenMarket Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -static const size_t OLM_MESSAGE_TYPE_PRE_KEY = 0; -static const size_t OLM_MESSAGE_TYPE_MESSAGE = 1; - -typedef struct OlmAccount OlmAccount; -typedef struct OlmSession OlmSession; -typedef struct OlmUtility OlmUtility; - -/** Get the version number of the library. - * Arguments will be updated if non-null. - */ -void olm_get_library_version(uint8_t *major, uint8_t *minor, uint8_t *patch); - -/** The size of an account object in bytes */ -size_t olm_account_size(); - -/** The size of a session object in bytes */ -size_t olm_session_size(); - -/** The size of a utility object in bytes */ -size_t olm_utility_size(); - -/** Initialise an account object using the supplied memory - * The supplied memory must be at least olm_account_size() bytes */ -OlmAccount * olm_account( - void * memory -); - -/** Initialise a session object using the supplied memory - * The supplied memory must be at least olm_session_size() bytes */ -OlmSession * olm_session( - void * memory -); - -/** Initialise a utility object using the supplied memory - * The supplied memory must be at least olm_utility_size() bytes */ -OlmUtility * olm_utility( - void * memory -); - -/** The value that olm will return from a function if there was an error */ -size_t olm_error(); - -/** A null terminated string describing the most recent error to happen to an - * account */ -const char * olm_account_last_error( - OlmAccount * account -); - -/** A null terminated string describing the most recent error to happen to a - * session */ -const char * olm_session_last_error( - OlmSession * session -); - -/** A null terminated string describing the most recent error to happen to a - * utility */ -const char * olm_utility_last_error( - OlmUtility * utility -); - -/** Clears the memory used to back this account */ -size_t olm_clear_account( - OlmAccount * account -); - -/** Clears the memory used to back this session */ -size_t olm_clear_session( - OlmSession * session -); - -/** Clears the memory used to back this utility */ -size_t olm_clear_utility( - OlmUtility * utility -); - -/** Returns the number of bytes needed to store an account */ -size_t olm_pickle_account_length( - OlmAccount * account -); - -/** Returns the number of bytes needed to store a session */ -size_t olm_pickle_session_length( - OlmSession * session -); - -/** Stores an account as a base64 string. Encrypts the account using the - * supplied key. Returns the length of the pickled account on success. - * Returns olm_error() on failure. If the pickle output buffer - * is smaller than olm_pickle_account_length() then - * olm_account_last_error() will be "OUTPUT_BUFFER_TOO_SMALL" */ -size_t olm_pickle_account( - OlmAccount * account, - void const * key, size_t key_length, - void * pickled, size_t pickled_length -); - -/** Stores a session as a base64 string. Encrypts the session using the - * supplied key. Returns the length of the pickled session on success. - * Returns olm_error() on failure. If the pickle output buffer - * is smaller than olm_pickle_session_length() then - * olm_session_last_error() will be "OUTPUT_BUFFER_TOO_SMALL" */ -size_t olm_pickle_session( - OlmSession * session, - void const * key, size_t key_length, - void * pickled, size_t pickled_length -); - -/** Loads an account from a pickled base64 string. Decrypts the account using - * the supplied key. Returns olm_error() on failure. If the key doesn't - * match the one used to encrypt the account then olm_account_last_error() - * will be "BAD_ACCOUNT_KEY". If the base64 couldn't be decoded then - * olm_account_last_error() will be "INVALID_BASE64". The input pickled - * buffer is destroyed */ -size_t olm_unpickle_account( - OlmAccount * account, - void const * key, size_t key_length, - void * pickled, size_t pickled_length -); - -/** Loads a session from a pickled base64 string. Decrypts the session using - * the supplied key. Returns olm_error() on failure. If the key doesn't - * match the one used to encrypt the account then olm_session_last_error() - * will be "BAD_ACCOUNT_KEY". If the base64 couldn't be decoded then - * olm_session_last_error() will be "INVALID_BASE64". The input pickled - * buffer is destroyed */ -size_t olm_unpickle_session( - OlmSession * session, - void const * key, size_t key_length, - void * pickled, size_t pickled_length -); - -/** The number of random bytes needed to create an account.*/ -size_t olm_create_account_random_length( - OlmAccount * account -); - -/** Creates a new account. Returns olm_error() on failure. If weren't - * enough random bytes then olm_account_last_error() will be - * "NOT_ENOUGH_RANDOM" */ -size_t olm_create_account( - OlmAccount * account, - void * random, size_t random_length -); - -/** The size of the output buffer needed to hold the identity keys */ -size_t olm_account_identity_keys_length( - OlmAccount * account -); - -/** Writes the public parts of the identity keys for the account into the - * identity_keys output buffer. Returns olm_error() on failure. If the - * identity_keys buffer was too small then olm_account_last_error() will be - * "OUTPUT_BUFFER_TOO_SMALL". */ -size_t olm_account_identity_keys( - OlmAccount * account, - void * identity_keys, size_t identity_key_length -); - - -/** The length of an ed25519 signature encoded as base64. */ -size_t olm_account_signature_length( - OlmAccount * account -); - -/** Signs a message with the ed25519 key for this account. Returns olm_error() - * on failure. If the signature buffer was too small then - * olm_account_last_error() will be "OUTPUT_BUFFER_TOO_SMALL" */ -size_t olm_account_sign( - OlmAccount * account, - void const * message, size_t message_length, - void * signature, size_t signature_length -); - -/** The size of the output buffer needed to hold the one time keys */ -size_t olm_account_one_time_keys_length( - OlmAccount * account -); - -/** Writes the public parts of the unpublished one time keys for the account - * into the one_time_keys output buffer. - *

- * The returned data is a JSON-formatted object with the single property - * curve25519, which is itself an object mapping key id to - * base64-encoded Curve25519 key. For example: - *

- * {
- *     curve25519: {
- *         "AAAAAA": "wo76WcYtb0Vk/pBOdmduiGJ0wIEjW4IBMbbQn7aSnTo",
- *         "AAAAAB": "LRvjo46L1X2vx69sS9QNFD29HWulxrmW11Up5AfAjgU"
- *     }
- * }
- * 
- * Returns olm_error() on failure. - *

- * If the one_time_keys buffer was too small then olm_account_last_error() - * will be "OUTPUT_BUFFER_TOO_SMALL". */ -size_t olm_account_one_time_keys( - OlmAccount * account, - void * one_time_keys, size_t one_time_keys_length -); - -/** Marks the current set of one time keys as being published. */ -size_t olm_account_mark_keys_as_published( - OlmAccount * account -); - -/** The largest number of one time keys this account can store. */ -size_t olm_account_max_number_of_one_time_keys( - OlmAccount * account -); - -/** The number of random bytes needed to generate a given number of new one - * time keys. */ -size_t olm_account_generate_one_time_keys_random_length( - OlmAccount * account, - size_t number_of_keys -); - -/** Generates a number of new one time keys. If the total number of keys stored - * by this account exceeds max_number_of_one_time_keys() then the old keys are - * discarded. Returns olm_error() on error. If the number of random bytes is - * too small then olm_account_last_error() will be "NOT_ENOUGH_RANDOM". */ -size_t olm_account_generate_one_time_keys( - OlmAccount * account, - size_t number_of_keys, - void * random, size_t random_length -); - -/** The number of random bytes needed to create an outbound session */ -size_t olm_create_outbound_session_random_length( - OlmSession * session -); - -/** Creates a new out-bound session for sending messages to a given identity_key - * and one_time_key. Returns olm_error() on failure. If the keys couldn't be - * decoded as base64 then olm_session_last_error() will be "INVALID_BASE64" - * If there weren't enough random bytes then olm_session_last_error() will - * be "NOT_ENOUGH_RANDOM". */ -size_t olm_create_outbound_session( - OlmSession * session, - OlmAccount * account, - void const * their_identity_key, size_t their_identity_key_length, - void const * their_one_time_key, size_t their_one_time_key_length, - void * random, size_t random_length -); - -/** Create a new in-bound session for sending/receiving messages from an - * incoming PRE_KEY message. Returns olm_error() on failure. If the base64 - * couldn't be decoded then olm_session_last_error will be "INVALID_BASE64". - * If the message was for an unsupported protocol version then - * olm_session_last_error() will be "BAD_MESSAGE_VERSION". If the message - * couldn't be decoded then then olm_session_last_error() will be - * "BAD_MESSAGE_FORMAT". If the message refers to an unknown one time - * key then olm_session_last_error() will be "BAD_MESSAGE_KEY_ID". */ -size_t olm_create_inbound_session( - OlmSession * session, - OlmAccount * account, - void * one_time_key_message, size_t message_length -); - -/** Create a new in-bound session for sending/receiving messages from an - * incoming PRE_KEY message. Returns olm_error() on failure. If the base64 - * couldn't be decoded then olm_session_last_error will be "INVALID_BASE64". - * If the message was for an unsupported protocol version then - * olm_session_last_error() will be "BAD_MESSAGE_VERSION". If the message - * couldn't be decoded then then olm_session_last_error() will be - * "BAD_MESSAGE_FORMAT". If the message refers to an unknown one time - * key then olm_session_last_error() will be "BAD_MESSAGE_KEY_ID". */ -size_t olm_create_inbound_session_from( - OlmSession * session, - OlmAccount * account, - void const * their_identity_key, size_t their_identity_key_length, - void * one_time_key_message, size_t message_length -); - -/** The length of the buffer needed to return the id for this session. */ -size_t olm_session_id_length( - OlmSession * session -); - -/** An identifier for this session. Will be the same for both ends of the - * conversation. If the id buffer is too small then olm_session_last_error() - * will be "OUTPUT_BUFFER_TOO_SMALL". */ -size_t olm_session_id( - OlmSession * session, - void * id, size_t id_length -); - -int olm_session_has_received_message( - OlmSession *session -); - -/** Checks if the PRE_KEY message is for this in-bound session. This can happen - * if multiple messages are sent to this account before this account sends a - * message in reply. Returns 1 if the session matches. Returns 0 if the session - * does not match. Returns olm_error() on failure. If the base64 - * couldn't be decoded then olm_session_last_error will be "INVALID_BASE64". - * If the message was for an unsupported protocol version then - * olm_session_last_error() will be "BAD_MESSAGE_VERSION". If the message - * couldn't be decoded then then olm_session_last_error() will be - * "BAD_MESSAGE_FORMAT". */ -size_t olm_matches_inbound_session( - OlmSession * session, - void * one_time_key_message, size_t message_length -); - -/** Checks if the PRE_KEY message is for this in-bound session. This can happen - * if multiple messages are sent to this account before this account sends a - * message in reply. Returns 1 if the session matches. Returns 0 if the session - * does not match. Returns olm_error() on failure. If the base64 - * couldn't be decoded then olm_session_last_error will be "INVALID_BASE64". - * If the message was for an unsupported protocol version then - * olm_session_last_error() will be "BAD_MESSAGE_VERSION". If the message - * couldn't be decoded then then olm_session_last_error() will be - * "BAD_MESSAGE_FORMAT". */ -size_t olm_matches_inbound_session_from( - OlmSession * session, - void const * their_identity_key, size_t their_identity_key_length, - void * one_time_key_message, size_t message_length -); - -/** Removes the one time keys that the session used from the account. Returns - * olm_error() on failure. If the account doesn't have any matching one time - * keys then olm_account_last_error() will be "BAD_MESSAGE_KEY_ID". */ -size_t olm_remove_one_time_keys( - OlmAccount * account, - OlmSession * session -); - -/** The type of the next message that olm_encrypt() will return. Returns - * OLM_MESSAGE_TYPE_PRE_KEY if the message will be a PRE_KEY message. - * Returns OLM_MESSAGE_TYPE_MESSAGE if the message will be a normal message. - * Returns olm_error on failure. */ -size_t olm_encrypt_message_type( - OlmSession * session -); - -/** The number of random bytes needed to encrypt the next message. */ -size_t olm_encrypt_random_length( - OlmSession * session -); - -/** The size of the next message in bytes for the given number of plain-text - * bytes. */ -size_t olm_encrypt_message_length( - OlmSession * session, - size_t plaintext_length -); - -/** Encrypts a message using the session. Returns the length of the message in - * bytes on success. Writes the message as base64 into the message buffer. - * Returns olm_error() on failure. If the message buffer is too small then - * olm_session_last_error() will be "OUTPUT_BUFFER_TOO_SMALL". If there - * weren't enough random bytes then olm_session_last_error() will be - * "NOT_ENOUGH_RANDOM". */ -size_t olm_encrypt( - OlmSession * session, - void const * plaintext, size_t plaintext_length, - void * random, size_t random_length, - void * message, size_t message_length -); - -/** The maximum number of bytes of plain-text a given message could decode to. - * The actual size could be different due to padding. The input message buffer - * is destroyed. Returns olm_error() on failure. If the message base64 - * couldn't be decoded then olm_session_last_error() will be - * "INVALID_BASE64". If the message is for an unsupported version of the - * protocol then olm_session_last_error() will be "BAD_MESSAGE_VERSION". - * If the message couldn't be decoded then olm_session_last_error() will be - * "BAD_MESSAGE_FORMAT". */ -size_t olm_decrypt_max_plaintext_length( - OlmSession * session, - size_t message_type, - void * message, size_t message_length -); - -/** Decrypts a message using the session. The input message buffer is destroyed. - * Returns the length of the plain-text on success. Returns olm_error() on - * failure. If the plain-text buffer is smaller than - * olm_decrypt_max_plaintext_length() then olm_session_last_error() - * will be "OUTPUT_BUFFER_TOO_SMALL". If the base64 couldn't be decoded then - * olm_session_last_error() will be "INVALID_BASE64". If the message is for - * an unsupported version of the protocol then olm_session_last_error() will - * be "BAD_MESSAGE_VERSION". If the message couldn't be decoded then - * olm_session_last_error() will be BAD_MESSAGE_FORMAT". - * If the MAC on the message was invalid then olm_session_last_error() will - * be "BAD_MESSAGE_MAC". */ -size_t olm_decrypt( - OlmSession * session, - size_t message_type, - void * message, size_t message_length, - void * plaintext, size_t max_plaintext_length -); - -/** The length of the buffer needed to hold the SHA-256 hash. */ -size_t olm_sha256_length( - OlmUtility * utility -); - -/** Calculates the SHA-256 hash of the input and encodes it as base64. If the - * output buffer is smaller than olm_sha256_length() then - * olm_session_last_error() will be "OUTPUT_BUFFER_TOO_SMALL". */ -size_t olm_sha256( - OlmUtility * utility, - void const * input, size_t input_length, - void * output, size_t output_length -); - -/** Verify an ed25519 signature. If the key was too small then - * olm_session_last_error will be "INVALID_BASE64". If the signature was invalid - * then olm_session_last_error() will be "BAD_MESSAGE_MAC". */ -size_t olm_ed25519_verify( - OlmUtility * utility, - void const * key, size_t key_length, - void const * message, size_t message_length, - void * signature, size_t signature_length -); - -typedef struct OlmOutboundGroupSession OlmOutboundGroupSession; - -/** get the size of an outbound group session, in bytes. */ -size_t olm_outbound_group_session_size(); - -/** - * Initialise an outbound group session object using the supplied memory - * The supplied memory should be at least olm_outbound_group_session_size() - * bytes. - */ -OlmOutboundGroupSession * olm_outbound_group_session( - void *memory -); - -/** - * A null terminated string describing the most recent error to happen to a - * group session */ -const char *olm_outbound_group_session_last_error( - const OlmOutboundGroupSession *session -); - -/** Clears the memory used to back this group session */ -size_t olm_clear_outbound_group_session( - OlmOutboundGroupSession *session -); - -/** Returns the number of bytes needed to store an outbound group session */ -size_t olm_pickle_outbound_group_session_length( - const OlmOutboundGroupSession *session -); - -/** - * Stores a group session as a base64 string. Encrypts the session using the - * supplied key. Returns the length of the session on success. - * - * Returns olm_error() on failure. If the pickle output buffer - * is smaller than olm_pickle_outbound_group_session_length() then - * olm_outbound_group_session_last_error() will be "OUTPUT_BUFFER_TOO_SMALL" - */ -size_t olm_pickle_outbound_group_session( - OlmOutboundGroupSession *session, - void const * key, size_t key_length, - void * pickled, size_t pickled_length -); - -/** - * Loads a group session from a pickled base64 string. Decrypts the session - * using the supplied key. - * - * Returns olm_error() on failure. If the key doesn't match the one used to - * encrypt the account then olm_outbound_group_session_last_error() will be - * "BAD_ACCOUNT_KEY". If the base64 couldn't be decoded then - * olm_outbound_group_session_last_error() will be "INVALID_BASE64". The input - * pickled buffer is destroyed - */ -size_t olm_unpickle_outbound_group_session( - OlmOutboundGroupSession *session, - void const * key, size_t key_length, - void * pickled, size_t pickled_length -); - - -/** The number of random bytes needed to create an outbound group session */ -size_t olm_init_outbound_group_session_random_length( - const OlmOutboundGroupSession *session -); - -/** - * Start a new outbound group session. Returns olm_error() on failure. On - * failure last_error will be set with an error code. The last_error will be - * NOT_ENOUGH_RANDOM if the number of random bytes was too small. - */ -size_t olm_init_outbound_group_session( - OlmOutboundGroupSession *session, - uint8_t *random, size_t random_length -); - -/** - * The number of bytes that will be created by encrypting a message - */ -size_t olm_group_encrypt_message_length( - OlmOutboundGroupSession *session, - size_t plaintext_length -); - -/** - * Encrypt some plain-text. Returns the length of the encrypted message or - * olm_error() on failure. On failure last_error will be set with an - * error code. The last_error will be OUTPUT_BUFFER_TOO_SMALL if the output - * buffer is too small. - */ -size_t olm_group_encrypt( - OlmOutboundGroupSession *session, - uint8_t const * plaintext, size_t plaintext_length, - uint8_t * message, size_t message_length -); - - -/** - * Get the number of bytes returned by olm_outbound_group_session_id() - */ -size_t olm_outbound_group_session_id_length( - const OlmOutboundGroupSession *session -); - -/** - * Get a base64-encoded identifier for this session. - * - * Returns the length of the session id on success or olm_error() on - * failure. On failure last_error will be set with an error code. The - * last_error will be OUTPUT_BUFFER_TOO_SMALL if the id buffer was too - * small. - */ -size_t olm_outbound_group_session_id( - OlmOutboundGroupSession *session, - uint8_t * id, size_t id_length -); - -/** - * Get the current message index for this session. - * - * Each message is sent with an increasing index; this returns the index for - * the next message. - */ -uint32_t olm_outbound_group_session_message_index( - OlmOutboundGroupSession *session -); - -/** - * Get the number of bytes returned by olm_outbound_group_session_key() - */ -size_t olm_outbound_group_session_key_length( - const OlmOutboundGroupSession *session -); - -/** - * Get the base64-encoded current ratchet key for this session. - * - * Each message is sent with a different ratchet key. This function returns the - * ratchet key that will be used for the next message. - * - * Returns the length of the ratchet key on success or olm_error() on - * failure. On failure last_error will be set with an error code. The - * last_error will be OUTPUT_BUFFER_TOO_SMALL if the buffer was too small. - */ -size_t olm_outbound_group_session_key( - OlmOutboundGroupSession *session, - uint8_t * key, size_t key_length -); - -typedef struct OlmInboundGroupSession OlmInboundGroupSession; - -/** get the size of an inbound group session, in bytes. */ -size_t olm_inbound_group_session_size(); - -/** - * Initialise an inbound group session object using the supplied memory - * The supplied memory should be at least olm_inbound_group_session_size() - * bytes. - */ -OlmInboundGroupSession * olm_inbound_group_session( - void *memory -); - -/** - * A null terminated string describing the most recent error to happen to a - * group session */ -const char *olm_inbound_group_session_last_error( - const OlmInboundGroupSession *session -); - -/** Clears the memory used to back this group session */ -size_t olm_clear_inbound_group_session( - OlmInboundGroupSession *session -); - -/** Returns the number of bytes needed to store an inbound group session */ -size_t olm_pickle_inbound_group_session_length( - const OlmInboundGroupSession *session -); - -/** - * Stores a group session as a base64 string. Encrypts the session using the - * supplied key. Returns the length of the session on success. - * - * Returns olm_error() on failure. If the pickle output buffer - * is smaller than olm_pickle_inbound_group_session_length() then - * olm_inbound_group_session_last_error() will be "OUTPUT_BUFFER_TOO_SMALL" - */ -size_t olm_pickle_inbound_group_session( - OlmInboundGroupSession *session, - void const * key, size_t key_length, - void * pickled, size_t pickled_length -); - -/** - * Loads a group session from a pickled base64 string. Decrypts the session - * using the supplied key. - * - * Returns olm_error() on failure. If the key doesn't match the one used to - * encrypt the account then olm_inbound_group_session_last_error() will be - * "BAD_ACCOUNT_KEY". If the base64 couldn't be decoded then - * olm_inbound_group_session_last_error() will be "INVALID_BASE64". The input - * pickled buffer is destroyed - */ -size_t olm_unpickle_inbound_group_session( - OlmInboundGroupSession *session, - void const * key, size_t key_length, - void * pickled, size_t pickled_length -); - - -/** - * Start a new inbound group session, from a key exported from - * olm_outbound_group_session_key - * - * Returns olm_error() on failure. On failure last_error will be set with an - * error code. The last_error will be: - * - * * OLM_INVALID_BASE64 if the session_key is not valid base64 - * * OLM_BAD_SESSION_KEY if the session_key is invalid - */ -size_t olm_init_inbound_group_session( - OlmInboundGroupSession *session, - /* base64-encoded keys */ - uint8_t const * session_key, size_t session_key_length -); - -/** - * Import an inbound group session, from a previous export. - * - * Returns olm_error() on failure. On failure last_error will be set with an - * error code. The last_error will be: - * - * * OLM_INVALID_BASE64 if the session_key is not valid base64 - * * OLM_BAD_SESSION_KEY if the session_key is invalid - */ -size_t olm_import_inbound_group_session( - OlmInboundGroupSession *session, - /* base64-encoded keys; note that it will be overwritten with the base64-decoded - data. */ - uint8_t const * session_key, size_t session_key_length -); - - -/** - * Get an upper bound on the number of bytes of plain-text the decrypt method - * will write for a given input message length. The actual size could be - * different due to padding. - * - * The input message buffer is destroyed. - * - * Returns olm_error() on failure. - */ -size_t olm_group_decrypt_max_plaintext_length( - OlmInboundGroupSession *session, - uint8_t * message, size_t message_length -); - -/** - * Decrypt a message. - * - * The input message buffer is destroyed. - * - * Returns the length of the decrypted plain-text, or olm_error() on failure. - * - * On failure last_error will be set with an error code. The last_error will - * be: - * * OLM_OUTPUT_BUFFER_TOO_SMALL if the plain-text buffer is too small - * * OLM_INVALID_BASE64 if the message is not valid base-64 - * * 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 (ie, it was sent before the session key was shared with - * us) - */ -size_t olm_group_decrypt( - OlmInboundGroupSession *session, - - /* input; note that it will be overwritten with the base64-decoded - message. */ - uint8_t * message, size_t message_length, - - /* output */ - uint8_t * plaintext, size_t max_plaintext_length, - uint32_t * message_index -); - - -/** - * Get the number of bytes returned by olm_inbound_group_session_id() - */ -size_t olm_inbound_group_session_id_length( - const OlmInboundGroupSession *session -); - -/** - * Get a base64-encoded identifier for this session. - * - * Returns the length of the session id on success or olm_error() on - * failure. On failure last_error will be set with an error code. The - * last_error will be OUTPUT_BUFFER_TOO_SMALL if the id buffer was too - * small. - */ -size_t olm_inbound_group_session_id( - OlmInboundGroupSession *session, - uint8_t * id, size_t id_length -); - -/** - * Get the first message index we know how to decrypt. - */ -uint32_t olm_inbound_group_session_first_known_index( - const OlmInboundGroupSession *session -); - - -/** - * Check if the session has been verified as a valid session. - * - * (A session is verified either because the original session share was signed, - * or because we have subsequently successfully decrypted a message.) - * - * This is mainly intended for the unit tests, currently. - */ -int olm_inbound_group_session_is_verified( - const OlmInboundGroupSession *session -); - -/** - * Get the number of bytes returned by olm_export_inbound_group_session() - */ -size_t olm_export_inbound_group_session_length( - const OlmInboundGroupSession *session -); - -/** - * Export the base64-encoded ratchet key for this session, at the given index, - * in a format which can be used by olm_import_inbound_group_session - * - * Returns the length of the ratchet key on success or olm_error() on - * failure. On failure last_error will be set with an error code. The - * last_error will be: - * * OUTPUT_BUFFER_TOO_SMALL if the buffer was too small - * * 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) - */ -size_t olm_export_inbound_group_session( - OlmInboundGroupSession *session, - uint8_t * key, size_t key_length, uint32_t message_index -); -- cgit v1.2.3-70-g09d2 From 718763f8fc8c0650fd880b32fe9bbf931d7e2183 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Fri, 12 Oct 2018 19:06:46 -0400 Subject: build and test improvements - build both Python2 and Python3 libs by default, and add separate rules building Python2 and Python. - use the libraries as built by setuptools, rather than building again separately --- python/Makefile | 60 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 30 insertions(+), 30 deletions(-) (limited to 'python/Makefile') diff --git a/python/Makefile b/python/Makefile index ad2621c..a19c4f8 100644 --- a/python/Makefile +++ b/python/Makefile @@ -1,41 +1,41 @@ -PYTHON ?= python - -all: olm +all: olm-python2 olm-python3 include/olm/olm.h: ../include/olm/olm.h ../include/olm/inbound_group_session.h ../include/olm/outbound_group_session.h mkdir -p include/olm $(CPP) -I dummy -I ../include ../include/olm/olm.h -o include/olm/olm.h -olm: include/olm/olm.h - DEVELOP=$(DEVELOP) $(PYTHON) setup.py build - -install: olm - $(PYTHON) setup.py install --skip-build -O1 --root=$(DESTDIR) +olm-python2: include/olm/olm.h + DEVELOP=$(DEVELOP) python2 setup.py build -test: develop py2develop - python3 -m pytest - python2 -m pytest - python3 -m pytest --flake8 --benchmark-disable - python3 -m pytest --isort --benchmark-disable - python3 -m pytest --cov --cov-branch --benchmark-disable +olm-python3: include/olm/olm.h + DEVELOP=$(DEVELOP) python3 setup.py build -clean: - -rm -r python_olm.egg-info/ dist/ __pycache__/ - -rm *.so _libolm.o - -rm -r packages/ - -rm -r build/ +install: install-python2 install-python3 -develop: _libolm.o -py2develop: _libolm.so +install-python2: olm-python2 + python2 setup.py install --skip-build -O1 --root=$(DESTDIR) -_libolm.so: DEVELOP ?= 1 -_libolm.so: include/olm/olm.h olm_build.py - DEVELOP=$(DEVELOP) python2 olm_build.py - -rm _libolm.c +install-python3: olm-python3 + python3 setup.py install --skip-build -O1 --root=$(DESTDIR) -_libolm.o: DEVELOP ?= 1 -_libolm.o: include/olm/olm.h olm_build.py - DEVELOP=$(DEVELOP) python3 olm_build.py - -rm _libolm.c +test: olm-python2 olm-python3 + rm -rf install-temp + mkdir -p install-temp/2 install-temp/3 + PYTHONPATH=install-temp/2 python2 setup.py install --skip-build --install-lib install-temp/2 --install-script install-temp/bin + PYTHONPATH=install-temp/3 python3 setup.py install --skip-build --install-lib install-temp/3 --install-script install-temp/bin + PYTHONPATH=install-temp/3 python3 -m pytest + PYTHONPATH=install-temp/2 python2 -m pytest + PYTHONPATH=install-temp/3 python3 -m pytest --flake8 --benchmark-disable + PYTHONPATH=install-temp/3 python3 -m pytest --isort --benchmark-disable + PYTHONPATH=install-temp/3 python3 -m pytest --cov --cov-branch --benchmark-disable + rm -rf install-temp -.PHONY: all olm install clean test develop +clean: + rm -rf python_olm.egg-info/ dist/ __pycache__/ + rm -rf *.so _libolm.o + rm -rf packages/ + rm -rf build/ + rm -rf install-temp/ + rm -rf include/ + +.PHONY: all olm-python2 olm-python3 install install-python2 install-python3 clean test -- cgit v1.2.3-70-g09d2 From 5ef6a844d6fd3d58d1eb85dcd188ac6b6baa3fbe Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Tue, 16 Oct 2018 00:31:56 -0400 Subject: overwrite buffers that may contain sensitive data also reduce the amount of memory copying that we do --- python/Makefile | 2 + python/olm/_compat.py | 10 +++ python/olm/account.py | 62 +++++++++++------ python/olm/group_session.py | 165 +++++++++++++++++++++++++++----------------- python/olm/session.py | 106 +++++++++++++++++----------- python/olm/utility.py | 19 +++-- 6 files changed, 236 insertions(+), 128 deletions(-) (limited to 'python/Makefile') diff --git a/python/Makefile b/python/Makefile index a19c4f8..5da703a 100644 --- a/python/Makefile +++ b/python/Makefile @@ -3,6 +3,8 @@ all: olm-python2 olm-python3 include/olm/olm.h: ../include/olm/olm.h ../include/olm/inbound_group_session.h ../include/olm/outbound_group_session.h mkdir -p include/olm $(CPP) -I dummy -I ../include ../include/olm/olm.h -o include/olm/olm.h +# add memset to the header so that we can use it to clear buffers + echo 'void *memset(void *s, int c, size_t n);' >> include/olm/olm.h olm-python2: include/olm/olm.h DEVELOP=$(DEVELOP) python2 setup.py build diff --git a/python/olm/_compat.py b/python/olm/_compat.py index 8f1670d..91e4d1b 100644 --- a/python/olm/_compat.py +++ b/python/olm/_compat.py @@ -26,6 +26,16 @@ except ImportError: # pragma: no cover URANDOM = urandom # type: ignore +def to_bytearray(string): + # type: (AnyStr) -> bytes + if isinstance(string, bytes): + return bytearray(string) + elif isinstance(string, str): + return bytearray(string, "utf-8") + + raise TypeError("Invalid type {}".format(type(string))) + + def to_bytes(string): # type: (AnyStr) -> bytes if isinstance(string, bytes): diff --git a/python/olm/account.py b/python/olm/account.py index 1dba96d..8455655 100644 --- a/python/olm/account.py +++ b/python/olm/account.py @@ -37,7 +37,7 @@ from future.utils import bytes_to_native_str # pylint: disable=no-name-in-module from _libolm import ffi, lib # type: ignore -from ._compat import URANDOM, to_bytes +from ._compat import URANDOM, to_bytearray from ._finalize import track_for_finalization # This is imported only for type checking purposes @@ -82,12 +82,12 @@ class Account(object): random_length = lib.olm_create_account_random_length(self._account) random = URANDOM(random_length) - random_buffer = ffi.new("char[]", random) self._check_error( - lib.olm_create_account(self._account, random_buffer, + lib.olm_create_account(self._account, ffi.from_buffer(random), random_length)) + def _check_error(self, ret): # type: (int) -> None if ret != lib.olm_error(): @@ -111,15 +111,23 @@ class Account(object): passphrase(str, optional): The passphrase to be used to encrypt the account. """ - byte_key = bytes(passphrase, "utf-8") if passphrase else b"" - key_buffer = ffi.new("char[]", byte_key) + 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) - self._check_error( - lib.olm_pickle_account(self._account, key_buffer, len(byte_key), - pickle_buffer, 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) @classmethod @@ -143,15 +151,22 @@ class Account(object): 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) + byte_key = bytearray(passphrase, "utf-8") if passphrase else b"" + # copy because unpickle will destroy the buffer 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) + try: + ret = lib.olm_unpickle_account(obj._account, + ffi.from_buffer(byte_key), + len(byte_key), + pickle_buffer, + len(pickle)) + obj._check_error(ret) + finally: + for i in range(0, len(byte_key)): + byte_key[i] = 0 return obj @@ -178,14 +193,21 @@ class Account(object): Args: message(str): The message to sign. """ - bytes_message = to_bytes(message) + bytes_message = to_bytearray(message) out_length = lib.olm_account_signature_length(self._account) - message_buffer = ffi.new("char[]", bytes_message) out_buffer = ffi.new("char[]", out_length) - self._check_error( - lib.olm_account_sign(self._account, message_buffer, - len(bytes_message), out_buffer, 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)) @@ -214,10 +236,10 @@ class Account(object): random_length = lib.olm_account_generate_one_time_keys_random_length( self._account, count) random = URANDOM(random_length) - random_buffer = ffi.new("char[]", random) + self._check_error( lib.olm_account_generate_one_time_keys( - self._account, count, random_buffer, random_length)) + self._account, count, ffi.from_buffer(random), random_length)) @property def one_time_keys(self): diff --git a/python/olm/group_session.py b/python/olm/group_session.py index bbb5e56..814ce27 100644 --- a/python/olm/group_session.py +++ b/python/olm/group_session.py @@ -33,7 +33,7 @@ from future.utils import bytes_to_native_str # pylint: disable=no-name-in-module from _libolm import ffi, lib # type: ignore -from ._compat import URANDOM, to_bytes +from ._compat import URANDOM, to_bytearray, to_bytes from ._finalize import track_for_finalization @@ -78,12 +78,17 @@ class InboundGroupSession(object): if False: # pragma: no cover self._session = self._session # type: ffi.cdata - byte_session_key = to_bytes(session_key) - - key_buffer = ffi.new("char[]", byte_session_key) - ret = lib.olm_init_inbound_group_session( - self._session, key_buffer, len(byte_session_key) - ) + byte_session_key = to_bytearray(session_key) + + try: + ret = lib.olm_init_inbound_group_session( + self._session, + ffi.from_buffer(byte_session_key), len(byte_session_key) + ) + finally: + if byte_session_key is not session_key: + for i in range(0, len(byte_session_key)): + byte_session_key[i] = 0 self._check_error(ret) def pickle(self, passphrase=""): @@ -98,19 +103,23 @@ class InboundGroupSession(object): passphrase(str, optional): The passphrase to be used to encrypt the session. """ - byte_passphrase = bytes(passphrase, "utf-8") if passphrase else b"" + byte_passphrase = bytearray(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) + try: + ret = lib.olm_pickle_inbound_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) @@ -135,20 +144,25 @@ class InboundGroupSession(object): 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) + byte_passphrase = bytearray(passphrase, "utf-8") if passphrase else b"" + # copy because unpickle will destroy the buffer pickle_buffer = ffi.new("char[]", pickle) obj = cls.__new__(cls) - ret = lib.olm_unpickle_inbound_group_session( - obj._session, - passphrase_buffer, - len(byte_passphrase), - pickle_buffer, - len(pickle) - ) - obj._check_error(ret) + try: + ret = lib.olm_unpickle_inbound_group_session( + obj._session, + ffi.from_buffer(byte_passphrase), + len(byte_passphrase), + pickle_buffer, + len(pickle) + ) + obj._check_error(ret) + finally: + # clear out copies of the passphrase + for i in range(0, len(byte_passphrase)): + byte_passphrase[i] = 0 return obj @@ -189,12 +203,15 @@ class InboundGroupSession(object): 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*") @@ -206,10 +223,15 @@ class InboundGroupSession(object): self._check_error(plaintext_length) - return bytes_to_native_str(ffi.unpack( + plaintext = bytes_to_native_str(ffi.unpack( plaintext_buffer, plaintext_length - )), message_index[0] + )) + + # clear out copies of the plaintext + lib.memset(plaintext_buffer, 0, max_plaintext_length) + + return plaintext, message_index[0] @property def id(self): @@ -281,15 +303,19 @@ class InboundGroupSession(object): """ obj = cls.__new__(cls) - byte_session_key = to_bytes(session_key) + byte_session_key = to_bytearray(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) + try: + ret = lib.olm_import_inbound_group_session( + obj._session, + ffi.from_buffer(byte_session_key), + len(byte_session_key) + ) + obj._check_error(ret) + finally: + if byte_session_key is not session_key: + for i in range(0, len(byte_session_key)): + byte_session_key[i] = 0 return obj @@ -323,10 +349,9 @@ class OutboundGroupSession(object): self._session ) random = URANDOM(random_length) - random_buffer = ffi.new("char[]", random) ret = lib.olm_init_outbound_group_session( - self._session, random_buffer, random_length + self._session, ffi.from_buffer(random), random_length ) self._check_error(ret) @@ -353,17 +378,23 @@ class OutboundGroupSession(object): 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) + 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) - ret = lib.olm_pickle_outbound_group_session( - self._session, passphrase_buffer, len(byte_passphrase), - pickle_buffer, pickle_length - ) - self._check_error(ret) + 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) @classmethod @@ -387,20 +418,25 @@ class OutboundGroupSession(object): 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) + byte_passphrase = bytearray(passphrase, "utf-8") if passphrase else b"" + # copy because unpickle will destroy the buffer 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) + try: + ret = lib.olm_unpickle_outbound_group_session( + obj._session, + ffi.from_buffer(byte_passphrase), + len(byte_passphrase), + pickle_buffer, + len(pickle) + ) + obj._check_error(ret) + finally: + # clear out copies of the passphrase + for i in range(0, len(byte_passphrase)): + byte_passphrase[i] = 0 return obj @@ -414,21 +450,26 @@ class OutboundGroupSession(object): plaintext(str): A string that will be encrypted using the group session. """ - byte_plaintext = to_bytes(plaintext) + 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) - plaintext_buffer = ffi.new("char[]", byte_plaintext) + 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 - 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)) @property diff --git a/python/olm/session.py b/python/olm/session.py index b123e8a..cba9be0 100644 --- a/python/olm/session.py +++ b/python/olm/session.py @@ -40,7 +40,7 @@ from future.utils import bytes_to_native_str # pylint: disable=no-name-in-module from _libolm import ffi, lib # type: ignore -from ._compat import URANDOM, to_bytes +from ._compat import URANDOM, to_bytearray, to_bytes from ._finalize import track_for_finalization # This is imported only for type checking purposes @@ -164,15 +164,22 @@ class Session(object): passphrase(str, optional): The passphrase to be used to encrypt the session. """ - byte_key = bytes(passphrase, "utf-8") if passphrase else b"" - key_buffer = ffi.new("char[]", byte_key) + byte_key = bytearray(passphrase, "utf-8") if passphrase else b"" pickle_length = lib.olm_pickle_session_length(self._session) pickle_buffer = ffi.new("char[]", pickle_length) - self._check_error( - lib.olm_pickle_session(self._session, key_buffer, len(byte_key), - pickle_buffer, pickle_length)) + try: + self._check_error( + lib.olm_pickle_session(self._session, + ffi.from_buffer(byte_key), + len(byte_key), + pickle_buffer, pickle_length)) + finally: + # clear out copies of the passphrase + for i in range(0, len(byte_key)): + byte_key[i] = 0 + return ffi.unpack(pickle_buffer, pickle_length) @classmethod @@ -196,16 +203,23 @@ class Session(object): 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) + byte_key = bytearray(passphrase, "utf-8") if passphrase else b"" + # copy because unpickle will destroy the buffer pickle_buffer = ffi.new("char[]", pickle) session = cls.__new__(cls) - ret = lib.olm_unpickle_session(session._session, key_buffer, - len(byte_key), pickle_buffer, - len(pickle)) - session._check_error(ret) + try: + ret = lib.olm_unpickle_session(session._session, + ffi.from_buffer(byte_key), + len(byte_key), + pickle_buffer, + len(pickle)) + session._check_error(ret) + finally: + # clear out copies of the passphrase + for i in range(0, len(byte_key)): + byte_key[i] = 0 return session @@ -217,29 +231,32 @@ class Session(object): Args: plaintext(str): The plaintext message that will be encrypted. """ - byte_plaintext = to_bytes(plaintext) + byte_plaintext = to_bytearray(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) + try: + 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) + self._check_error(message_type) - plaintext_buffer = ffi.new("char[]", byte_plaintext) + ciphertext_length = lib.olm_encrypt_message_length( + self._session, len(byte_plaintext) + ) + ciphertext_buffer = ffi.new("char[]", ciphertext_length) - self._check_error(lib.olm_encrypt( - self._session, - plaintext_buffer, len(byte_plaintext), - random_buffer, r_length, - ciphertext_buffer, ciphertext_length, - )) + self._check_error(lib.olm_encrypt( + self._session, + ffi.from_buffer(byte_plaintext), len(byte_plaintext), + ffi.from_buffer(random), r_length, + ciphertext_buffer, ciphertext_length, + )) + finally: + # clear out copies of plaintext + if byte_plaintext is not plaintext: + for i in range(0, len(byte_plaintext)): + byte_plaintext[i] = 0 if message_type == lib.OLM_MESSAGE_TYPE_PRE_KEY: return OlmPreKeyMessage( @@ -274,22 +291,34 @@ class Session(object): 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._session, message.message_type, + ciphertext_buffer, len(byte_ciphertext), + plaintext_buffer, max_plaintext_length ) self._check_error(plaintext_length) - return bytes_to_native_str( + 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 + @property def id(self): # type: () -> str @@ -331,16 +360,16 @@ class Session(object): ret = None byte_ciphertext = to_bytes(message.ciphertext) - + # make a copy, because olm_matches_inbound_session(_from) will distroy + # it 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), + ffi.from_buffer(byte_id_key), len(byte_id_key), message_buffer, len(byte_ciphertext) ) @@ -447,14 +476,11 @@ class OutboundSession(Session): 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 + ffi.from_buffer(byte_id_key), len(byte_id_key), + ffi.from_buffer(byte_one_time), len(byte_one_time), + ffi.from_buffer(random), session_random_length )) diff --git a/python/olm/utility.py b/python/olm/utility.py index 1c5c41d..0a64128 100644 --- a/python/olm/utility.py +++ b/python/olm/utility.py @@ -36,7 +36,7 @@ from typing import AnyStr, Type # pylint: disable=no-name-in-module from _libolm import ffi, lib # type: ignore -from ._compat import to_bytes +from ._compat import to_bytearray, to_bytes from ._finalize import track_for_finalization @@ -80,13 +80,20 @@ class _Utility(object): cls._allocate() byte_key = to_bytes(key) - byte_message = to_bytes(message) + byte_message = to_bytearray(message) byte_signature = to_bytes(signature) - cls._check_error( - lib.olm_ed25519_verify(cls._utility, byte_key, len(byte_key), - byte_message, len(byte_message), - byte_signature, len(byte_signature))) + try: + cls._check_error( + lib.olm_ed25519_verify(cls._utility, byte_key, len(byte_key), + ffi.from_buffer(byte_message), + len(byte_message), + byte_signature, len(byte_signature))) + finally: + # clear out copies of the message, which may be a plaintext + if byte_message is not message: + for i in range(0, len(byte_message)): + byte_message[i] = 0 def ed25519_verify(key, message, signature): -- cgit v1.2.3-70-g09d2