diff options
-rw-r--r-- | CHANGELOG.rst | 6 | ||||
-rw-r--r-- | CMakeLists.txt | 6 | ||||
-rw-r--r-- | OLMKit.podspec | 2 | ||||
-rw-r--r-- | android/olm-sdk/build.gradle | 6 | ||||
-rw-r--r-- | common.mk | 2 | ||||
-rw-r--r-- | javascript/package.json | 2 | ||||
-rw-r--r-- | python/Makefile | 3 | ||||
-rw-r--r-- | python/olm/__version__.py | 2 | ||||
-rw-r--r-- | python/olm/_compat.py | 21 | ||||
-rw-r--r-- | python/olm/group_session.py | 21 | ||||
-rw-r--r-- | python/olm/pk.py | 15 | ||||
-rw-r--r-- | python/olm/sas.py | 4 | ||||
-rw-r--r-- | python/olm/session.py | 21 | ||||
-rw-r--r-- | python/olm/utility.py | 1 | ||||
-rw-r--r-- | python/tests/group_session_test.py | 14 | ||||
-rw-r--r-- | python/tests/pk_test.py | 8 | ||||
-rw-r--r-- | python/tests/session_test.py | 9 |
17 files changed, 114 insertions, 29 deletions
diff --git a/CHANGELOG.rst b/CHANGELOG.rst index ffdbd1d..68292d3 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,3 +1,9 @@ +Changes in `3.1.3 <https://gitlab.matrix.org/matrix-org/olm/tags/3.1.3>`_ +========================================================================= + +This release fixes unicode issues in the Python bindings, and adds some +clarifications to the documentation. + Changes in `3.1.2 <https://gitlab.matrix.org/matrix-org/olm/tags/3.1.2>`_ ========================================================================= diff --git a/CMakeLists.txt b/CMakeLists.txt index 0932a00..da5baee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.4) -project(olm VERSION 3.1.2 LANGUAGES CXX C) +project(olm VERSION 3.1.3 LANGUAGES CXX C) option(OLM_TESTS "Build tests" ON) option(BUILD_SHARED_LIBS "Build as a shared library" ON) @@ -50,8 +50,8 @@ add_library(Olm::Olm ALIAS olm) target_include_directories(olm PUBLIC - $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include> - $<INSTALL_INTERFACE:include> + $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> + $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/lib) diff --git a/OLMKit.podspec b/OLMKit.podspec index e16c58f..a0c48e3 100644 --- a/OLMKit.podspec +++ b/OLMKit.podspec @@ -3,7 +3,7 @@ Pod::Spec.new do |s| # The libolm version MAJOR = 3 MINOR = 1 - PATCH = 2 + PATCH = 3 s.name = "OLMKit" s.version = "#{MAJOR}.#{MINOR}.#{PATCH}" diff --git a/android/olm-sdk/build.gradle b/android/olm-sdk/build.gradle index 660ecbc..354f93b 100644 --- a/android/olm-sdk/build.gradle +++ b/android/olm-sdk/build.gradle @@ -8,9 +8,9 @@ android { defaultConfig { minSdkVersion 11 targetSdkVersion 28 - versionCode 312 - versionName "3.1.2" - version "3.1.2" + versionCode 313 + versionName "3.1.3" + version "3.1.3" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { @@ -1,4 +1,4 @@ MAJOR := 3 MINOR := 1 -PATCH := 2 +PATCH := 3 diff --git a/javascript/package.json b/javascript/package.json index f5ddf1e..bfd2053 100644 --- a/javascript/package.json +++ b/javascript/package.json @@ -1,6 +1,6 @@ { "name": "olm", - "version": "3.1.2", + "version": "3.1.3", "description": "An implementation of the Double Ratchet cryptographic ratchet", "main": "olm.js", "files": [ diff --git a/python/Makefile b/python/Makefile index e4d0611..16f9823 100644 --- a/python/Makefile +++ b/python/Makefile @@ -43,6 +43,9 @@ test: olm-python2 olm-python3 PYTHONPATH=install-temp/3 python3 -m pytest --cov --cov-branch --benchmark-disable rm -rf install-temp +isort: + isort -y -p olm + clean: rm -rf python_olm.egg-info/ dist/ __pycache__/ rm -rf *.so _libolm.o diff --git a/python/olm/__version__.py b/python/olm/__version__.py index 3ce140b..b70cb89 100644 --- a/python/olm/__version__.py +++ b/python/olm/__version__.py @@ -2,7 +2,7 @@ __title__ = "python-olm" __description__ = ("python CFFI bindings for the olm " "cryptographic ratchet library") __url__ = "https://github.com/poljar/python-olm" -__version__ = "3.1.2" +__version__ = "3.1.3" __author__ = "Damir Jelić" __author_email__ = "poljar@termina.org.uk" __license__ = "Apache 2.0" diff --git a/python/olm/_compat.py b/python/olm/_compat.py index 91e4d1b..2ceaa33 100644 --- a/python/olm/_compat.py +++ b/python/olm/_compat.py @@ -44,3 +44,24 @@ def to_bytes(string): return bytes(string, "utf-8") raise TypeError("Invalid type {}".format(type(string))) + + +def to_unicode_str(byte_string, errors="replace"): + """Turn a byte string into a unicode string. + + Should be used everywhere where the input byte string might not be trusted + and may contain invalid unicode values. + + Args: + byte_string (bytes): The bytestring that will be converted to a native + string. + errors (str, optional): The error handling scheme that should be used + to handle unicode decode errors. Can be one of "strict" (raise an + UnicodeDecodeError exception, "ignore" (remove the offending + characters), "replace" (replace the offending character with + U+FFFD), "xmlcharrefreplace" as well as any other name registered + with codecs.register_error that can handle UnicodeEncodeErrors. + + Returns the decoded native string. + """ + return byte_string.decode(encoding="utf-8", errors=errors) diff --git a/python/olm/group_session.py b/python/olm/group_session.py index 737d9ef..5068192 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_bytearray, to_bytes +from ._compat import URANDOM, to_bytearray, to_bytes, to_unicode_str from ._finalize import track_for_finalization @@ -176,8 +176,8 @@ class InboundGroupSession(object): raise OlmGroupSessionError(last_error) - def decrypt(self, ciphertext): - # type: (AnyStr) -> Tuple[str, int] + def decrypt(self, ciphertext, unicode_errors="replace"): + # type: (AnyStr, str) -> Tuple[str, int] """Decrypt a message Returns a tuple of the decrypted plain-text and the message index of @@ -197,6 +197,13 @@ class InboundGroupSession(object): Args: ciphertext(str): Base64 encoded ciphertext containing the encrypted message + 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 ciphertext: raise ValueError("Ciphertext can't be empty.") @@ -223,10 +230,10 @@ class InboundGroupSession(object): self._check_error(plaintext_length) - plaintext = bytes_to_native_str(ffi.unpack( - plaintext_buffer, - 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) diff --git a/python/olm/pk.py b/python/olm/pk.py index 193aba5..4352359 100644 --- a/python/olm/pk.py +++ b/python/olm/pk.py @@ -40,7 +40,7 @@ from future.utils import bytes_to_native_str from _libolm import ffi, lib # type: ignore -from ._compat import URANDOM, to_bytearray +from ._compat import URANDOM, to_bytearray, to_unicode_str from ._finalize import track_for_finalization @@ -313,8 +313,8 @@ class PkDecryption(object): return obj - def decrypt(self, message): - # type (PkMessage) -> str + def decrypt(self, message, unicode_errors="replace"): + # type (PkMessage, str) -> str """Decrypt a previously encrypted Pk message. Returns the decrypted plaintext. @@ -322,6 +322,13 @@ class PkDecryption(object): Args: message(PkMessage): the pk message to decrypt. + 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. """ ephemeral_key = to_bytearray(message.ephemeral_key) ephemeral_key_size = len(ephemeral_key) @@ -354,7 +361,7 @@ class PkDecryption(object): # clear out copies of the plaintext lib.memset(plaintext_buffer, 0, max_plaintext_length) - return bytes_to_native_str(plaintext) + return to_unicode_str(plaintext, errors=unicode_errors) def _clear_pk_signing(pk_struct): diff --git a/python/olm/sas.py b/python/olm/sas.py index c12b7bc..bea1dd0 100644 --- a/python/olm/sas.py +++ b/python/olm/sas.py @@ -30,15 +30,15 @@ Examples: """ -from functools import wraps from builtins import bytes +from functools import wraps from typing import Optional from future.utils import bytes_to_native_str from _libolm import ffi, lib -from ._compat import URANDOM, to_bytes, to_bytearray +from ._compat import URANDOM, to_bytearray, to_bytes from ._finalize import track_for_finalization diff --git a/python/olm/session.py b/python/olm/session.py index cba9be0..636eb3d 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_bytearray, to_bytes +from ._compat import URANDOM, to_bytearray, to_bytes, to_unicode_str from ._finalize import track_for_finalization # This is imported only for type checking purposes @@ -273,8 +273,8 @@ class Session(object): else: # pragma: no cover raise ValueError("Unknown message type") - def decrypt(self, message): - # type: (_OlmMessage) -> str + 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 @@ -285,7 +285,14 @@ class Session(object): Args: message(OlmMessage): The Olm message that will be decrypted. It can - be either an OlmPreKeyMessage or an OlmMessage. + 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") @@ -311,8 +318,10 @@ class Session(object): plaintext_buffer, max_plaintext_length ) self._check_error(plaintext_length) - plaintext = bytes_to_native_str( - ffi.unpack(plaintext_buffer, 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) diff --git a/python/olm/utility.py b/python/olm/utility.py index 10d5ab4..bddef38 100644 --- a/python/olm/utility.py +++ b/python/olm/utility.py @@ -32,6 +32,7 @@ Examples: # pylint: disable=redefined-builtin,unused-import from typing import AnyStr, Type + from future.utils import bytes_to_native_str # pylint: disable=no-name-in-module diff --git a/python/tests/group_session_test.py b/python/tests/group_session_test.py index c17e84f..4632a60 100644 --- a/python/tests/group_session_test.py +++ b/python/tests/group_session_test.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import pytest from olm import InboundGroupSession, OlmGroupSessionError, OutboundGroupSession @@ -112,3 +113,16 @@ class TestClass(object): outbound = OutboundGroupSession() inbound = InboundGroupSession(outbound.session_key) del inbound + + def test_invalid_unicode_decrypt(self): + outbound = OutboundGroupSession() + inbound = InboundGroupSession(outbound.session_key) + + text = outbound.encrypt(b"\xed") + plaintext, _ = inbound.decrypt(text) + + print(plaintext) + assert plaintext == u"�" + + plaintext, _ = inbound.decrypt(text, "ignore") + assert plaintext == "" diff --git a/python/tests/pk_test.py b/python/tests/pk_test.py index fe3b4b6..ef87465 100644 --- a/python/tests/pk_test.py +++ b/python/tests/pk_test.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import pytest from olm import (PkDecryption, PkDecryptionError, PkEncryption, PkSigning, @@ -55,3 +56,10 @@ class TestClass(object): message = "This statement is true" signature = signing.sign(message) ed25519_verify(signing.public_key, message, signature) + + def test_invalid_unicode_decrypt(self): + decryption = PkDecryption() + encryption = PkEncryption(decryption.public_key) + message = encryption.encrypt(b"\xed") + plaintext = decryption.decrypt(message) + assert plaintext == u"�" diff --git a/python/tests/session_test.py b/python/tests/session_test.py index ab1c38b..b856585 100644 --- a/python/tests/session_test.py +++ b/python/tests/session_test.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import pytest from olm import (Account, InboundSession, OlmMessage, OlmPreKeyMessage, @@ -141,3 +142,11 @@ class TestClass(object): new_message = new_session.encrypt(plaintext) assert bob_session.matches(new_message) is False + + def test_invalid_unicode_decrypt(self): + alice, bob, session = self._create_session() + message = session.encrypt(b"\xed") + + bob_session = InboundSession(bob, message) + plaintext = bob_session.decrypt(message) + assert plaintext == u"�" |