Files
lnbits/tests/unit/test_crypto_aes.py
T
2026-03-31 09:48:43 +03:00

110 lines
3.0 KiB
Python

from base64 import b64encode
from hashlib import sha256
import pytest
from pytest_mock.plugin import MockerFixture
from lnbits.utils.crypto import (
AESCipher,
fake_privkey,
random_secret_and_hash,
verify_preimage,
)
@pytest.mark.anyio
@pytest.mark.parametrize(
"key",
[
"normal_string",
b"normal_bytes",
b"hex_string".hex(),
],
)
async def test_aes_encrypt_decrypt(key):
aes = AESCipher(key)
original_text = "Hello, World!"
encrypted_text = aes.encrypt(original_text.encode())
decrypted_text = aes.decrypt(encrypted_text)
assert original_text == decrypted_text
def test_random_secret_and_hash():
secret, payment_hash = random_secret_and_hash(16)
assert len(secret) == 32
assert payment_hash == sha256(bytes.fromhex(secret)).hexdigest()
def test_fake_privkey_is_deterministic():
assert fake_privkey("secret") == fake_privkey("secret")
assert fake_privkey("secret") != fake_privkey("other-secret")
def test_verify_preimage_success_and_failure():
preimage = "00" * 32
payment_hash = sha256(bytes.fromhex(preimage)).hexdigest()
assert verify_preimage(preimage, payment_hash) is True
assert verify_preimage(preimage, "0" * 64) is False
@pytest.mark.anyio
async def test_aes_urlsafe_encrypt_decrypt():
aes = AESCipher("normal_string")
encrypted_text = aes.encrypt(b"url-safe", urlsafe=True)
assert aes.decrypt(encrypted_text, urlsafe=True) == "url-safe"
def test_aes_derive_iv_and_key_requires_eight_byte_salt():
aes = AESCipher("normal_string")
with pytest.raises(ValueError, match="Salt must be 8 bytes"):
aes.derive_iv_and_key(b"short")
def test_aes_decrypt_rejects_invalid_salt_prefix():
aes = AESCipher("normal_string")
encrypted_text = b64encode(b"NotSalted__12345678ciphertext").decode()
with pytest.raises(ValueError, match="Invalid salt."):
aes.decrypt(encrypted_text)
def test_aes_decrypt_raises_for_cipher_errors(mocker: MockerFixture):
aes = AESCipher("normal_string")
fake_cipher = mocker.Mock()
fake_cipher.decrypt.side_effect = RuntimeError("boom")
mocker.patch.object(
aes,
"derive_iv_and_key",
return_value=(b"0" * aes.block_size, b"1" * 32),
)
mocker.patch("lnbits.utils.crypto.AES.new", return_value=fake_cipher)
encrypted_text = b64encode(b"Salted__12345678ciphertext").decode()
with pytest.raises(ValueError, match="Could not decrypt payload"):
aes.decrypt(encrypted_text)
def test_aes_decrypt_raises_for_invalid_utf8_output(mocker: MockerFixture):
aes = AESCipher("normal_string")
fake_cipher = mocker.Mock()
fake_cipher.decrypt.return_value = b"\xff\x01"
mocker.patch.object(
aes,
"derive_iv_and_key",
return_value=(b"0" * aes.block_size, b"1" * 32),
)
mocker.patch("lnbits.utils.crypto.AES.new", return_value=fake_cipher)
encrypted_text = b64encode(b"Salted__12345678ciphertext").decode()
with pytest.raises(ValueError, match="invalid UTF-8 data"):
aes.decrypt(encrypted_text)