From 446628753b6283f6dcbf27589b2c18f93c71768b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 2 Apr 2019 12:54:00 +0200 Subject: python: Add Short Authentication String bindings. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds bindings to the SAS part of the Olm library contained in the sas.h header file. Signed-off-by: Damir Jelić --- python/olm/__init__.py | 1 + python/olm/sas.py | 223 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 224 insertions(+) create mode 100644 python/olm/sas.py (limited to 'python/olm') diff --git a/python/olm/__init__.py b/python/olm/__init__.py index 015c4f8..fe3e199 100644 --- a/python/olm/__init__.py +++ b/python/olm/__init__.py @@ -36,3 +36,4 @@ from .group_session import ( OutboundGroupSession, OlmGroupSessionError ) +from .sas import Sas, OlmSasError diff --git a/python/olm/sas.py b/python/olm/sas.py new file mode 100644 index 0000000..c7cccb2 --- /dev/null +++ b/python/olm/sas.py @@ -0,0 +1,223 @@ +# -*- coding: utf-8 -*- +# libolm python bindings +# Copyright © 2015-2017 OpenMarket Ltd +# Copyright © 2019 Damir Jelić +# +# 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 SAS module. + +This module contains functions to perform key verification using the Short +Authentication String (SAS) method. + +Examples: + >>> sas = Sas() + >>> bob_key = "3p7bfXt9wbTTW2HC7OQ1Nz+DQ8hbeGdNrfx+FG+IK08" + >>> message = "Hello world!" + >>> extra_info = "MAC" + >>> sas_alice.set_their_pubkey(bob_key) + >>> sas_alice.calculate_mac(message, extra_info) + >>> sas_alice.generate_bytes(extra_info, 5) + +""" + +from functools import wraps +from builtins import bytes +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 ._finalize import track_for_finalization + + +def other_pubkey_set(func): + """Ensure that the other pubkey is added to the Sas object.""" + @wraps(func) + def wrapper(self, *args, **kwargs): + if not self.other_key_set: + raise OlmSasError("The other public key isn't set.") + return func(self, *args, **kwargs) + return wrapper + + +def _clear_sas(sas): + # type: (ffi.cdata) -> None + lib.olm_clear_sas(sas) + + +class OlmSasError(Exception): + """libolm Sas error exception.""" + + +class Sas(object): + """libolm Short Authenticaton String (SAS) class.""" + + 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) + + def _create_sas(self, buffer, buffer_length): + self._check_error( + lib.olm_create_sas( + self._sas, + ffi.from_buffer(buffer), + buffer_length + ) + ) + + def _check_error(self, ret): + # type: (int) -> None + if ret != lib.olm_error(): + return + + last_error = bytes_to_native_str( + ffi.string((lib.olm_sas_last_error(self._sas)))) + + raise OlmSasError(last_error) + + @property + def pubkey(self): + # type: () -> str + """Get the public key for the SAS object. + + This returns the public key of the SAS object that can then be shared + with another user to perform the authentication process. + + Raises OlmSasError on failure. + + """ + pubkey_length = lib.olm_sas_pubkey_length(self._sas) + pubkey_buffer = ffi.new("char[]", pubkey_length) + + self._check_error( + lib.olm_sas_get_pubkey(self._sas, pubkey_buffer, pubkey_length) + ) + + return bytes_to_native_str(ffi.unpack(pubkey_buffer, pubkey_length)) + + def set_their_pubkey(self, key): + # type: (str) -> None + """Set the public key of the other user. + + This sets the public key of the other user, it needs to be set before + bytes can be generated for the authentication string and a MAC can be + calculated. + + Args: + key (str): The other users public key. + + Raises OlmSasError on failure. + + """ + byte_key = to_bytearray(key) + + self._check_error( + lib.olm_sas_set_their_key( + self._sas, + ffi.from_buffer(byte_key), + len(byte_key) + ) + ) + self.other_key_set = True + + @other_pubkey_set + 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) + + @other_pubkey_set + 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)) -- cgit v1.2.3