Files
seedPass/src/tests/test_fuzz_key_derivation.py
2025-08-19 09:53:46 -04:00

81 lines
2.5 KiB
Python

import os
from pathlib import Path
from hypothesis import given, strategies as st, settings, HealthCheck
from mnemonic import Mnemonic
import hashlib
import base64
import os
from utils.key_derivation import (
derive_key_from_password,
derive_key_from_password_argon2,
derive_index_key,
KdfConfig,
)
from utils.fingerprint import generate_fingerprint
from seedpass.core.encryption import EncryptionManager
cfg_values = st.one_of(
st.integers(min_value=0, max_value=100),
st.text(min_size=0, max_size=20),
st.booleans(),
)
@given(
password=st.text(min_size=8, max_size=32),
seed_bytes=st.binary(min_size=16, max_size=16),
config=st.dictionaries(st.text(min_size=1, max_size=10), cfg_values, max_size=5),
mode=st.sampled_from(["pbkdf2", "argon2"]),
)
@settings(
deadline=None,
max_examples=20,
suppress_health_check=[HealthCheck.function_scoped_fixture],
)
def test_fuzz_key_round_trip(password, seed_bytes, config, mode, tmp_path: Path):
"""Ensure EncryptionManager round-trips arbitrary data."""
seed_phrase = Mnemonic("english").to_mnemonic(seed_bytes)
fp = generate_fingerprint(seed_phrase)
if mode == "argon2":
cfg = KdfConfig(
params={"time_cost": 1, "memory_cost": 8, "parallelism": 1},
salt_b64=base64.b64encode(
hashlib.sha256(fp.encode()).digest()[:16]
).decode(),
)
key = derive_key_from_password_argon2(password, cfg)
else:
key = derive_key_from_password(password, fp, iterations=1)
cfg = KdfConfig(
name="pbkdf2",
params={"iterations": 1},
salt_b64=base64.b64encode(
hashlib.sha256(fp.encode()).digest()[:16]
).decode(),
)
enc_mgr = EncryptionManager(key, tmp_path)
# Parent seed round trip
enc_mgr.encrypt_parent_seed(seed_phrase, kdf=cfg)
assert enc_mgr.decrypt_parent_seed() == seed_phrase
# JSON data round trip
enc_mgr.save_json_data(config, Path("config.enc"))
loaded = enc_mgr.load_json_data(Path("config.enc"))
assert loaded == config
# Binary data round trip
blob = os.urandom(32)
enc_mgr.encrypt_and_save_file(blob, Path("blob.enc"))
assert enc_mgr.decrypt_file(Path("blob.enc")) == blob
# Index key derived from seed also decrypts
index_key = derive_index_key(seed_phrase)
idx_mgr = EncryptionManager(index_key, tmp_path)
idx_mgr.save_json_data(config)
assert idx_mgr.load_json_data() == config