59 lines
1.9 KiB
Python
59 lines
1.9 KiB
Python
"""enc:v2 envelope encryption (H4): roundtrip, legacy v1 compat, format checks."""
|
|
import base64
|
|
import hashlib
|
|
|
|
import pytest
|
|
from cryptography.fernet import Fernet
|
|
|
|
from app.services import crypto
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _kek(monkeypatch):
|
|
monkeypatch.setattr(crypto.settings, "encryption_key", "k" * 64)
|
|
# Reset module caches so each test derives from the patched KEK.
|
|
crypto._fernet_v1 = None
|
|
crypto._fernet_v2_cache.clear()
|
|
crypto._encrypt_salt = None
|
|
yield
|
|
crypto._fernet_v1 = None
|
|
crypto._fernet_v2_cache.clear()
|
|
crypto._encrypt_salt = None
|
|
|
|
|
|
def test_v2_roundtrip():
|
|
blob = crypto.encrypt_api_key("0x" + "ab" * 32)
|
|
assert blob.startswith(crypto.ENC_PREFIX_V2)
|
|
assert crypto.decrypt_api_key(blob) == "0x" + "ab" * 32
|
|
assert crypto.is_current_format(blob)
|
|
# Fits the hl_api_key String(256) column.
|
|
assert len(blob) <= 256
|
|
|
|
|
|
def test_v1_blob_still_decrypts():
|
|
# Build a v1 blob exactly the way the legacy code did.
|
|
digest = hashlib.sha256(("k" * 64).encode()).digest()
|
|
f = Fernet(base64.urlsafe_b64encode(digest))
|
|
blob = crypto.ENC_PREFIX_V1 + f.encrypt(b"secret-key").decode()
|
|
assert not crypto.is_current_format(blob)
|
|
assert crypto.decrypt_api_key(blob) == "secret-key"
|
|
|
|
|
|
def test_wrong_kek_raises(monkeypatch):
|
|
blob = crypto.encrypt_api_key("topsecret")
|
|
monkeypatch.setattr(crypto.settings, "encryption_key", "x" * 64)
|
|
crypto._fernet_v2_cache.clear()
|
|
with pytest.raises(RuntimeError):
|
|
crypto.decrypt_api_key(blob)
|
|
|
|
|
|
def test_malformed_v2_raises():
|
|
with pytest.raises(RuntimeError):
|
|
crypto.decrypt_api_key(crypto.ENC_PREFIX_V2 + "no-salt-separator")
|
|
|
|
|
|
def test_plaintext_refused_in_production(monkeypatch):
|
|
monkeypatch.setattr(crypto.settings, "environment", "production")
|
|
with pytest.raises(RuntimeError):
|
|
crypto.decrypt_api_key("raw-plaintext-key")
|