Skip to content

Commit d4bf5bf

Browse files
committed
create a wrapper for libsodium to access functions needed for protocol version4
1 parent dc43388 commit d4bf5bf

File tree

2 files changed

+69
-0
lines changed

2 files changed

+69
-0
lines changed

paseto/crypto/libsodium_wrapper.py

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"""This module accesses libsodium via ctypes."""
2+
3+
import ctypes
4+
import ctypes.util
5+
6+
_library = ctypes.util.find_library("sodium") or ctypes.util.find_library("libsodium")
7+
if _library is None:
8+
raise ValueError("Could not find libsodium")
9+
_sodium = ctypes.cdll.LoadLibrary(_library)
10+
11+
12+
def crypto_stream_xchacha20_xor(message: bytes, nonce: bytes, key: bytes) -> bytes:
13+
"""Gives access to libsodium function of the same name."""
14+
15+
if len(nonce) != _sodium.crypto_stream_xchacha20_noncebytes():
16+
raise ValueError("incorrect nonce size")
17+
if len(key) != _sodium.crypto_stream_xchacha20_keybytes():
18+
raise ValueError("incorrect key size")
19+
20+
message_length: ctypes.c_longlong = ctypes.c_longlong(len(message))
21+
22+
ciphertext = ctypes.create_string_buffer(len(message))
23+
24+
exit_code = _sodium.crypto_stream_xchacha20_xor(
25+
ciphertext, message, message_length, nonce, key
26+
)
27+
if exit_code != 0:
28+
raise ValueError
29+
30+
return ciphertext.raw
+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"""This module contains tests for libsodium wrapper."""
2+
3+
import ctypes.util
4+
from unittest.mock import MagicMock, patch
5+
6+
import pytest
7+
8+
from paseto.crypto import libsodium_wrapper
9+
10+
11+
def test_key_size() -> None:
12+
"""Test exception when nonce size is incorrect."""
13+
with pytest.raises(ValueError, match="key"):
14+
libsodium_wrapper.crypto_stream_xchacha20_xor(b"", b"0" * 24, b"")
15+
16+
17+
def test_nonce_size() -> None:
18+
"""Test exception when key size is incorrect."""
19+
with pytest.raises(ValueError, match="nonce"):
20+
libsodium_wrapper.crypto_stream_xchacha20_xor(b"", b"", b"0" * 32)
21+
22+
23+
@patch.object(ctypes.util, "find_library")
24+
def test_no_libsodium(mock: MagicMock) -> None:
25+
"""Test that exception is raised when libsodium can is not found."""
26+
mock.return_value = None
27+
with pytest.raises(ValueError):
28+
import importlib
29+
30+
import paseto.crypto.libsodium_wrapper
31+
32+
importlib.reload(paseto.crypto.libsodium_wrapper)
33+
34+
35+
@patch.object(libsodium_wrapper._sodium, "crypto_stream_xchacha20_xor")
36+
def test_non_zero_exit_code(mock: MagicMock) -> None:
37+
mock.return_value = 1
38+
with pytest.raises(ValueError):
39+
libsodium_wrapper.crypto_stream_xchacha20_xor(b"", b"0" * 24, b"0" * 32)

0 commit comments

Comments
 (0)