From 22461f024f9df78cb84bab39cc623e5726ac7f89 Mon Sep 17 00:00:00 2001 From: Ben Westgate Date: Mon, 21 Sep 2026 02:03:33 -0500 Subject: [PATCH] Validate supplied BIP32 master seeds Apply the existing stdlib BIP32 root validity check to caller-supplied seed bytes before constructing a MasterSeed. Fresh generation already retries the same invalid roots. Security: prevents an unusable supplied root from crossing the master-seed generation boundary. Validation: python -m pytest -q; python -O -m pytest -q; Ruff check and format; strict mypy; differential_wallet.py --verify. --- src/codex32/generation.py | 3 +++ tests/test_generation.py | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/src/codex32/generation.py b/src/codex32/generation.py index 551ef61..db1843f 100644 --- a/src/codex32/generation.py +++ b/src/codex32/generation.py @@ -21,6 +21,7 @@ ) from codex32.errors import ( CeremonyStateError, + CodexError, HeaderCollision, InvalidIdentifier, InvalidLength, @@ -155,6 +156,8 @@ def generate_master_seed( """Generate or encode one unshared ``ms`` secret.""" supplied, length = _seed_input(seed_bytes, byte_length) if supplied is not None: + if not _valid_root(supplied): + raise CodexError("master seed does not form a valid BIP32 root") identifier = _random_identifier() if identifier is None else _identifier(identifier) return MasterSeed.from_seed(supplied, identifier=identifier) if identifier is None and fingerprint is None: diff --git a/tests/test_generation.py b/tests/test_generation.py index 44cd688..289662b 100644 --- a/tests/test_generation.py +++ b/tests/test_generation.py @@ -25,6 +25,7 @@ from codex32.bech32 import _u5_to_chars from codex32.errors import ( CeremonyStateError, + CodexError, HeaderCollision, InvalidIdentifier, InvalidLength, @@ -139,6 +140,12 @@ def test_raw_bytes_accept_random_or_explicit_identifiers() -> None: assert secret.header.identifier == "test" +def test_supplied_seed_must_form_a_valid_bip32_root(monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setattr(generation_module, "_valid_root", lambda _seed: False) + with pytest.raises(CodexError, match="master seed does not form a valid BIP32 root"): + generate_master_seed(bytes(16), identifier="test") + + def test_explicit_and_random_output_order_contracts() -> None: source = generate_master_seed(bytes(range(16)), identifier="test") _secret, shares = _complete(