2f5590bf38
This patch changes the decryption functions not to fail if there was an unicode decode error while converting the decrypted bytes plaintext into a native python string. Characters that cannot be decoded as unicode are now replaced with the unicode replacement character (U+FFFD). The old behaviour of raising an UnicodeDecodeError can be achieved by passing the "strict" error handling scheme to the decrypt function.
151 lines
4.4 KiB
Python
151 lines
4.4 KiB
Python
# -*- coding: utf-8 -*-
|
|
# libolm python bindings
|
|
# Copyright © 2015-2017 OpenMarket Ltd
|
|
# Copyright © 2018 Damir Jelić <poljar@termina.org.uk>
|
|
#
|
|
# 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.
|
|
"""libolm Utility module.
|
|
|
|
This module contains utilities for olm.
|
|
It only contains the ed25519_verify function for signature verification.
|
|
|
|
Examples:
|
|
>>> alice = Account()
|
|
|
|
>>> message = "Test"
|
|
>>> signature = alice.sign(message)
|
|
>>> signing_key = alice.identity_keys["ed25519"]
|
|
|
|
>>> ed25519_verify(signing_key, message, signature)
|
|
|
|
"""
|
|
|
|
# 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
|
|
from _libolm import ffi, lib # type: ignore
|
|
|
|
from ._compat import to_bytearray, to_bytes
|
|
from ._finalize import track_for_finalization
|
|
|
|
|
|
def _clear_utility(utility): # pragma: no cover
|
|
# type: (ffi.cdata) -> None
|
|
lib.olm_clear_utility(utility)
|
|
|
|
|
|
class OlmVerifyError(Exception):
|
|
"""libolm signature verification exception."""
|
|
|
|
|
|
class OlmHashError(Exception):
|
|
"""libolm hash calculation exception."""
|
|
|
|
|
|
class _Utility(object):
|
|
# pylint: disable=too-few-public-methods
|
|
"""libolm Utility class."""
|
|
|
|
_buf = None
|
|
_utility = None
|
|
|
|
@classmethod
|
|
def _allocate(cls):
|
|
# type: (Type[_Utility]) -> None
|
|
cls._buf = ffi.new("char[]", lib.olm_utility_size())
|
|
cls._utility = lib.olm_utility(cls._buf)
|
|
track_for_finalization(cls, cls._utility, _clear_utility)
|
|
|
|
@classmethod
|
|
def _check_error(cls, ret, error_class):
|
|
# type: (int, Type) -> None
|
|
if ret != lib.olm_error():
|
|
return
|
|
|
|
raise error_class("{}".format(
|
|
ffi.string(lib.olm_utility_last_error(
|
|
cls._utility)).decode("utf-8")))
|
|
|
|
@classmethod
|
|
def _ed25519_verify(cls, key, message, signature):
|
|
# type: (Type[_Utility], AnyStr, AnyStr, AnyStr) -> None
|
|
if not cls._utility:
|
|
cls._allocate()
|
|
|
|
byte_key = to_bytes(key)
|
|
byte_message = to_bytearray(message)
|
|
byte_signature = to_bytearray(signature)
|
|
|
|
try:
|
|
ret = lib.olm_ed25519_verify(
|
|
cls._utility,
|
|
byte_key,
|
|
len(byte_key),
|
|
ffi.from_buffer(byte_message),
|
|
len(byte_message),
|
|
ffi.from_buffer(byte_signature),
|
|
len(byte_signature)
|
|
)
|
|
|
|
cls._check_error(ret, OlmVerifyError)
|
|
|
|
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
|
|
|
|
@classmethod
|
|
def _sha256(cls, input):
|
|
# type: (Type[_Utility], AnyStr) -> str
|
|
if not cls._utility:
|
|
cls._allocate()
|
|
|
|
byte_input = to_bytes(input)
|
|
hash_length = lib.olm_sha256_length(cls._utility)
|
|
hash = ffi.new("char[]", hash_length)
|
|
|
|
ret = lib.olm_sha256(cls._utility, byte_input, len(byte_input),
|
|
hash, hash_length)
|
|
|
|
cls._check_error(ret, OlmHashError)
|
|
|
|
return bytes_to_native_str(ffi.unpack(hash, hash_length))
|
|
|
|
|
|
def ed25519_verify(key, message, signature):
|
|
# type: (AnyStr, AnyStr, AnyStr) -> None
|
|
"""Verify an ed25519 signature.
|
|
|
|
Raises an OlmVerifyError if verification fails.
|
|
|
|
Args:
|
|
key(str): The ed25519 public key used for signing.
|
|
message(str): The signed message.
|
|
signature(bytes): The message signature.
|
|
"""
|
|
return _Utility._ed25519_verify(key, message, signature)
|
|
|
|
|
|
def sha256(input_string):
|
|
# type: (AnyStr) -> str
|
|
"""Calculate the SHA-256 hash of the input and encodes it as base64.
|
|
|
|
Args:
|
|
input_string(str): The input for which the hash will be calculated.
|
|
|
|
"""
|
|
return _Utility._sha256(input_string)
|