Merge branch 'python-sas'
This commit is contained in:
commit
086133f39a
8 changed files with 444 additions and 11 deletions
|
@ -1,4 +1,5 @@
|
||||||
include include/olm/olm.h
|
include include/olm/olm.h
|
||||||
include include/olm/pk.h
|
include include/olm/pk.h
|
||||||
|
include include/olm/sas.h
|
||||||
include Makefile
|
include Makefile
|
||||||
include olm_build.py
|
include olm_build.py
|
||||||
|
|
|
@ -12,7 +12,10 @@ include/olm/olm.h: $(OLM_HEADERS)
|
||||||
include/olm/pk.h: include/olm/olm.h ../include/olm/pk.h
|
include/olm/pk.h: include/olm/olm.h ../include/olm/pk.h
|
||||||
$(CPP) -I dummy -I ../include ../include/olm/pk.h -o include/olm/pk.h
|
$(CPP) -I dummy -I ../include ../include/olm/pk.h -o include/olm/pk.h
|
||||||
|
|
||||||
headers: include/olm/olm.h include/olm/pk.h
|
include/olm/sas.h: include/olm/olm.h ../include/olm/sas.h
|
||||||
|
$(CPP) -I dummy -I ../include ../include/olm/sas.h -o include/olm/sas.h
|
||||||
|
|
||||||
|
headers: include/olm/olm.h include/olm/pk.h include/olm/sas.h
|
||||||
|
|
||||||
olm-python2: headers
|
olm-python2: headers
|
||||||
DEVELOP=$(DEVELOP) python2 setup.py build
|
DEVELOP=$(DEVELOP) python2 setup.py build
|
||||||
|
|
|
@ -21,7 +21,7 @@ Olm Python bindings
|
||||||
| © Copyright 2015-2017 by OpenMarket Ltd
|
| © Copyright 2015-2017 by OpenMarket Ltd
|
||||||
| © Copyright 2018 by Damir Jelić
|
| © Copyright 2018 by Damir Jelić
|
||||||
"""
|
"""
|
||||||
from .utility import ed25519_verify, OlmVerifyError
|
from .utility import ed25519_verify, OlmVerifyError, OlmHashError, sha256
|
||||||
from .account import Account, OlmAccountError
|
from .account import Account, OlmAccountError
|
||||||
from .session import (
|
from .session import (
|
||||||
Session,
|
Session,
|
||||||
|
@ -43,3 +43,4 @@ from .pk import (
|
||||||
PkEncryptionError,
|
PkEncryptionError,
|
||||||
PkDecryptionError
|
PkDecryptionError
|
||||||
)
|
)
|
||||||
|
from .sas import Sas, OlmSasError
|
||||||
|
|
257
python/olm/sas.py
Normal file
257
python/olm/sas.py
Normal file
|
@ -0,0 +1,257 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# libolm python bindings
|
||||||
|
# Copyright © 2019 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 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))
|
||||||
|
|
||||||
|
@other_pubkey_set
|
||||||
|
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))
|
|
@ -32,6 +32,7 @@ Examples:
|
||||||
|
|
||||||
# pylint: disable=redefined-builtin,unused-import
|
# pylint: disable=redefined-builtin,unused-import
|
||||||
from typing import AnyStr, Type
|
from typing import AnyStr, Type
|
||||||
|
from future.utils import bytes_to_native_str
|
||||||
|
|
||||||
# pylint: disable=no-name-in-module
|
# pylint: disable=no-name-in-module
|
||||||
from _libolm import ffi, lib # type: ignore
|
from _libolm import ffi, lib # type: ignore
|
||||||
|
@ -49,6 +50,10 @@ class OlmVerifyError(Exception):
|
||||||
"""libolm signature verification exception."""
|
"""libolm signature verification exception."""
|
||||||
|
|
||||||
|
|
||||||
|
class OlmHashError(Exception):
|
||||||
|
"""libolm hash calculation exception."""
|
||||||
|
|
||||||
|
|
||||||
class _Utility(object):
|
class _Utility(object):
|
||||||
# pylint: disable=too-few-public-methods
|
# pylint: disable=too-few-public-methods
|
||||||
"""libolm Utility class."""
|
"""libolm Utility class."""
|
||||||
|
@ -64,12 +69,12 @@ class _Utility(object):
|
||||||
track_for_finalization(cls, cls._utility, _clear_utility)
|
track_for_finalization(cls, cls._utility, _clear_utility)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _check_error(cls, ret):
|
def _check_error(cls, ret, error_class):
|
||||||
# type: (int) -> None
|
# type: (int, Type) -> None
|
||||||
if ret != lib.olm_error():
|
if ret != lib.olm_error():
|
||||||
return
|
return
|
||||||
|
|
||||||
raise OlmVerifyError("{}".format(
|
raise error_class("{}".format(
|
||||||
ffi.string(lib.olm_utility_last_error(
|
ffi.string(lib.olm_utility_last_error(
|
||||||
cls._utility)).decode("utf-8")))
|
cls._utility)).decode("utf-8")))
|
||||||
|
|
||||||
|
@ -84,18 +89,41 @@ class _Utility(object):
|
||||||
byte_signature = to_bytearray(signature)
|
byte_signature = to_bytearray(signature)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cls._check_error(
|
ret = lib.olm_ed25519_verify(
|
||||||
lib.olm_ed25519_verify(cls._utility, byte_key, len(byte_key),
|
cls._utility,
|
||||||
ffi.from_buffer(byte_message),
|
byte_key,
|
||||||
len(byte_message),
|
len(byte_key),
|
||||||
ffi.from_buffer(byte_signature),
|
ffi.from_buffer(byte_message),
|
||||||
len(byte_signature)))
|
len(byte_message),
|
||||||
|
ffi.from_buffer(byte_signature),
|
||||||
|
len(byte_signature)
|
||||||
|
)
|
||||||
|
|
||||||
|
cls._check_error(ret, OlmVerifyError)
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
# clear out copies of the message, which may be a plaintext
|
# clear out copies of the message, which may be a plaintext
|
||||||
if byte_message is not message:
|
if byte_message is not message:
|
||||||
for i in range(0, len(byte_message)):
|
for i in range(0, len(byte_message)):
|
||||||
byte_message[i] = 0
|
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):
|
def ed25519_verify(key, message, signature):
|
||||||
# type: (AnyStr, AnyStr, AnyStr) -> None
|
# type: (AnyStr, AnyStr, AnyStr) -> None
|
||||||
|
@ -109,3 +137,14 @@ def ed25519_verify(key, message, signature):
|
||||||
signature(bytes): The message signature.
|
signature(bytes): The message signature.
|
||||||
"""
|
"""
|
||||||
return _Utility._ed25519_verify(key, 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)
|
||||||
|
|
|
@ -43,6 +43,7 @@ ffibuilder.set_source(
|
||||||
#include <olm/inbound_group_session.h>
|
#include <olm/inbound_group_session.h>
|
||||||
#include <olm/outbound_group_session.h>
|
#include <olm/outbound_group_session.h>
|
||||||
#include <olm/pk.h>
|
#include <olm/pk.h>
|
||||||
|
#include <olm/sas.h>
|
||||||
""",
|
""",
|
||||||
libraries=["olm"],
|
libraries=["olm"],
|
||||||
extra_compile_args=compile_args,
|
extra_compile_args=compile_args,
|
||||||
|
@ -54,5 +55,8 @@ with open(os.path.join(PATH, "include/olm/olm.h")) as f:
|
||||||
with open(os.path.join(PATH, "include/olm/pk.h")) as f:
|
with open(os.path.join(PATH, "include/olm/pk.h")) as f:
|
||||||
ffibuilder.cdef(f.read(), override=True)
|
ffibuilder.cdef(f.read(), override=True)
|
||||||
|
|
||||||
|
with open(os.path.join(PATH, "include/olm/sas.h")) as f:
|
||||||
|
ffibuilder.cdef(f.read(), override=True)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
ffibuilder.compile(verbose=True)
|
ffibuilder.compile(verbose=True)
|
||||||
|
|
99
python/tests/sas_test.py
Normal file
99
python/tests/sas_test.py
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
from builtins import bytes
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from olm import OlmSasError, Sas
|
||||||
|
|
||||||
|
MESSAGE = "Test message"
|
||||||
|
EXTRA_INFO = "extra_info"
|
||||||
|
|
||||||
|
|
||||||
|
class TestClass(object):
|
||||||
|
def test_sas_creation(self):
|
||||||
|
sas = Sas()
|
||||||
|
assert sas.pubkey
|
||||||
|
|
||||||
|
def test_other_key_setting(self):
|
||||||
|
sas_alice = Sas()
|
||||||
|
sas_bob = Sas()
|
||||||
|
|
||||||
|
assert not sas_alice.other_key_set
|
||||||
|
sas_alice.set_their_pubkey(sas_bob.pubkey)
|
||||||
|
assert sas_alice.other_key_set
|
||||||
|
|
||||||
|
def test_bytes_generating(self):
|
||||||
|
sas_alice = Sas()
|
||||||
|
sas_bob = Sas(sas_alice.pubkey)
|
||||||
|
|
||||||
|
assert sas_bob.other_key_set
|
||||||
|
|
||||||
|
with pytest.raises(OlmSasError):
|
||||||
|
sas_alice.generate_bytes(EXTRA_INFO, 5)
|
||||||
|
|
||||||
|
sas_alice.set_their_pubkey(sas_bob.pubkey)
|
||||||
|
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
sas_alice.generate_bytes(EXTRA_INFO, 0)
|
||||||
|
|
||||||
|
alice_bytes = sas_alice.generate_bytes(EXTRA_INFO, 5)
|
||||||
|
bob_bytes = sas_bob.generate_bytes(EXTRA_INFO, 5)
|
||||||
|
|
||||||
|
assert alice_bytes == bob_bytes
|
||||||
|
|
||||||
|
def test_mac_generating(self):
|
||||||
|
sas_alice = Sas()
|
||||||
|
sas_bob = Sas()
|
||||||
|
|
||||||
|
with pytest.raises(OlmSasError):
|
||||||
|
sas_alice.calculate_mac(MESSAGE, EXTRA_INFO)
|
||||||
|
|
||||||
|
sas_alice.set_their_pubkey(sas_bob.pubkey)
|
||||||
|
sas_bob.set_their_pubkey(sas_alice.pubkey)
|
||||||
|
|
||||||
|
alice_mac = sas_alice.calculate_mac(MESSAGE, EXTRA_INFO)
|
||||||
|
bob_mac = sas_bob.calculate_mac(MESSAGE, EXTRA_INFO)
|
||||||
|
|
||||||
|
assert alice_mac == bob_mac
|
||||||
|
|
||||||
|
def test_cross_language_mac(self):
|
||||||
|
"""Test MAC generating with a predefined key pair.
|
||||||
|
|
||||||
|
This test imports a private and public key from the C test and checks
|
||||||
|
if we are getting the same MAC that the C code calculated.
|
||||||
|
"""
|
||||||
|
alice_private = [
|
||||||
|
0x77, 0x07, 0x6D, 0x0A, 0x73, 0x18, 0xA5, 0x7D,
|
||||||
|
0x3C, 0x16, 0xC1, 0x72, 0x51, 0xB2, 0x66, 0x45,
|
||||||
|
0xDF, 0x4C, 0x2F, 0x87, 0xEB, 0xC0, 0x99, 0x2A,
|
||||||
|
0xB1, 0x77, 0xFB, 0xA5, 0x1D, 0xB9, 0x2C, 0x2A
|
||||||
|
]
|
||||||
|
|
||||||
|
bob_key = "3p7bfXt9wbTTW2HC7OQ1Nz+DQ8hbeGdNrfx+FG+IK08"
|
||||||
|
message = "Hello world!"
|
||||||
|
extra_info = "MAC"
|
||||||
|
expected_mac = "2nSMTXM+TStTU3RUVTNSVVZUTlNWVlpVVGxOV1ZscFY"
|
||||||
|
|
||||||
|
sas_alice = Sas()
|
||||||
|
sas_alice._create_sas(bytes(alice_private), 32)
|
||||||
|
sas_alice.set_their_pubkey(bob_key)
|
||||||
|
|
||||||
|
alice_mac = sas_alice.calculate_mac(message, extra_info)
|
||||||
|
|
||||||
|
assert alice_mac == expected_mac
|
||||||
|
|
||||||
|
def test_long_mac_generating(self):
|
||||||
|
sas_alice = Sas()
|
||||||
|
sas_bob = Sas()
|
||||||
|
|
||||||
|
with pytest.raises(OlmSasError):
|
||||||
|
sas_alice.calculate_mac_long_kdf(MESSAGE, EXTRA_INFO)
|
||||||
|
|
||||||
|
sas_alice.set_their_pubkey(sas_bob.pubkey)
|
||||||
|
sas_bob.set_their_pubkey(sas_alice.pubkey)
|
||||||
|
|
||||||
|
alice_mac = sas_alice.calculate_mac_long_kdf(MESSAGE, EXTRA_INFO)
|
||||||
|
bob_mac = sas_bob.calculate_mac_long_kdf(MESSAGE, EXTRA_INFO)
|
||||||
|
bob_short_mac = sas_bob.calculate_mac(MESSAGE, EXTRA_INFO)
|
||||||
|
|
||||||
|
assert alice_mac == bob_mac
|
||||||
|
assert alice_mac != bob_short_mac
|
29
python/tests/utils_test.py
Normal file
29
python/tests/utils_test.py
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import base64
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
from future.utils import bytes_to_native_str
|
||||||
|
from hypothesis import given
|
||||||
|
from hypothesis.strategies import text
|
||||||
|
|
||||||
|
from olm import sha256
|
||||||
|
from olm._compat import to_bytes
|
||||||
|
|
||||||
|
|
||||||
|
class TestClass(object):
|
||||||
|
@given(text(), text())
|
||||||
|
def test_sha256(self, input1, input2):
|
||||||
|
first_hash = sha256(input1)
|
||||||
|
second_hash = sha256(input2)
|
||||||
|
|
||||||
|
hashlib_hash = base64.b64encode(
|
||||||
|
hashlib.sha256(to_bytes(input1)).digest()
|
||||||
|
)
|
||||||
|
|
||||||
|
hashlib_hash = bytes_to_native_str(hashlib_hash[:-1])
|
||||||
|
|
||||||
|
if input1 == input2:
|
||||||
|
assert first_hash == second_hash
|
||||||
|
else:
|
||||||
|
assert first_hash != second_hash
|
||||||
|
|
||||||
|
assert hashlib_hash == first_hash
|
Loading…
Reference in a new issue