Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ nox = [
"nox-uv>=0.6.0",
]
tests = [
"hypothesis>=6.135.21",
"maturin>=1.9.0",
"pytest>=8.4.1",
]
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ fn my_module(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<types::PreKeyMessage>()?;

m.add_class::<sas::Sas>()?;
m.add_class::<sas::EstablishedSas>()?;

m.add_class::<group_sessions::GroupSession>()?;
m.add_class::<group_sessions::InboundGroupSession>()?;
Expand Down
111 changes: 48 additions & 63 deletions tests/account_test.py
Original file line number Diff line number Diff line change
@@ -1,64 +1,49 @@
import vodozemac
import pytest

from vodozemac import Account, PickleException, SignatureException

PICKLE_KEY = b"DEFAULT_PICKLE_KEY_1234567890___"

class TestClass(object):
def test_account_creation(self):
account = Account()

assert account.ed25519_key
assert account.curve25519_key

def test_generating_onet_time_keys(self):
account = Account()

assert len(account.one_time_keys) == 0

account.generate_one_time_keys(10)
assert len(account.one_time_keys) == 10

def test_pickling(self):
alice = Account()
pickle = alice.pickle(PICKLE_KEY)
unpickled = Account.from_pickle(pickle, PICKLE_KEY)
assert (alice.ed25519_key == unpickled.ed25519_key)

def test_libolm_pickling(self):
pickle = (
"3wpPcPT4xsRYCYF34NcnozxE5bN2E6qwBXQYuoovt/TX//8Dnd8gaKsxN9En/"
"7Hkh5XemuGUo3dXHVTl76G2pjf9ehfryhITMbeBrE/XuxmNvS2aB9KU4mOKXl"
"AWhCEsE7JW9fUkRhHWWkFwTvSC3eDthd6eNx3VKZlmGR270vIpIG5/Ho4YK9/"
"03lPGpil0cuEuGTTjKHXGRu9kpnQe99QGCB4KBuP5IJjFeWbtSgJ4ZrajZdlTew"
)

unpickled = Account.from_libolm_pickle(pickle, b"It's a secret to everybody")

assert unpickled.ed25519_key.to_base64() == "MEQCwaTE/gcrHaxwv06WEVy5xDA30FboFzCAtYhzmoc"

def test_invalid_pickle(self):
with pytest.raises(PickleException):
Account.from_pickle("", PICKLE_KEY)

def test_max_one_time_keys(self):
alice = Account()
assert isinstance(alice.max_number_of_one_time_keys, int)

def test_publish_one_time_keys(self):
alice = Account()
alice.generate_one_time_keys(10)

assert len(alice.one_time_keys) == 10

alice.mark_keys_as_published()
assert not alice.one_time_keys

def test_signing(self):
alice = Account()
signature = alice.sign(b"This is a test")

alice.ed25519_key.verify_signature(b"This is a test", signature)
with pytest.raises(SignatureException):
alice.ed25519_key.verify_signature(b"This should fail", signature)
from hypothesis import given
from vodozemac import Account, PickleException, SignatureException, Ed25519PublicKey, Curve25519PublicKey

@pytest.fixture(scope="module")
def account() -> Account:
return Account()

def test_creation(account: Account):
assert isinstance(account.ed25519_key, Ed25519PublicKey)
assert isinstance(account.curve25519_key, Curve25519PublicKey)
assert isinstance(account.max_number_of_one_time_keys, int)

def test_generate_and_publish_one_time_keys(account: Account):
assert len(account.one_time_keys) == 0
account.generate_one_time_keys(10)
assert len(account.one_time_keys) == 10
account.mark_keys_as_published()
assert not account.one_time_keys

def test_pickling(account: Account, pickle_key: bytes):
pickle = account.pickle(pickle_key)
unpickled = Account.from_pickle(pickle, pickle_key)
assert account.ed25519_key == unpickled.ed25519_key
assert account.curve25519_key == unpickled.curve25519_key
assert account.one_time_keys == unpickled.one_time_keys

def test_libolm_pickling():
pickle = (
"3wpPcPT4xsRYCYF34NcnozxE5bN2E6qwBXQYuoovt/TX//8Dnd8gaKsxN9En/"
"7Hkh5XemuGUo3dXHVTl76G2pjf9ehfryhITMbeBrE/XuxmNvS2aB9KU4mOKXl"
"AWhCEsE7JW9fUkRhHWWkFwTvSC3eDthd6eNx3VKZlmGR270vIpIG5/Ho4YK9/"
"03lPGpil0cuEuGTTjKHXGRu9kpnQe99QGCB4KBuP5IJjFeWbtSgJ4ZrajZdlTew"
)

unpickled = Account.from_libolm_pickle(pickle, b"It's a secret to everybody")

assert unpickled.ed25519_key.to_base64() == "MEQCwaTE/gcrHaxwv06WEVy5xDA30FboFzCAtYhzmoc"

def test_invalid_pickle(pickle_key: bytes):
with pytest.raises(PickleException):
Account.from_pickle("", pickle_key)

@given(message=...)
def test_signing(account: Account, message: bytes):
signature = account.sign(message)
account.ed25519_key.verify_signature(message, signature)
with pytest.raises(SignatureException):
account.ed25519_key.verify_signature(b"This should fail", signature)
6 changes: 6 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import pytest


@pytest.fixture(scope="session")
def pickle_key():
return b"DEFAULT_PICKLE_KEY_1234567890___"
167 changes: 71 additions & 96 deletions tests/group_session_test.py
Original file line number Diff line number Diff line change
@@ -1,114 +1,89 @@
import pytest

from hypothesis import given
from vodozemac import (
InboundGroupSession,
GroupSession,
PickleException,
DecodeException,
InboundGroupSession,
MegolmDecryptionException,
PickleException,
)

PICKLE_KEY = b"DEFAULT_PICKLE_KEY_1234567890___"
@pytest.fixture(scope="module")
def group_session() -> GroupSession:
return GroupSession()

@pytest.fixture(scope="module")
def inbound_group_session(group_session: GroupSession) -> InboundGroupSession:
return InboundGroupSession(group_session.session_key)


def test_create(group_session: GroupSession, inbound_group_session: InboundGroupSession):
assert isinstance(group_session.session_id, str)
assert isinstance(group_session.message_index, int)
assert group_session.message_index == 0

assert isinstance(inbound_group_session.first_known_index, int)
assert inbound_group_session.first_known_index == 0

class TestClass(object):
def test_session_create(self):
GroupSession()
assert group_session.session_id == inbound_group_session.session_id

def test_session_id(self):
session = GroupSession()
assert isinstance(session.session_id, str)
def test_outbound_pickle(group_session: GroupSession, pickle_key: bytes):
pickle = group_session.pickle(pickle_key)
unpickled = GroupSession.from_pickle(pickle, pickle_key)

def test_session_index(self):
session = GroupSession()
assert isinstance(session.message_index, int)
assert session.message_index == 0
assert group_session.session_id == unpickled.session_id

def test_outbound_pickle(self):
session = GroupSession()
pickle = session.pickle(PICKLE_KEY)
unpickled = GroupSession.from_pickle(pickle, PICKLE_KEY)
def test_outbound_pickle_fail(group_session: GroupSession, pickle_key: bytes):
wrong_pickle_key = b"It's a secret to everybody 12345"
pickle = group_session.pickle(wrong_pickle_key)

assert session.session_id == unpickled.session_id
with pytest.raises(ValueError):
GroupSession.from_pickle(pickle, pickle_key)

def test_invalid_unpickle(self):
with pytest.raises(PickleException):
GroupSession.from_pickle("", PICKLE_KEY)
@pytest.mark.parametrize("cls", (GroupSession, InboundGroupSession))
def test_invalid_pickle(cls: type, pickle_key: bytes):
with pytest.raises(PickleException):
cls.from_pickle("", pickle_key)

with pytest.raises(PickleException):
InboundGroupSession.from_pickle("", PICKLE_KEY)

def test_inbound_create(self):
outbound = GroupSession()
InboundGroupSession(outbound.session_key)
def test_inbound_create(inbound_group_session: InboundGroupSession, pickle_key: bytes):
pickle = inbound_group_session.pickle(pickle_key)
unpickled = InboundGroupSession.from_pickle(pickle, pickle_key)
assert unpickled.session_id == inbound_group_session.session_id

def test_inbound_pickle(self):
outbound = GroupSession()
inbound = InboundGroupSession(outbound.session_key)
pickle = inbound.pickle(PICKLE_KEY)
InboundGroupSession.from_pickle(pickle, PICKLE_KEY)
@given(message1=..., message2=...)
def test_encrypt_twice(group_session: GroupSession, inbound_group_session: InboundGroupSession, message1: bytes, message2: bytes):
decrypted1 = inbound_group_session.decrypt(group_session.encrypt(message1))
assert decrypted1.plaintext == message1

def test_inbound_export(self):
outbound = GroupSession()
inbound = InboundGroupSession(outbound.session_key)
imported = InboundGroupSession.import_session(
inbound.export_at(inbound.first_known_index)
decrypted2 = inbound_group_session.decrypt(group_session.encrypt(message2))
assert decrypted2.plaintext == message2

assert decrypted2.message_index == decrypted1.message_index + 1

def test_decrypt_failure(inbound_group_session: InboundGroupSession):
wrong_group_session = GroupSession()
with pytest.raises(MegolmDecryptionException):
inbound_group_session.decrypt(wrong_group_session.encrypt(b"Test"))


@given(message=...)
def test_inbound_export(group_session: GroupSession, inbound_group_session: InboundGroupSession, message: bytes):
imported = InboundGroupSession.import_session(
session_key=inbound_group_session.export_at(
index=inbound_group_session.first_known_index
)
message = imported.decrypt(outbound.encrypt(b"Test"))
assert message.plaintext == b"Test"
assert message.message_index == 0

def test_first_index(self):
outbound = GroupSession()
inbound = InboundGroupSession(outbound.session_key)
index = inbound.first_known_index
assert index == 0
assert isinstance(index, int)

def test_encrypt(self):
outbound = GroupSession()
inbound = InboundGroupSession(outbound.session_key)
message = inbound.decrypt(outbound.encrypt(b"Test"))
assert b"Test", 0 == inbound.decrypt(outbound.encrypt(b"Test"))

def test_decrypt_twice(self):
outbound = GroupSession()
inbound = InboundGroupSession(outbound.session_key)
outbound.encrypt(b"Test 1")
message = inbound.decrypt(outbound.encrypt(b"Test 2"))
assert isinstance(message.message_index, int)
assert message.message_index == 1
assert message.plaintext == b"Test 2"

def test_decrypt_failure(self):
outbound = GroupSession()
inbound = InboundGroupSession(outbound.session_key)
eve_outbound = GroupSession()
with pytest.raises(MegolmDecryptionException):
inbound.decrypt(eve_outbound.encrypt(b"Test"))

def test_id(self):
outbound = GroupSession()
inbound = InboundGroupSession(outbound.session_key)
assert outbound.session_id == inbound.session_id

def test_inbound_fail(self):
with pytest.raises(TypeError):
InboundGroupSession()

def test_outbound_pickle_fail(self):
outbound = GroupSession()
pickle_key = b"It's a secret to everybody 12345"
pickle = outbound.pickle(pickle_key)

with pytest.raises(ValueError):
GroupSession.from_pickle(pickle, PICKLE_KEY)

def test_outbound_clear(self):
session = GroupSession()
del session

def test_inbound_clear(self):
outbound = GroupSession()
inbound = InboundGroupSession(outbound.session_key)
del inbound
)
index = group_session.message_index
decrypted = imported.decrypt(group_session.encrypt(message))

assert decrypted.plaintext == message
assert decrypted.message_index == index

def test_outbound_clear():
session = GroupSession()
del session

def test_inbound_clear():
outbound = GroupSession()
inbound = InboundGroupSession(outbound.session_key)
del inbound
Loading
Loading