diff --git a/src/tests/test_key_derivation.py b/src/tests/test_key_derivation.py index 6eff174..f734eb6 100644 --- a/src/tests/test_key_derivation.py +++ b/src/tests/test_key_derivation.py @@ -1,6 +1,10 @@ import logging import pytest -from utils.key_derivation import derive_key_from_password +from utils.key_derivation import ( + derive_key_from_password, + derive_index_key_seed_only, + derive_index_key_seed_plus_pw, +) def test_derive_key_deterministic(): @@ -16,3 +20,19 @@ def test_derive_key_empty_password_error(): with pytest.raises(ValueError): derive_key_from_password("") logging.info("Empty password correctly raised ValueError") + + +def test_seed_only_key_deterministic(): + seed = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" + k1 = derive_index_key_seed_only(seed) + k2 = derive_index_key_seed_only(seed) + assert k1 == k2 + assert len(k1) == 44 + + +def test_seed_plus_pw_differs_from_seed_only(): + seed = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" + pw = "hunter2" + k1 = derive_index_key_seed_only(seed) + k2 = derive_index_key_seed_plus_pw(seed, pw) + assert k1 != k2 diff --git a/src/utils/key_derivation.py b/src/utils/key_derivation.py index 31a3fd9..3a9065f 100644 --- a/src/utils/key_derivation.py +++ b/src/utils/key_derivation.py @@ -167,3 +167,32 @@ class KeyManager: private_key_hex = entropy_bytes.hex() keys = Keys(priv_key=private_key_hex) return keys + + +def derive_index_key_seed_only(seed: str) -> bytes: + """Derive a deterministic Fernet key from only the BIP-39 seed.""" + seed_bytes = Bip39SeedGenerator(seed).Generate() + hkdf = HKDF( + algorithm=hashes.SHA256(), + length=32, + salt=None, + info=b"password-db", + backend=default_backend(), + ) + key = hkdf.derive(seed_bytes) + return base64.urlsafe_b64encode(key) + + +def derive_index_key_seed_plus_pw(seed: str, password: str) -> bytes: + """Derive the index key from seed and password combined.""" + seed_bytes = Bip39SeedGenerator(seed).Generate() + pw_bytes = unicodedata.normalize("NFKD", password).encode("utf-8") + hkdf = HKDF( + algorithm=hashes.SHA256(), + length=32, + salt=None, + info=b"password-db", + backend=default_backend(), + ) + key = hkdf.derive(seed_bytes + b"|" + pw_bytes) + return base64.urlsafe_b64encode(key)