79 lines
2.6 KiB
Python
79 lines
2.6 KiB
Python
|
|
import pytest
|
|||
|
|
|
|||
|
|
from app import crypto
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_round_trip():
|
|||
|
|
data = '{"привет": "мир"}'.encode("utf-8")
|
|||
|
|
blob = crypto.encrypt(data, "пароль123")
|
|||
|
|
assert crypto.decrypt(blob, "пароль123") == data
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_wrong_password():
|
|||
|
|
blob = crypto.encrypt(b"secret", "правильный")
|
|||
|
|
with pytest.raises(crypto.WrongPassword):
|
|||
|
|
crypto.decrypt(blob, "неправильный")
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_empty_password_still_works():
|
|||
|
|
blob = crypto.encrypt(b"secret", "")
|
|||
|
|
assert crypto.decrypt(blob, "") == b"secret"
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_same_data_gives_different_bytes():
|
|||
|
|
"""Соль и nonce случайны, поэтому шифртекст не детерминирован.
|
|||
|
|
|
|||
|
|
Это причина, по которой storage сравнивает хеш открытого JSON, а не байты
|
|||
|
|
файла — иначе git получал бы новый коммит на каждое сохранение.
|
|||
|
|
"""
|
|||
|
|
a = crypto.encrypt(b"same", "pw")
|
|||
|
|
b = crypto.encrypt(b"same", "pw")
|
|||
|
|
assert a != b
|
|||
|
|
assert crypto.decrypt(a, "pw") == crypto.decrypt(b, "pw")
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_header_is_authenticated():
|
|||
|
|
"""Подмена соли в заголовке ловится как ошибка, а не молча меняет ключ."""
|
|||
|
|
blob = bytearray(crypto.encrypt(b"secret", "pw"))
|
|||
|
|
blob[9] ^= 0xFF # первый байт соли
|
|||
|
|
with pytest.raises(crypto.WrongPassword):
|
|||
|
|
crypto.decrypt(bytes(blob), "pw")
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_tampered_ciphertext_rejected():
|
|||
|
|
blob = bytearray(crypto.encrypt(b"secret", "pw"))
|
|||
|
|
blob[-1] ^= 0xFF
|
|||
|
|
with pytest.raises(crypto.WrongPassword):
|
|||
|
|
crypto.decrypt(bytes(blob), "pw")
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_not_a_vault_file():
|
|||
|
|
with pytest.raises(crypto.UnsupportedFormat):
|
|||
|
|
crypto.decrypt(b"\x00" * 100, "pw")
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_truncated_file():
|
|||
|
|
with pytest.raises(crypto.UnsupportedFormat):
|
|||
|
|
crypto.decrypt(b"FMDB\x01", "pw")
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_future_format_version_reports_clearly():
|
|||
|
|
blob = bytearray(crypto.encrypt(b"secret", "pw"))
|
|||
|
|
blob[4] = 99
|
|||
|
|
with pytest.raises(crypto.UnsupportedFormat, match="новой версией"):
|
|||
|
|
crypto.decrypt(bytes(blob), "pw")
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_absurd_scrypt_params_rejected_without_allocating():
|
|||
|
|
"""Чужой файл не должен заставить нас выделить гигабайты под scrypt."""
|
|||
|
|
blob = bytearray(crypto.encrypt(b"secret", "pw"))
|
|||
|
|
blob[6] = 40 # log2(n) = 40
|
|||
|
|
with pytest.raises(crypto.UnsupportedFormat):
|
|||
|
|
crypto.decrypt(bytes(blob), "pw")
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_compression_actually_helps():
|
|||
|
|
data = b'{"bun": "poppy"}' * 500
|
|||
|
|
blob = crypto.encrypt(data, "pw")
|
|||
|
|
assert len(blob) < len(data) // 4
|