Merge pull request #168 from PR0M3TH3AN/codex/implement-derivation-functions-for-totp,-ssh,-and-seed-phras

Add BIP85 derivation helpers
This commit is contained in:
thePR0M3TH3AN
2025-07-02 22:37:15 -04:00
committed by GitHub
2 changed files with 36 additions and 0 deletions

View File

@@ -19,6 +19,7 @@ import hashlib
import string import string
import random import random
import traceback import traceback
import base64
from typing import Optional from typing import Optional
from termcolor import colored from termcolor import colored
from pathlib import Path from pathlib import Path
@@ -332,3 +333,19 @@ class PasswordGenerator:
logger.error(f"Error ensuring password complexity: {e}", exc_info=True) logger.error(f"Error ensuring password complexity: {e}", exc_info=True)
print(colored(f"Error: Failed to ensure password complexity: {e}", "red")) print(colored(f"Error: Failed to ensure password complexity: {e}", "red"))
raise raise
def derive_totp_secret(bip85: BIP85, idx: int) -> str:
"""Derive a TOTP secret for the given index using BIP85."""
entropy = bip85.derive_entropy(index=idx, bytes_len=10, app_no=2)
return base64.b32encode(entropy).decode("utf-8")
def derive_ssh_key(bip85: BIP85, idx: int) -> bytes:
"""Derive 32 bytes of entropy suitable for an SSH key."""
return bip85.derive_entropy(index=idx, bytes_len=32, app_no=32)
def derive_seed_phrase(bip85: BIP85, idx: int, words: int = 24) -> str:
"""Derive a new BIP39 seed phrase using BIP85."""
return bip85.derive_mnemonic(index=idx, words_num=words)

View File

@@ -5,6 +5,11 @@ import pytest
sys.path.append(str(Path(__file__).resolve().parents[1])) sys.path.append(str(Path(__file__).resolve().parents[1]))
from local_bip85.bip85 import BIP85, Bip85Error from local_bip85.bip85 import BIP85, Bip85Error
from password_manager.password_generation import (
derive_totp_secret,
derive_ssh_key,
derive_seed_phrase,
)
MASTER_XPRV = "xprv9s21ZrQH143K2LBWUUQRFXhucrQqBpKdRRxNVq2zBqsx8HVqFk2uYo8kmbaLLHRdqtQpUm98uKfu3vca1LqdGhUtyoFnCNkfmXRyPXLjbKb" MASTER_XPRV = "xprv9s21ZrQH143K2LBWUUQRFXhucrQqBpKdRRxNVq2zBqsx8HVqFk2uYo8kmbaLLHRdqtQpUm98uKfu3vca1LqdGhUtyoFnCNkfmXRyPXLjbKb"
@@ -13,6 +18,8 @@ EXPECTED_12 = "girl mad pet galaxy egg matter matrix prison refuse sense ordinar
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_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" EXPECTED_SYMM_KEY = "7040bb53104f27367f317558e78a994ada7296c6fde36a364e5baf206e502bb1"
EXPECTED_TOTP_SECRET = "OBALWUYQJ4TTM7ZR"
EXPECTED_SSH_KEY = "52405cd0dd21c5be78314a7c1a3c65ffd8d896536cc7dee3157db5824f0c92e2"
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
@@ -32,6 +39,18 @@ def test_bip85_symmetric_key(bip85):
assert bip85.derive_symmetric_key(index=0).hex() == EXPECTED_SYMM_KEY assert bip85.derive_symmetric_key(index=0).hex() == EXPECTED_SYMM_KEY
def test_derive_totp_secret(bip85):
assert derive_totp_secret(bip85, 0) == EXPECTED_TOTP_SECRET
def test_derive_ssh_key(bip85):
assert derive_ssh_key(bip85, 0).hex() == EXPECTED_SSH_KEY
def test_derive_seed_phrase(bip85):
assert derive_seed_phrase(bip85, 0) == EXPECTED_24
def test_invalid_params(bip85): def test_invalid_params(bip85):
with pytest.raises(Bip85Error): with pytest.raises(Bip85Error):
bip85.derive_mnemonic(index=0, words_num=15) bip85.derive_mnemonic(index=0, words_num=15)