mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2025-09-08 07:18:47 +00:00
Merge pull request #95 from PR0M3TH3AN/codex/create-bip-85-test-vector-file
Add BIP85 official test vectors
This commit is contained in:
@@ -32,9 +32,13 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BIP85:
|
||||
def __init__(self, seed_bytes: bytes):
|
||||
def __init__(self, seed_bytes: bytes | str):
|
||||
"""Initialize from BIP39 seed bytes or BIP32 xprv string."""
|
||||
try:
|
||||
self.bip32_ctx = Bip32Slip10Secp256k1.FromSeed(seed_bytes)
|
||||
if isinstance(seed_bytes, (bytes, bytearray)):
|
||||
self.bip32_ctx = Bip32Slip10Secp256k1.FromSeed(seed_bytes)
|
||||
else:
|
||||
self.bip32_ctx = Bip32Slip10Secp256k1.FromExtendedKey(seed_bytes)
|
||||
logging.debug("BIP32 context initialized successfully.")
|
||||
except Exception as e:
|
||||
logging.error(f"Error initializing BIP32 context: {e}")
|
||||
@@ -42,7 +46,9 @@ class BIP85:
|
||||
print(f"{Fore.RED}Error initializing BIP32 context: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
def derive_entropy(self, index: int, bytes_len: int, app_no: int = 39) -> bytes:
|
||||
def derive_entropy(
|
||||
self, index: int, bytes_len: int, app_no: int = 39, words_len: int | None = None
|
||||
) -> bytes:
|
||||
"""
|
||||
Derives entropy using BIP-85 HMAC-SHA512 method.
|
||||
|
||||
@@ -58,7 +64,9 @@ class BIP85:
|
||||
SystemExit: If derivation fails or entropy length is invalid.
|
||||
"""
|
||||
if app_no == 39:
|
||||
path = f"m/83696968'/{app_no}'/0'/{bytes_len}'/{index}'"
|
||||
if words_len is None:
|
||||
words_len = bytes_len
|
||||
path = f"m/83696968'/{app_no}'/0'/{words_len}'/{index}'"
|
||||
elif app_no == 32:
|
||||
path = f"m/83696968'/{app_no}'/{index}'"
|
||||
else:
|
||||
@@ -100,49 +108,29 @@ class BIP85:
|
||||
print(f"{Fore.RED}Error: Unsupported number of words: {words_num}")
|
||||
sys.exit(1)
|
||||
|
||||
entropy = self.derive_entropy(index=index, bytes_len=bytes_len, app_no=39)
|
||||
entropy = self.derive_entropy(
|
||||
index=index, bytes_len=bytes_len, app_no=39, words_len=words_num
|
||||
)
|
||||
try:
|
||||
mnemonic = Bip39MnemonicGenerator(Bip39Languages.ENGLISH).FromEntropy(
|
||||
entropy
|
||||
)
|
||||
logging.debug(f"Derived mnemonic: {mnemonic}")
|
||||
return mnemonic
|
||||
return mnemonic.ToStr()
|
||||
except Exception as e:
|
||||
logging.error(f"Error generating mnemonic: {e}")
|
||||
logging.error(traceback.format_exc()) # Log full traceback
|
||||
print(f"{Fore.RED}Error generating mnemonic: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
def derive_symmetric_key(self, app_no: int = 48, index: int = 0) -> bytes:
|
||||
"""
|
||||
Derives a symmetric encryption key using BIP85.
|
||||
|
||||
Parameters:
|
||||
app_no (int): Application number for key derivation (48 chosen arbitrarily).
|
||||
index (int): Index for key derivation.
|
||||
|
||||
Returns:
|
||||
bytes: Derived symmetric key (32 bytes for AES-256).
|
||||
|
||||
Raises:
|
||||
SystemExit: If symmetric key derivation fails.
|
||||
"""
|
||||
entropy = self.derive_entropy(
|
||||
app_no, language_code=0, words_num=24, index=index
|
||||
)
|
||||
def derive_symmetric_key(self, index: int = 0, app_no: int = 2) -> bytes:
|
||||
"""Derive 32 bytes of entropy for symmetric key usage."""
|
||||
try:
|
||||
hkdf = HKDF(
|
||||
algorithm=hashes.SHA256(),
|
||||
length=32, # 256 bits for AES-256
|
||||
salt=None,
|
||||
info=b"seedos-encryption-key",
|
||||
backend=default_backend(),
|
||||
)
|
||||
symmetric_key = hkdf.derive(entropy)
|
||||
logging.debug(f"Derived symmetric key: {symmetric_key.hex()}")
|
||||
return symmetric_key
|
||||
key = self.derive_entropy(index=index, bytes_len=32, app_no=app_no)
|
||||
logging.debug(f"Derived symmetric key: {key.hex()}")
|
||||
return key
|
||||
except Exception as e:
|
||||
logging.error(f"Error deriving symmetric key: {e}")
|
||||
logging.error(traceback.format_exc()) # Log full traceback
|
||||
logging.error(traceback.format_exc())
|
||||
print(f"{Fore.RED}Error deriving symmetric key: {e}")
|
||||
sys.exit(1)
|
||||
|
@@ -656,11 +656,8 @@ class PasswordManager:
|
||||
try:
|
||||
master_seed = os.urandom(32) # Generate a random 32-byte seed
|
||||
bip85 = BIP85(master_seed)
|
||||
mnemonic_obj = bip85.derive_mnemonic(index=0, words_num=12)
|
||||
mnemonic_str = (
|
||||
mnemonic_obj.ToStr()
|
||||
) # Convert Bip39Mnemonic object to string
|
||||
return mnemonic_str
|
||||
mnemonic = bip85.derive_mnemonic(index=0, words_num=12)
|
||||
return mnemonic
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to generate BIP-85 seed: {e}")
|
||||
logging.error(traceback.format_exc())
|
||||
|
39
src/tests/test_bip85_vectors.py
Normal file
39
src/tests/test_bip85_vectors.py
Normal file
@@ -0,0 +1,39 @@
|
||||
import sys
|
||||
from pathlib import Path
|
||||
import pytest
|
||||
|
||||
sys.path.append(str(Path(__file__).resolve().parents[1]))
|
||||
|
||||
from local_bip85.bip85 import BIP85
|
||||
|
||||
MASTER_XPRV = "xprv9s21ZrQH143K2LBWUUQRFXhucrQqBpKdRRxNVq2zBqsx8HVqFk2uYo8kmbaLLHRdqtQpUm98uKfu3vca1LqdGhUtyoFnCNkfmXRyPXLjbKb"
|
||||
|
||||
EXPECTED_12 = "girl mad pet galaxy egg matter matrix prison refuse sense ordinary nose"
|
||||
|
||||
EXPECTED_24 = "puppy ocean match cereal symbol another shed magic wrap hammer bulb intact gadget divorce twin tonight reason outdoor destroy simple truth cigar social volcano"
|
||||
|
||||
EXPECTED_SYMM_KEY = "7040bb53104f27367f317558e78a994ada7296c6fde36a364e5baf206e502bb1"
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def bip85():
|
||||
return BIP85(MASTER_XPRV)
|
||||
|
||||
|
||||
def test_bip85_mnemonic_12(bip85):
|
||||
assert bip85.derive_mnemonic(index=0, words_num=12) == EXPECTED_12
|
||||
|
||||
|
||||
def test_bip85_mnemonic_24(bip85):
|
||||
assert bip85.derive_mnemonic(index=0, words_num=24) == EXPECTED_24
|
||||
|
||||
|
||||
def test_bip85_symmetric_key(bip85):
|
||||
assert bip85.derive_symmetric_key(index=0).hex() == EXPECTED_SYMM_KEY
|
||||
|
||||
|
||||
def test_invalid_params(bip85):
|
||||
with pytest.raises(SystemExit):
|
||||
bip85.derive_mnemonic(index=0, words_num=15)
|
||||
with pytest.raises(SystemExit):
|
||||
bip85.derive_mnemonic(index=-1, words_num=12)
|
Reference in New Issue
Block a user